Swift for Android Won't Amount to Anything
The Swift team just announced nightly builds of the Swift SDK for Android, complete with Java interoperability and a getting started guide. They’re pitching it as “opening new avenues for cross-platform development.” It won’t work. Not because the technology is bad, but because cross-platform solutions always follow the same script: initial promise, growing adoption, then a slow descent into maintenance hell as the inevitable roadblocks appear.
I’ve watched this pattern repeat for two decades. The pitch is always seductive. Write your code once, deploy everywhere. Share your business logic across platforms. Use the language and tools you already know. It sounds efficient, and in the first few months of a project, it genuinely feels that way.
Then you hit the interop layer bugs. With Swift on Android, you’re going to spend hours debugging crashes that occur at the boundary between Swift and Java code. The stack traces will be unhelpful because they span two different runtime environments. The issue will turn out to be something subtle about how Swift’s memory management interacts with the JVM’s garbage collector, or how optionals translate to nullable types, or how Swift’s value types behave when crossing the FFI boundary.
Don’t take my word for it. Joe Masilotti, who maintains Hotwire Native and would genuinely benefit from consolidating Swift and Kotlin, tried the Swift for Android preview. Here’s what calling Swift from Android actually looks like:
@_cdecl("Java_org_example_helloswift_MainActivity_stringFromSwift")
public func MainActivity_stringFromSwift(env: UnsafeMutablePointer<JNIEnv?>, clazz: jclass) -> jstring {
let hello = ["Hello", "from", "Swift", "❤️"].joined(separator: " ")
return hello.withCString { ptr in
env.pointee!.pointee.NewStringUTF(env, ptr)!
}
}
That’s the code required to return a string from Swift to Android. UnsafeMutablePointer<JNIEnv?>, manual pointer manipulation, and cryptic @_cdecl decorators with package names embedded in the function signature. And this is the “Hello World” example—the simplest possible case. Masilotti’s verdict: “I’d honestly rather write Kotlin than that mess of Swift code.”
This is someone who would love for Swift on Android to work, who maintains a cross-platform framework, who already juggles Ruby, Swift, and Kotlin. Even he took one look and said no thanks.
I’ve been here before with React Native’s bridge bugs, with Xamarin’s marshalling issues, with Cordova’s plugin problems. The interop layer is where these solutions die. Not immediately, but slowly, as your team burns hours troubleshooting issues that wouldn’t exist if you’d just written native code.
The announcement touts that “over 25% of packages in the Swift Package Index already build for Android.” Building is the easy part. The hard part is the critical libraries you need that don’t work. Need to integrate with Google Pay? That library was written for Kotlin. Want to use Android’s CameraX API effectively? The nice Swift wrapper doesn’t exist yet, and the auto-generated Java bindings are awkward to use. Build your persistence layer with Core Data? It doesn’t exist on Android. Every Android-specific feature becomes a negotiation with a foreign type system, and every iOS framework you rely on simply isn’t there.
This is where the ecosystem problem becomes real. All those Swift packages that make iOS development productive are useless for building Android apps. You end up writing your business logic in Swift while your persistence and UI layers are still mostly Java or Kotlin anyway, which defeats the entire purpose of using Swift in the first place.
The debugging experience will be miserable. When something goes wrong, you’re simultaneously debugging Swift code, Java interop, and Android platform behavior. Your IDE’s debugger works great for Java, decent for Kotlin, and questionably for Swift-on-Android. Setting breakpoints across the interop boundary becomes an adventure. Understanding what’s actually happening in production crashes requires expertise in multiple ecosystems.
Then there’s the support problem. Apple has no incentive to maintain Swift on Android beyond vague open source goodwill. They make money selling iPhones. When Swift’s ABI changes or new language features get added, Android support will lag. When Android releases a new API surface, Swift bindings will take months to appear. You’ll be perpetually one version behind on both platforms, unable to use the latest features on either.
Google certainly won’t help. They’ve invested heavily in Kotlin and have explicitly positioned it as the future of Android development. Why would they ensure their tooling works great with Swift? When Android Studio gets new features, they’ll work seamlessly with Kotlin and tolerably with Java. Swift support will be an afterthought, if it works at all.
I watched this exact dynamic play out with Windows Phone and C#. Microsoft had every incentive to make cross-platform C# development work, and they still couldn’t make Xamarin feel native on iOS. Now imagine that same problem, except the platform vendor actively doesn’t want you to succeed.
Cross-platform solutions eventually become maintenance liabilities. You’re stuck maintaining knowledge of multiple platforms anyway, plus the interop layer, plus whatever abstraction framework you’ve built on top. You haven’t reduced complexity—you’ve added a layer of complexity that makes everything harder to debug and slower to change.
The people who will try Swift on Android are the ones who think “write once, run anywhere” means something different this time. It doesn’t. It means the same thing it always means: write once, debug everywhere, slowly realize you should have written native code, and eventually either rewrite or live with a codebase that nobody wants to touch.