Kotlin Multiplatform as iOS Developer
Let’s be honest, Java is popular... very popular and in the future, Kotlin can be the next big thing, as the popularity is expected to grow as it is now. According to Stack Overflow's 2018 Annual Developer Survey Kotlin is the second beloved language among Developers.
Kotlin Multiplatform?
Kotlin Multiplatform puts together all the hard work done by JetBrains to allow compilation of Kotlin to three different targets: JS, JVM and Native being three separate projects that you could use separately as well. Kotlin Native is one of them and it's basically a solution for compiling Kotlin code to native binaries. Primarily it is designed to allow compilation without any virtual machine. At the moment target platforms are:
- iOS
- MacOS
- Android
- Windows
- Linux
- WebAssembly
It means our business logic could be written once and shared natively on all of those platforms. Nice!
Cross-platform
When deciding about native or cross-platform development, there are a few things we must consider. First of all money and time - these are the most important things from the business perspective. As we all know cross-platform apps are cheaper and faster to create. Shared code between platforms seems to be a great idea, but … is it always so great?
Here comes the second thing - technology.
We have Xamarin that compiles to CIL and React Native which is basically JS. With both of them, we have additional abstraction above the UI layer. We are also running a code that might not be the most performant on given platform. JS will have to run on V8/JavaScriptCore and then communicate with native code through some bridge. Same would happen for Xamarin that would use ACW and MCW to wrap native calls (mechanism similar to JNI) on Android.
Kotlin Multiplatform compiles to multiple targets, giving us native binaries where we want them - iOS or JVM bytecode output - e.g. Android.
Also, we are using standard components made for UI building, so we can completely separate and differ UIs for both platforms.
Let’s look at our Kotlin Multiplatform Storage as an example (Made in Kotlin Multiplatform). You can read more about creating it here
We wanted to create secure keychain wrapper for both platforms, so I guess UserDefaults wasn't the best place for this kind of data. Firstly we tried to implement Keychain fully written in Kotlin, what at the end seemed to be impossible, due to lack of Objective-C casting. After many tries, we finally decided that we will use c-interop tool, for created native library and connect it into Kotlin App. So what we did, was basically creating Objective-C library, that will create Kotlin Interface from linked framework, so we can use it in the Swift app! Yeah… I know - Cross-platform...
Right now it is hard to tell if any of these could be done better. This technology is still very experimental, no documentation, no tutorials, no Stack Overflow topics.
What are the basics behind Kotlin Multiplatform?
We have two types of modules - platform-specific and common.
Platform-specific module defines view and implements code from the common one.
The common main module is the one that keeps all the common part of the project. The code placed in this module is shared between every declared platform. However, it has some limitations, because you cannot access platform-specific features here. Instead, common main module can define a common interface for those features using expect/actual mechanism. So, this a good place to implement common business logic!
Platform package (e.g. androidMain) is a code that depends on the platform. For example framework implementation for the platform.
Implementation
So here is an example of the module for the iOS platform
Simply to make use of shared code base, you have to implement interface in your Swift class. In this example, we will have MainView interface
Kotlininterface MainView {
fun showElementAddedInfo()
fun showToDoList(toDoList: List<String>)
}
Swift
final class MainViewController: UIViewController, MainView { }
Interface provides you methods that are used in Kotlin. So this is how Swift „delegates” Kotlin methods. And how it works in the other way?
We simply need to create instance of Presenter written in Kotlin so we can use methods created there.
Kotlin
class MainPresenter {
fun foo() {
print(„bar")
}
}
Swift
private lazy var presenter: MainPresenter = {
MainPresenter()
}()
func bar() {
presenter.foo()
}
Another thing, which we can’t forget is attaching and detaching Views
We should attach view on the creation of ViewController instance. Without it, Kotlin code cannot be called properly.
presenter.attachView(view: self)
attachView is creating strong reference to passed object, so to release object we have to simply call
deinit {
presenter.detachView()
}
Kotlin
fun detachView() {
this.view = null
}
After all, we don’t need much to make use of the shared code base, but above we have scenario when the whole environment is already set up. So you will have to import generated framework, change Framework Search Paths in build settings, and call scripts. If you want to give it a try you should check this out
Conclusion
As the iOS Dev, you can read Kotlin code without any problem just at the first sight. Syntax similarity for smaller snippets can be more than 60%, so it wasn’t a problem to read and write code in Kotlin (there are even ready to use converters like this one). Probably if not the annotations above the code snippets, it would be hard to tell which code is written in Swift or Kotlin. But the sad part is you still need to know how to create views on other platforms, so at least minimum knowledge about platform specific UI building is a must. Furthermore, solutions like Kotlin Native can be the future, one code base, and a lot of targets, including backend, frontend, mobile, and even embedded! Right now this technology is very experimental, and not production ready yet, but we will most likely hear much more about it in the future as there it is a lot of potential.