Kotlin Native 0.4 is out since the beginning of November and, with it, some nice sample iOS demo apps to be run into your favourite iOS device. That sounds amazing, even for some hardcore Swift fans like me. Yet, there’s a catch: the sample apps can only run on a real iOS device but not in the Simulator. Sure, that’s just an unsignificant drawback compared to what the Kotlin Native team achieved so far. Still, I’m a huge fan of the iOS Simulator, as it’s by far the fastest way to do some quick testing of an app while doing a pretty good job in mimicking the behaviour of an app on a real device.
Update: since Kotlin/Native 0.5, the following article is now outdated. I’ll be keeping its content for reference.
So, as a way to understand the Kotlin Native project, I decided to try supporting the Simulator on a Kotln Native iOS app.
In order to do this, I started from the UIKit project sample found in the samples dir of the main repo and tried to make it run in the sim.
The first element to be considered when building for the simulator is obviously that this platform runs on the x86_64 architecture, compared to the armv7s of a real device.
To follow this tutorial, please clone the Kotlin/Native repo from https://github.com/JetBrains/kotlin-native. This repository contains all the sources to build konan, the kotlin compiler for native targets.
First thing I discovered when reading some of the KN source code, was that the iPhone Simulator support has been considered and partially implemented. Just, it was not active.
In the KonanTarget.kt file, which manages which targets are actually available in the platforms, the line the
KonanTarget.IPHONE_SIM.enabled = true was simply commented out. So, let’s uncomment it in order for Kotlin to start acknowledging the presence of an iOS Simulator target.
A minor configuration issue had to be repaired as well. In particular, in the konan.proprerties file, the dependencies.osx-ios_sim at line 75 needs to be changed to:
dependencies.osx-ios_sim = target-sysroot-1-darwin-macos \ libffi-3.2.1-2-darwin-ios-sim \ clang-llvm-3.9.0-darwin-macos \ target-sysroot-1-darwin-ios-sim
It’s now time to build the compiler, by following the (few) steps described in the README.md of the Kotlin/Native project.
In particular, we’ll have to execute
./gradlew dist distPlatformLibs
Next step, is to build the uikit project for the simulator. To do that, we simply have to change the
konan.targets = ['iphone'] in samples/uikit/build.gradle to read
konan.targets = ['iphone_sim'].
When I started building, without success, I hoped for some intelligible error message. And, in fact, there it was. Turns out, prior to building, konan downloads a number of dependencies needed for compiling to a specific target. Archives bundling the dependencies are pulled directly from downloads.jetbrains.com, saved to the ~/.konan/cache directory and then extracted to the ~/.konan/dependencies folder.
In the case of the iOS Simulator, two of those are actually missing from the jetbrains repository: libffi and target-sysroot.
Libffi is an open-source project providing a Foreign Function Interface. As the libffi repo README says, Libffi is
a foreign function interface is the popular name for the interface that allows code written in one language to call code written in another language
Kotlin Native uses libffi to call Objective-C platform frameworks, such as UIKit or Foundation.
Unfortunately, while downloads.jetbrains.com provides a compiled libffi library supporting the iphoneos platform, there is no version available for iphonesimulator. So, time to build it from source. Luckily enough, the libffi repo already contains a pre-configured libffi.xcodeproj to build the library, so I just had to download it, generate Darwin headers files (using the generate-darwin-source-and-headers.py script) and producing a static library with Xcode.
Feel lazy? Here is a pre-compiled version, built with Xcode 9.0.
It’s now possible to build the library and archive it in a file named libffi-3.2.1-2-darwin-ios-sim.tar.gz. The archive has then to be inserted into the _~/.konan/cache _folder. This procedure is needed for konan to retrieve the libffi from the cache, instead of trying downloading the archive from the online repository.
The target-sysroot is, in this case, the SDK we’re targeting, in other words the iphonesimulator. You can get a list of the sdks installed with Xcode by running
xcrun --sdk iphoneos11.1 --show-sdk-path
in the terminal.
Since we’re looking for the iOS simulator SDK, we’ll be typing again in the terminal
xcrun --sdk iphonesimulator11.1 --show-sdk-path
to retrieve the SDK path.
Normally, the SDK is ready to be used for our app to be built against. Instead, some minor modifications should be added for the compilation to succeed. In particular, I needed to tweak the NSUUID.h header in _
Of course, the whole folder had to be tarred to a target-sysroot-1-darwin-ios-sim.tar.gz file and then moved into ~/.konan/cache.
Once again, this procedure is needed for konan to avoid downloading the archive from the online repository.
Once libffi is build, the target sysroot is retrieved and both are achieved and inserted in the cache folder, it was time to compile the kotlin runtime for the iphonesimulator.
The runtime will be linked to iOS app during the build process, and this will ultimately allow our kotlin code to access the functions provided by the iOS native frameworks.
In order to build the runtime for the Simulator, we simply have to cd to our kotlin-native source folder and run
Building the runtime may take up to 10/15 minutes, so get grab a coffee and please come back more excited than ever.
Alright, all the prerequisites are in place!
Before running, back to Xcode, let’s change the “Replace Binary” phase to read
cp "$SRCROOT/build/konan/bin/iphone_sim/app.kexe" "$TARGET_BUILD_DIR/$EXECUTABLE_PATH"
And, that’s it. Just build the app as you would normally do and run it in an iOS simulator.
To be honest, I’m pretty excited by the result and I can’t wait to do some more with kotlin for iOS.
As for the technicalities of the articles, even if my initial goal involved some degree of knowledge on subjects I barely know about, turns out that there was not much work to be done after all: in fact, the Kotlin Native team did all the heavy lifting, as the simulator support was almost finished apart from some missing bits.
Most of all, that was, to some extent, a rewarding task and a good opportunity to submit a PR to the team.