With the official announcement of the Swift SDK for Android, the Android workgroup has fielded some questions about the minimum supported Android version for Swift. Currently we are supporting Android API 28 (a.k.a. Android 9, version code P, codename "Pie"). Released in 2018, it is supported by 91.7% of active Android devices according to the estimates at apilevels.com.
The choice of minimum supported API has been largely pragmatic. Older versions lack some APIs that are needed by various components of the core frameworks. In particular, posix_spawn was added in API 28, and as discussed at https://github.com/swiftlang/github-workflows/issues/83, we settled on that minimum level for expedience.
That being said, there are paths to supporting even older versions of Android, especially now that we will be able to conditionally gate APIs using #available clauses for Android levels (thanks to @madsodgaard for https://github.com/swiftlang/swift/pull/84574!)
We have heard of custom Swift SDKs builds being created for older API levels, and we would like to survey the community about whether they have the need to support older Android versions, along with any details they can share about their target environment. This might be because they would like to use Swift on bespoke Android hardware that is stuck on older API levels, or simply because they want/need to be able to target an even wider swath of Android consumers than the ~92% coverage provided by API 28. Android powers not just phones and tablets, but a huge range of consumer and industrial devices, and we do not presume to know all the scenarios where developers might benefit from being able to develop in Swift for Android.
In any case, we want to hear from you! Feel free to chime in on this thread about your ideal Android minimums, or reach out directly to the team by messaging @android-workgroup on the Swift Forums.
I personally find a worldwide 8.3% to be still pretty relevant, given that this could be much higher in Africa or countries like India, to name one. I noticed this change in the recent releases of the SDK with Swift 6.2, but sticking to 6.1 has allowed me to keep support down to API 24, for others reading.
At the end of the day, it really depends on the nature of the applications, and while I find the % significant for my case, I don’t think most developers will notice.
Still curious to hear other perspectives on the matter.
I do understand the rationale behind API 28 minimum. But, we also need to support API 23+ for our use case. We unfortunately do not have the luxury of upgrading Android versions, as we run it on "custom" hardware, i.e. non-phones, as well as Android phones.
Our current plan is to maintain a fork of finagolf's repo, modified to support API 23+, unless the working group decides to distribute the official SDK with lower versions available. API 23 is old, and there are a lot of APIs that were first added in 24 even, so maybe this is too much to ask... However, maybe the above fork can be an inspiration for what is "missing", and at least just a "fallback" for people requiring legacy support.
However, I do still think it's relevant to talk about API 24 minimum.
You mention that the main issue is probably posix_spawn. However, from what I could find the spawn APIs are only used in the new Subprocess API from Foundation and the old Process. I might be wrong on this one, so please correct me if it is also used in other parts of Swift. I would argue that this API is probably not the most common use case for the Android SDK, and it can now even be safely guarded using the Android availability checking added to Swift.
IMHO this approach is important for Swift evolution in general. Aside from the inherent issue of Android API support, if we're giving up API 24 for something that only a small % of developers use in Swift (first time I hear of the Subprocess API), then I 100% lean towards conservative availability checks. If supporting API 24 increases the complexity of the build system, that's a different story, but I don't know the internal extent of the change to draw any conclusions.
Yes, it is not used much in a Swift SDK, just there and in Testing for exit tests. As you and @etcwilde noted, it wouldn't take much more to integrate Bionic back to API 23 or 24 also, as I'm well aware since my Android SDK bundle has long supported API 24.
The problem is that before you brought up this API 23 support issue elsewhere for the first time a month ago, nobody in the workgroup seemed to really want to go that far back and do the work to fix the remaining holes. Then, there is of course the possible futex issue a workgroup member mentioned, where they saw crashes before API 29 because Swift 6 started using that system call in the stdlib and he said those older linux kernels didn't have it.
Another consideration I just remembered is that Android didn't add native Thread-Local Storage (TLS) till API 29, so we are actually compiling the one TLS variable in the Swift stdlib with emulated TLS, to support that older API.
However, as I pointed out to you then, even those Process-related APIs in Foundation don't really need posix_spawn_* in Bionic, as there are polyfills in Foundation for most of them, for all but these more newly added ones.
If you and whoever you're working with want to push the official SDK back to API 23, by doing that work to fix or avoid the few remaining issues, we can certainly consider it.
I'll point out we don't support exit tests on Android, at least not yet. (But I'm happy to work with you to get them up and running—it shouldn't be hard to support them.)
Thanks to everyone that has chimed in so far. This is very valuable feedback for us, so keep it coming!
As an experiment, I did a quick patching-out of the offending posix_spawn_* calls (in Android API 24 by marcprux · Pull Request #16 · swift-android-sdk/swift-docker · GitHub), and the SDK does indeed build for API 24 and pass some testing. So we might want to consider moving the official supported level down to 24. I'll add it to the agenda of the next workgroup meeting and we can discuss further.
P.S. for completeness, I did also try API 23, but hit some more significant build failures. If this is a compelling enough target for anyone, they might help seek workarounds for the missing symbols like getgrnam_r and getgrgid_r, as well as any subsequent shortcomings this target might encounter.
I'm definitely interested in hearing the results of testing against actual devices with API 23 or 24. I haven't even tested against an emulator at those levels, and don't have any device that goes back before API 28.
Nice! For API 23, you would probably also want to add shims for ifaddrs and change the FILE pointer for it to be actually useable. You check how I do it in repo linked further up. I’d be happy to help out, if the workgroup decides that this is something we officially want to support:)
I've also faced the need for an even lower NDK* version (21). My initial approach was to back-port the missing functions, but if I remember correctly it only worked for ndk 23 and I ran into another problem. I realized that I need a workaround similar to what you made in your fork. I'm also working with a non-phone integrated device that has very limited hardware (it even misses a screen).
Returning to the main topic, for me, the lower the supported version, the better. If an @available check is coming soon, why not simply mark all the relevant APIs - for example, Process from Foundation and Subprocess ?
I'm assuming you mean API level 21, not NDK version.
Yes, that feature will enable us to do that (should we decide to target the lower API levels). But we'd need to build using a higher NDK version (28+, I think).
I'll see what happens when I try building for API 21. Anyone have an even lower Android API they need to target?
I agree that for the vast majority, API 23 is more than sufficient. I actually meant version 23 when I wrote " the lower, the better" - sorry for not specifying. I only mentioned version 21 remembering personal experience.