Kotlin Native — Using Swift, not Objective-C

Paul Woitaschek
Nov 19 · 3 min read

At YAZIO we use Kotlin Native Extensively. The business logic of all new app features is exclusively written in Kotlin.

In its core, every screen has one ViewModel which exposes a single Flow<ViewState> that the Android App and the iOS can consume to render the state.
On Android, rendering the view state is no burden because it’s the language we use in the Android App anyways.

However this is different for iOS. While Swift does interop with Objective-C, the public API as seen from Swift is bad. A simple data class is exported as a regular class. The properties are not native Swift types. What should be a Bool becomes a KotlinBoolean. What should be come a Double becomes a KotlinDouble.
But what we want is most of the times a struct with Swift types.

These are the lesser evils, the greater ones are enums and sealed classes. In Objective-C, they both become regular classes. And therefore lose all the advantages they have when used from Swift.
But what we want is a Swift enum.

To overcome these restrictions for quite some time our iOS team has created mappings. There is a whole target, dedicated to mapping the Objective-C classes to Swift. Basically when developing a new feature, the whole public API is manually mapped to Swift.

This is good because the result is a clean Swift-native API. But that’s bad because it’s extreme boilerplate and a real productivity killer.

What if we could directly generate these mappings and use code generation to handle that for us?

Turns out: There are two tools available that make it possible.

The first one is KSP. It is basically a library that lets you implement a Kotlin compiler plugin and it gives you hooks to analyze the Kotlin Syntax Tree and generate code based on it.
The second one is SwiftPoet. SwiftPoet is a library you can use to generate Swift code from Kotlin, similar to what KotlinPoet and JavaPoet do.

Okay, let’s get our handy dirty. For the basic setup, just follow the tutorial of KSP. Here is how the SymbolProcessor looks like. Basically it’s requesting all Symbols that have the annotation SwiftExport and then operates on them. If you want to generate enums unconditionally you could as well create a visitor and just handle all of them.

Now that we have the hook into each annotated class, we can start writing the code.

First we determine the name of the enum based on the name of the class declaration.
Next we just iterate on all it’s containing declarations and add the enum members as enum cases.

Now that we have the spec for the enum itself, we can build the FileSpec and write it to a file.

One important thing to remember here is to declare the sources for the generated file in line 11. This makes it possible for KSP to keep track of the inputs and outputs and enables incremental compilation.

If we now try that out and take a simple enum like:

After compiling we’ll find a fresh Swift file in our resources folder. 🎉

In practice, the code is a lot more complicated. We generate the mappings from enums to enums, sealed classes to enums and data classes to structs.
We are also generating extensions to map the types from Objective-C to Swift.

But this should be enough to demonstrate the principle.

Ah and before I forget: We are hiring! If you like to work on a well architected Nutrition & Health-App in a fully remote position and integrate interesting technologies, we’d love to receive your application 😄

YAZIO Engineering

Engineering at YAZIO