Kotlin multiplatform, swiftui and jetpack compose
I’ve been writing an app recently in using the kotlin multiplatform mobile plugin, with an mvi architecture, swift ui for the iOS ui and compose for the android. Right from the start I’ve focused on doing as much of the coding in kotlin mpp as I reasonably can and in this article I’m going to talk about my general approach and the libraries I’ve used.
The easiest part to implement has actually been the database part. Rather than a traditional orm I’ve been using SQL delight, which takes the opposite approach — in a traditional orm SQL is generated from models, in SQL delight models are generated from SQL. As a library even without the multiplatform aspects it’s easy to work with, and it works very seamlessly with iOS and Android.
Kotlin multiplatform mvi
This is a library which has been very well thought through and has been very well developed- it has the option of coroutines or reaktive (I’ve opted for coroutines) and generally works well for my project, In iOS I instantiate the “store” inside a swift class called “proxy” and subscribe to states / labels from this proxy, before converting them in to an @published version of the state — meaning my swift ui is rebuilt automatically on state change. Handling labels (also called effects) is a bit more convoluted, depending on the type of effect but in most cases it will update some other state/ publisher that the ui is listening for, e.g a Boolean saying whether or not a snack bar should appear or else the tag describing which screen should be shown.
For Android, I’ve tended to wrap the store around an android view model and in inject using by viewmodel(), for the uis state I use the states.toLiveData().observers state() so my compose ui is built on state change, and for the labels/ effects I listen to the flow on a lifecycle scope and update the appropriate state or trigger an action there.
I’m going to write in future another article on gradle setup for multiplatform kotlin libraries but the main thing to say is that there is a gradle plugin for building xcode projects which means that with one gradle build command I’m able to build the entire project. While this might not be such a big deal for bigger teams, my ci setup has been made far simpler by having a single build command
Fastlane is a tool particularly popular with iOS developers but for this project I’ve setup fastlane twice for automated app store deployments for both iOS and Android. I’ve mostly used this for capturing screenshots automatically, as well as uploading beta builds to the app store. This actually brings me on to one of the best things about kotlin mpp over flutter — native docs are always the most reliable. There was no need to do anything differently for the iOS or Android setup of fastlane then for anything else.
I’m often accused of being a Firebase salesman but to setup basic analytics and crashlytics was a fairly straightforward process. Launching the app just involves setting it up like you would any other android or iOS project, and then the process for logging specific analytics from within your kotlin mpp code is similar to this:
Platform specific, but not kotlin
There are a few instances in my app where I need to call platform specific code, and it either doesn’t make sense to do it in kotlin or its not possible. The two examples that spring to mind are the feature I have for copying to clipboard, and another for storing/ creating and using encryption keys. For this, I’ve created an interface which is used by the multiplatform code, implemented for each platform and passed into the constructor of a multiplatform library. For example, I have an iOSPasteboardManager in swift and an AndroidPasteboardManager both implementing the same interface, with that interface being passed to the constructor of my store. To be fair this is good practice anyway for testability but it’s worth noting that this is not difficult for kotlin multiplatform mobile.
My app is still in development, and some of the things I’m hoping to do before then are Auth, backend integration (and indeed a backend), biometric and pin screens, app clips / instant apps and a fully formed ci/cd. Next up though, I’ll be writing more detail on some of the topics above with source codes and examples. Let me know if there are any specifics you want covered.