Announcing the Swift Commons Github Org
TLDR; We have a Github Org now. Also, it's possible to use swift on Android with some limitations that can be worked through
After a brief (relatively speaking compared to the last one) hiatus of the previous post's series, we're back.
An interesting development transpired between the last post and today that called for a shift of attention. It was relevant to the goals of this blog and I figured that it would help the structure of the content be more digestible.
In an effort to better document and help out users figure out how to work with swift in a cross platform capacity, I launched a Github org called Swift Commons that will serve as an accompaniment resource to the content posted on here.
The goal for the org being posting plenty of cross platform swift examples.
With that being said, what better way to hilight the usage of swift on other platforms than to talk about a platform that's basically the polar opposite of where swift is designed to run today. In case you haven't guessed by now, today's post will be about the state of Swift on Android
State of Android on Swift
At the time of this post, Swift is losely supported on Android. By that I mean, its technically possible but you probably won't get far with it. According to the official Swift Repo, Android 'works' but you can't really make apps with it.
By that they mean that you can technically compile Swift codes that run on Android, but you're on your own for any UI code. In essence, its the same issue we've talked about before.
And even though you can, the process isn't too streamlined yet. You can download versions of the swift toolchain for Windows, Linux, and even Web Assembly, that already come pre built and let you export swift programs for those platforms with ease.
There's no quick link to find one of these for Android, so you're basically on your own with that and have to build it yourself.
There are some nuggets of information on this regard however. It turns out other members of the Swift community had attempted to work with swift on Android before, like Readdle
Unfortunately it seems that their latest examples are a few Swift versions behind and require you to make your builds on macOS. I had a bit of trouble setting this up and I wasn't liking the way the dev flow was looking like.
Things weren't looking good for Android right now. I definitely wasn't won over but I wasn't about to quit just yet.
Making Swift Usable For Android
In the present day I'm pretty satisfied with my Swift workflow for integrating it with Android. Future articles will go more into depth with my process but I figured for this post I'll give a glimpse of the issues at hand that had to be solved.
Usable Toolchain
First and foremost, we need a usable swift toolchain for Android. As mentioned earlier, there's swift toolchains readily available for Windows + Linux, including some easy to find ones for Web Assembly. Mac's obviously have the easiest time because you get one just by having XCode installed.
Android on the other hand doesn't have one.
I found some bread crumbs showing previous work on github from Readdle devs that show them using their own toolchain built on macOS, but the steps to replicate it were tricky for me. Ontop of that, one advantage I like about Swift is that you can build pretty comfortably on each target platform.
I already have most of my swift code building on a linux box, and test my mac / iOS swift code on my mac, and windows code on windows. It's pretty nice being able to work on swift code that runs on browsers on linux too, so I figured I'd see how hard it would be to set this up on linux.
With a bit of patience it turns out it isn't too hard. Thankfully I found a more up to date project that was making more current swift android toolchains here.
By following their github action CI workflow file, I was able to make a toolchain in my own Dockerfile setup. This turned out great because I could then take this portable swift android toolchain builder across any platform that supports docker.
Now I can compile swift code that runs on android pretty easily.
Functioning Android App
Next up is actually integrating this code you can compile using the above toolchain, into an Android app. This part proved to be one of the trickiest because of how Android is set up to handle foreign 'native' code. Or in other words, code that isn't java / kotlin.
Android allows you to use c/c++ (and other similar code) with their apps using their 'NDK' interface. This basically creates a little c++ app inside your android code base that you can pass data to and from to the rest of your android app.
The caveat for Swift being that you need export your code as shared dynamic libraries instead of regular executables. That way you can access this swift code from this android c++ layer.
Once you do that, depending on how complex your swift code is, the only other thing you need to do to use this swift code is include the libraries that swift needs to function. You can find this on your toolchain and I'll make another post later on that goes into more detail.
In your main android activity (the starting point of your app) you also have to be sure to load your dynamic swift dependent libraries using a standard system call.
companion object {
init {
System.loadLibrary("MySwiftCodeLibraryName")
// ndk main c++ code file name
System.loadLibrary("main")
}
}
Otherwise your app will just crash at launch because it can't find the dependent framework code it needs.
Last but not least, you need to make sure your Swift code is visible from the java / kotlin (JVM) side. In order to do this, you need to adopt a standard that the NDK uses to expose native code to java. This part is pretty rough and manual and depends on naming your code relative to the android package and folder structure.
I'll go into further detail in a future article but for this we can thankfully leverage a package someone already created that imports these JVM symbols and function standards using a C library wrapped in swift located here.
Without going in too deep, the gist is having to make some Swift Functions that your Android app can locate and call on. You do this by creating a Swift function in your code and adding an attribute to it like in the following example:
@_silgen_name("Java_myandroid_projectfolder_underscorename_MainActivity_functionNameHowItAppearsOnAndroid")
func myAndroidFriendlySwiftFunction(env: UnsafeMutablePointer<JNIEnv>, jobj: jobject) {}
Don't say I didn't warn you. It doesn't look pretty but it works. In fact, if you've previously worked with the NDK and c++ code in your Android projects before, this looks pretty familiar
Useful Android App
If you somehow made it this far. you've only just scratched the surface of this endeavor. The last but most important part is making sure that your Android app is actually useful.
Remember, you don't have any GUI that can render on your Android app with swift alone, so your only real option is to use Swift to pass data to your UI that you code the old fashion android way.
This could still be very useful but one must wonder if it's worth the effort to get to this point, if all you can do is share business logic. Don't get me wrong, I think this is still useful. So does JetBrains who makes their own cross platform framework called KMM
After all, if you write Swift UI code on apple's platform, you can more or less reuse most of it to make UI that works on iOS / iPad / tvOS / apple watch, especially if you know what you're doing.
Perhaps you can abstract away from UI and write useful swift code that runs on Android that influences the UI. What ever your thoughts, you should be aware that this is precisely why apple mentioned in their Swift doc above that you can't really use Swift to make full blown Android apps.
I ended up taking the road less traveled and implemented a UI framework in swift that runs on Android that I plan to release once I clean it up, and will talk about that in a future article. But for now, this is where we're at as a whole.
Closing Thoughts
All things considered, making Swift code that can run on Android is technically possible. And after using it to the extend that I have, I can say it works pretty well. But getting to the point where it works, definitely took some growing pains and patience.
The triumph of knowing I'm one step closer to being able to use Swift as my daily driver for the myriad of platforms I touch (much like a c++ replacement) was reward enough though.
I'll be sure to cover a Swift Android app in further detail in a future article, but for now I wanted to hi-light the current state of things and hopefully help change that by integrating the knowledge I've learned on my journey into the Swift Commons org I mentioned at the start.
If I'm doing this right, that org will serve as a useful resource for those wanting to embark on this journey of using Swift everywhere.
Till next time!