I have ported an iOS app to Android using Vlad Gorlov's Swift Everywhere Toolchain.
The non-UI code is shared with the iOS version (about 20k lines of Swift code). The UI is written in Kotlin with a JNI bridge using a combination of my own Swift wrapper and swift-jni.
Aside from the initial grief of fixing all of the NS*
bridging issues to get the code to build, the code mostly worked.
One fix I had to make was the main bundle was pointing to a system directory (/system/bin/app_process32), so the localized strings were all missing. I created a pull request for this fix.
The next issue was my API requests weren't working. I setup cacert.pem as described in another post and turned on CURL debugging to see the request and response, but the completion handler never got called.
After a bit of debugging, I realized this was due to the lack of a run loop for Android. For Apple apps, UIApplicationMain
sets this all up. But Android is a little different, since the Java/Kotlin code also runs on the main thread.
My current initialization in Kotlin ended up like this...
- Configure the environmental variables
CFFIXED_USER_HOME
andURLSessionCertificateAuthorityInfoFile
. - Copy some files from Android's asset manager to the files directory, such as Info.plist and the localized strings.
- Load my application's library (the Swift code).
- Call my application's initialization function.
- Start a run loop dispatcher.
This is what I came up with for the dispatcher. Here is the Kotlin code...
private fun startDispatch() {
val handler = Handler(Looper.getMainLooper())
handler.post(object: Runnable {
override fun run() {
MyAppRun()
handler.postDelayed(this, 100)
}
})
}
And this Swift code...
@_cdecl("Java_com_myapp_MyApp_MyAppRun")
public func MyAppRun(env: UnsafeMutablePointer<JNIEnv>, this: JavaObject) {
let _ = RunLoop.main.run(mode: .default, before: Date().addingTimeInterval(0.01))
}
I tried just using Date()
, but then run
would lock up even though the documentation claims it should return immediately if nothing is ready to run.
I'm curious if anyone else has had some experience with this and has come up with a different solution.