Swift for Android: Call for the Community

Swift on Android works great right now for us. Happy to discuss more, and if you are new to Swift on Android, start with VGorloff's stuff- it lets you use Swift Package Manager to target Android from the Mac command line, and everything works great for us. And he is responsive to bugs. Kudos !

3 Likes

I think Swift should add support for FlatBuffers or protobufs to the language natively, and use them as its standard wire protocols, and be first class codable via those mechanisms. For android, you would just grab the Protobuf or FlatBuffers code for Java and everything would "just work". You would call a piece of code that sends the Protobuf version of your object to java code, and it would get turned into a Protobuf version of your instance, and send it to java via JNI, and java would receive it and efficiently unpack it and work with that. Async RPC is then built on top of that. It's what we do. But if swift supported them, Swift would gain a much more efficient mechanism to push objects than JSON, and a simple way to communicate with lots of things. Even a remote lambda ;-).

2 Likes

I had a try to pass Protobuf objects via JNI. Can't say that it was a good experience. Another attempt would be FFI. This will require export C-symbols from Swift and then reassemble them back to objects on Java side. I was using similar approach while using Swift in Ruby. It was not bad.

FFI still require bridge generation "Swift - C - Ruby/Java". Hope it can be automated via sourcekit or maybe via information from swift module.

Ideally would be if Apple open-sourced Combine and SwiftUI.

1 Like

Sorry for the delayed response. We were able to get a working version of our project using both @v.gorlov's and @Finagolfin's methods. We needed to use patchelf to remove the soname versions on Buttaface's generated files as AndroidStudio won't accept them but it did work. Both of the setups had good points to them and we wanted to compare so we did both. Just wanted to get that out there. Thanks, guys!

1 Like

Yeah agree- we dont want to do codegen for protobufs- and we want a cross platform wire protocol thats slightly more performant and standardized than JSON via codable. My point is if you had a standard object request broker thing you could just register on both ends of JNI and just use a JNI to open the swift side and connect the pipes. Then you would just pump stuff to the bus and it would get delivered. Thats what we do, minus the protobuf. (we use JSON codables for now.).-- the idea is if protobuf was in the swift lang tooling you could decorate a class or struct with a protocol like codable and generate the protobuf compatible binary data files automatically. You don't need codegen to do that. You hide it in the tooling.

Nice work stoker.

I've been using Kotlin Multiplatform for some toy projects, it just works. I would say Swift is very behind of it, even if Swift gets support for JVM. The tooling, the documentation and the fact that you could develop in Kotlin in almost all existing platforms (Linux, Windows, Mac). Don't get me wrong I would love to use Swift to develop Android libraries and is very interesting to see android toolchains for Swift and how others are using Swift on Android but not sure how much effort would need to get to the point that Kotlin Multiplatform has right now, starting with the interop for JVM languages, binary size, Good editors experience (cross platforms not just Xcode), etc. (Sorry if this isn't full related with the thread but just wanted to express my opinion about it since we are discussing more than just to have a Swift Toolchain for android)

2 Likes

I think we want to think of 2 separate use cases here-

  1. portable code you want to run everywhere, natively, without a VM, and have it run well, and predictably, and have good connections to big C/C++ libraries. Swift excels at this, even though it has performance issues around ARC, just as Obj-C did. We worked around those, but they are a serious issue.

  2. code you want to build around a small portable mobile app, but have a very high productivity experience. It looks like Flutter wins here, with some companion code written in whatever. If Kotlin provides a good rest of the code story fo Flutter, great.

  • I don't see people using Kotlin to do native UI on iOS though, and with Apple's monopoly position in mobile, making highest quality iOS apps is pretty important. You need great tools to do pretty apps. For the big apps (like Office, etc.) none of this really matters- except languages that can interop well with C/C++ on all platforms. That looks like Swift & Rust right now.
1 Like

on Android 11 Simulator. The application works as on, say, Android 8 Simulator. No differences.

Thanks for testing it, maybe the problem is with Swift executables then or just in the Termux app.

@dstoker-cricut, good to know you figured out the fix.

As for compiling Swift to JVM bytecode, that's only needed in tight loops that pass data between Java and Swift methods, but I doubt anybody is doing that yet, with early Swift apps on Android likely keeping the two fairly separate. I think JNI or Protobufs are good enough for such early use, and someone should only invest in such a SwiftJVM project once you have an app that truly needs it.

Right now, Swift bindings to JNI or Protobufs are good enough, certainly for me and likely for most using Swift on Android.

1 Like

tldr; Swift runtime on Android is big, can it be smaller?

First, many thanks to @Finagolfin and @v.gorlov as well as comments from others on this thread! Without it, we would not be able to do cross-platform work in Swift (currently deploying to macOS, Windows, Linux, iOS, and Android). I work with @dstoker-cricut who asked questions above to get that done.

We have a cross-platform geometry library written in Swift that uses only Foundation (lightly), SwiftNumerics, and ArgumentParser or SwiftProtobuf for interfacing.

Has there been any work on slimming down the requirements for a deployed Android GUI application using a Swift library? Our Android clients are experiencing a doubling of the size of their app when using the library.

Currently on Android, we access the library via a pair of Swift functions declared with @_cdecl taking either a command string or Protobuf command payload. To do that, we need to carry 19 shared object files around, totaling 78MB for the x86_64 arch alone. When stripped of debug symbols (using strip -g), they still weigh in at 58MB. The files are:

Library Size (bytes)
libicudataswift.so 30204656
libFoundation.so 7814560
libswiftCore.so 5710176
libicui18nswift.so 4115896
libcrypto.so 2936856
libicuucswift.so 2525416
libFoundationNetworking.so 1171920
libxml2.so 1098464
libc++_shared.so 989800
libswiftRemoteMirror.so 817512
libssl.so 601968
libdispatch.so 364848
libcurl.so 350528
libFoundationXML.so 329504
libicutuswift.so 322568
libswiftSwiftOnoneSupport.so 282344
libswiftDispatch.so 218376
libswiftGlibc.so 74616
libBlocksRuntime.so 10424

Around 56% of the size comes from the 4 International Components for Unicode (ICU) libs, 27% from other direct Swift runtime libs (including libxml2), and the remaining 17% from other dependencies.

We've tried all the tricks Google recommends on 앱 크기 줄이기  |  Android 개발자  |  Android Developers like arm-eabi-strip, leaving native libs compressed with extractNativeLibs in the manifest, and applying resource size tricks with shrink-resources. These help further, but the size of the Swift libraries just makes this hard.

What can be done to slim them down further? Using the Android-embedded ICU somehow instead of these libs is the clear #1 option, but a bit out of my league. Any help there would be greatly appreciated! Are others seeing these files at the same sizes? Any other thoughts to reduce their size?

5 Likes

What can be done to slim them down further?

I don't think all of those will be used by your geometry library, for example, I'm not sure where those libcrypto.so and libssl.so dependencies are coming from. I believe libswiftSwiftOnoneSupport.so is only needed when compiling with -Onone and libswiftRemoteMirror.so for runtime reflection or something. I doubt your geometry library is calling FoundationNetworking and probably not FoundationXML either, which means you can remove those and libcurl.so/libxml2.so also.

Libicudata is the biggest hog, there was talk a couple years ago of slimming it down. You may want to explore that.

Using the Android-embedded ICU somehow instead of these libs is the clear #1 option, but a bit out of my league.

There is only a Java interface for the Android system ICU, not a native one. Google says not to link against these system libraries as they will vary by system. Since there is no support in the NDK for linking against the system libicu, they say you should supply your own and that you should not link native code against system libraries (couldn't find the link where they said this).

Any other thoughts to reduce their size?

You could try static linking with -static-stdlib and then try running some tool to strip out unused code. If you want to stick with shared libraries, there may be some tool that audits the function dependency graph and will help you similarly strip out unused functions (I don't know of any such tool, never looked for it).

1 Like

Btw, a head's up to every one that 32-bit armv7 support is not in good shape in trunk and the upcoming 5.4 release. This SwiftOnARM thread lists the issues that have come up. I have spent several hours just to get it working for my upcoming Swift 5.4 Termux package, but it is very slow with optimizations turned off. It's gone on my back burner now, as I don't care that much about the dying armv7 support.

If anyone here needs armv7, you may want to spend some time on getting it working better.

We have a server communication piece to obtain geometry, so all the FoundationXML, libxml, libcrypto, etc are coming from that. We could remove it, and save a few megs that way, ya. ~5% is small, but something, certainly.

Too bad. I may have found the link(s) you were looking for (libicudata.so is not in the provided list of allowed libraries): Android Developers Blog: Improving Stability with Private C/C++ Symbol Restrictions in Android N and 기본 API  |  Android NDK  |  Android Developers So the /system/lib/libicudata.so that is available on some systems is not recommended for use.

I had assumed that since on Mac static linking the Swift runtime was now disallowed that the same was true of Android. If dynamic linking is not somehow enforced by the runtime, static linking would allow traversal of the call graph to remove unused symbols and code. Great idea!

Static Linking of the Swift Runtime on Mac is caused by two things: Swift is a system library on macOS (I hate that stupid lowercase "m" so much), and macOS doesn't support static-linking to system libraries. As neither of these things are true on Android, there shouldn't be a problem with static linking to the Swift runtime.

Slightly off topic. I'm interested in your geometry library, if it's open source can you share a link? I'm pretty excited about Swift's potential for computer graphics research.

Unfortunately no, it's not. I do have a similar open-source library cooking that is written in C, but has Swift bindings: HollowCore · GitHub specifically: GitHub - HollowCore/HollowSwift: Swift compatible bindings for HollowCore and friends. It provides a full object system with containers, the beginnings of a 2D drawing system using Bezier curves, and even a simple 3D ray tracer. It doesn't require any dependencies beyond a standard C library (stdio.h, math.h, etc).

1 Like

Would be interesting to see it static link - if you go that way, let us know ? Did anyone build the static swift libs?
You should be able to dead strip it well once you’ve linked your app.

Looks interesting. Thank you for sharing.

I may have found the link(s) you were looking for

Yes, that's it.

static linking would allow traversal of the call graph to remove unused symbols and code

Yes, as @Nobody1707 says, I know of no reason not to statically link against the Swift stdlib and Foundation on Android.

Did anyone build the static swift libs?

@John_Burkey, I always build them as part of the Termux packages and cross-compilation SDKs that I put out for Android. I test building small executables with them and report any issues that come up (that problem is with 5.4), but I haven't tried them with SPM much.

I updated the Termux packages to Swift 5.4 last month and just put up a Swift 5.4 cross-compilation SDK for Android AArch64. I will put one together for x86_64 next, then use it to set up an Android cross-compilation CI with the github Actions Android emulator for x86_64 and several Swift packages. An armv7 SDK will take some looking into, given the problems I listed last month.

@mstokercricut, I got rid of all the shared library versioning this time, let me know if that works better for you.

I tried to cross-compile some small files using -static-stdlib and hit errors with the configuration because a -resource-dir path is passed in, @drexin, looks like some work needs to be done on that. It works fine when natively compiling in the Termux app for Android though.

1 Like