Unless I'm missing something obvious, it still boils down to design choices, i.e., it depends on how much you want to be able to express in Swift. If we take Android, you may get around that by implementing the data layer in Kotlin and performing CRUD from Swift through JNI. Is it a PITA? Sure, it is, but if it's the tradeoff for a smaller binary, some may accept it. At least, I believe that developers should be given the option. Most won't need the full Foundation framework out of the box.
I think only someone desperate to use Swift would put up with that! Otherwise, going the other direction and using Kotlin Multiplatform starts to look much more sane.
I assume that the vast majority of those exploring Swift on Android on this forum come from a consolidated Swift codebase or knowledge. If starting from scratch, instead, there are more convenient alternatives (today) than both Swift and Kotlin, let's be honest.
I think there's something to be said for "we're already going to be using Swift for this and Kotlin for that, let's not add a third system to the mix"! I'm all for "use the right tool for the job" and people not being obstinate about learning new tools, but in theory two languages (and library ecosystems, and build systems) is going to be simpler than three even if the other two-way pairings would have otherwise been simpler. (Ex: maybe you say Rust&Swift and Rust&Kotlin are each individually easier than Swift&Kotlin, but is "Rust&Swift + Rust&Kotlin" easier than "Swift + Swift&Kotlin" or "Kotlin + Swift&Kotlin"?)
For the record, and also to restore a bit of optimism in the discussion. I've been redesigning my Swift/SwiftUI app to build its entire core business everywhere else as a shared library (Linux, Android, and Windows). The library is a quite complex and low-level Swift/C codebase with several non-Swift 3rd parties.
These are the footprints I attained on Android (even less on other platforms):
My Swift/C + JNI library, including the static Swift runtime and FoundationEssentials = ~18MB
OpenSSL plus other static 3rd parties = ~10MB
The release .apk, minified and shrinked with R8 = 31MB (12MB compressed)
IIRC, the original iOS app (compressed .ipa) is 20MB+. Just saying.
These are IMHO amazing numbers for a non-native Android app, given that cross-platform Swift allowed me to reuse a well-established production codebase. Also, performance is pretty impressive, and my app needed that too. Not sure if other languages would perform as well (maybe not even Rust, let alone Go or Kotlin).
I played with this a lot, but most apps should get similar outputs with FoundationEssentials already. As I mentioned before, static linking plus stripping is the game changer.
C++ is unsafe and very clumsy compared to modern languages. I say this as a guy who’s spent most of the last 30 years writing C++. I’m currently trying to rewrite a legacy C++ codebase in Swift.
I personally dislike Rust and Zig, for reasons that are even more off topic.
On a more objective level, choosing one of those means you have to write two glue layers to connect to Apple & Android UIs instead of just one.
To be fair, once we get out of the Swift / Apple centric mindset and/or the indie developer one, if a new dev wants to go work for a company doing mobile development today they are very very likely to see React Native (thus TypeScript) or increasingly Compose Multiplatform as they figured out not only the business logic but the UI portability layer (going across Android, iOS, and web too) and KMP + CMP are backed up directly by JetBrains and Google’s Android teams too. Modern devices and connections, apps in the 50-100 MB range are increasingly common as well (both ecosystem have some sort of delta updates providing smaller than full binary updates too anyways).
I do have hopes for what the community is trying to reach, but it is an uphill battle unless someone with the resources of Apple steps into the ring. Open source SwiftUI and invest money in its portability (likewise async/await, structured concurrency, actors, etc… available on Android too). In a way I understand how it might not seem beneficial to Apple’s own platforms, but the downside is that most businesses are moving away from Swift and SwiftUI (at least in the code their devs write day to day).
Multiplatform tools can be a big benefit but depending on your needs and scale it is best to have direct control and less layers between you and the platform’s SDK as their issues (of the big phat multiplatform engine) might not be your fault but they are your problem (TM - ATP).
In my case UIKit on iOS and AppKit on macOS wasn't enough to add up to 1 person's full time job, while ~8 people worked on the business logic. I suspect similar is true for most apps that mainly build on stock UI elements with only a few custom components for flavor or when the stock one is bad.
If "company doing mobile development" is one where people log a full day of work for updating a single text resource (=most companies based on the job ads I saw over the past several years, although I'm only looking at the ones using Swift) then I don't think it's worth comparing.
I would be remiss to not point out that Skip.dev exists to fill exactly this role, enabling the creation of complete iOS/Android apps with Xcode in pure Swift. If the Swift SDK for Android is analogous to KMP, then Skip would be the CMP equivalent (except that unlike CMP, Skip has the advantage of a native UI on both platforms and no garbage collection imposed on the iOS side; see also: Swift on Android talk at iOSoho Meetup (w/ Compose Multiplatform)).
Every app is different. Indie developers and small teams will benefit greatly from building their apps with just a single language, IDE, and technology stack. Sprawling and complex apps with large teams will often be developed with modules in multiple languages and technologies. This split is most often seen between the UI/Logic layers, where the UI is created with SwiftUI/UIKit on iOS and Jetpack Compose/Views on Android and the logic is shared in a single implementation layer (Swift, Java, Kotlin, Rust, C++, whatever), and the two layers are glued together somehow, with a fluency that often benefits one platform over another (e.g., C++ bridging is smooth for Swift but painful for Kotlin; Java bridging is smooth for Kotlin but painful for Swift).
To circle back to the main focus of this thread, some people assert that a Logic layer shouldn't need internationalization, and thus FoundationEssentials is sufficient and the size inflation from FoundationInternationalization is a non-issue. In my experience, unless we are talking about an extremely limited "Logic" layer (e.g., pure algorithms or scientific applications), any logic layer without i18n support for the handling of numbers, dates, calendars, currency, pluralization, and collation would be so trivial as to undercut the benefit of partitioning the app into separate languages in the first place.
I think we can all agree that a smaller app size is better, regardless of how it be architected. Reducing the i18n overhead for non-Darwin Swift apps is a particularly thorny technical issue, but I think it is eminently solvable and a worthwhile endeavor.
If anyone is interested in a setup for a slimmer Swift/Android lib (static + FoundationEssentials), you can copy my CMake toolchain file from Partout (attribution required).
Then, in Passepartout, you can look into the CMakeLists.txt of the native build inside app-android (open it with Android Studio). The Swift lib generation (libpassepartout.so + dependencies) is integrated into the gradle plugin, so any update to the Swift code triggers a lib update before the Android app starts. I work with Android Studio on macOS, but I tested the CMake build on Linux, too.
The layout of Passepartout is a bit too convoluted for a tutorial, but I'll be sharing a sample app based on this approach on my blog in the upcoming weeks.
Yup. For example, SQL supports all of those things, and the database layer of an app is often called upon to run queries that use them. Even something as humble as ORDER BY name is often required to do not only Unicode collation but localized collation in some particular language.
(And no, you can’t move the collation up to the higher layers. If you have a million rows and query for the first ten ordered by name, the database would have to return all million rows for the app to sort.)
This is a partial view of the problem to me. Not all apps are database CRUD browsers. Probably the premise of this research should clarify if "Swift Android SDK" is meant to embrace the full stack (like Skip does), in which case I would agree with you anytime.
However, if the scope is the Swift runtime, any Foundation overreach should be opt-in IMHO. There are endless cases where internationalization is a redundant dependency for a library.
Yes, the only debate here about this matter is what to make the default in Skip or some hypothetical build config in the future, for which we've been discussing if localized Swift libraries are more common.
Honestly, it should not much matter which is the default, as devs will always be able to choose whichever they need. Right now, it is all up to you to put everything together, as I'm sure you've found.