Adopting Kotlin Multiplatform (KMP) on 9GAG App
Opinions expressed are solely my own and do not express the views or opinions of my employer.
Note: 2023–10–12 The name KMM had been officially re to KMP
It is so excited to see that Kotlin Multiplatform Mobile (KMM) enters beta stage and Google has announced that they have published experimental Jetpack libraries to KMM. It is so thrilling to see Google jumps in and porting Jetpack libraries to KMM, this could probably give KMM a further push. I believe KMM will be the future of mobile development if more and more frameworks or libraries are supported.
React Native
9GAG has been trying out various cross-platform solution and yet none of them fully went to production. We tried to re-write the whole application in React Native in the 2016, everything went well at the beginning yet as we were adding more and more features, application’s performance kept degrading, the app size kept increasing. Besides, as we added more and more libraries, we entered into a dependency hell.
We turned out drop the whole project and going back to native development.
Flutter
Back in 2020, Flutter was getting the traction. We tried out Flutter on a side project. In my opinion, Flutter has a much better performance than React Native and the Material Theming looks pretty well. In terms of app size, it is quite decent thanks to app bundling support.
I was asked if Flutter was the right cross-platform solution, I hesitated. The reason was that the support for ads and Firebase was not that good at that moment. Moreover, there was some concern for images and videos handling, I was not comfortable to recommend this to the team.
KMM
Then in 2021, we knew that Jetbrains is developing their multiplatform solution: Kotlin Multiplatform Mobile. I was a bit skeptical at the very beginning, it looks like something similar to Xamarin and it was still in alpha stage, which may not be a good solution. One of my colleagues suggested to try it out as we had a side feature to develop at that time.
Why jumping into KMM?
We are still looking for ways to align the behaviour of the app on both platforms. The problem we used to have with React Native and Flutter is that, if the project failed, all the code you have written is wasted and that was what I felt skeptical at the very beginning. However, after a second thought, it was not the case and I agreed to give KMM a try and that is how the journey started.
Even if our code written in our KMM repository failed to port to iOS, we can still use it on our Android app, no waste at all! That is the simple reason that I would like to give it a try.
How
The ultimate goal is: existing iOS and Android app can use KMM as the logic layer. I believe that is the most of the cases for many of us instead of creating a completely new app with KMM. The goal seems big, but here are some tips on how to start small. This talk from Droidcon NYC 2022 was talking almost about the same thing what we have done!
To begin with let’s have a high level project structure overview of 9GAG:
:ninegag-shared-lib
— This served as a wrapper project, contains no code inside, but to use it for exporting XCFramwork (NineGagKmm) for iOS:ninegag-shared-app
— KMM code that contains business logic, app constants, etc:core
— Another KMM project, but contains some low-level stuff like some util class, password/signature encoding methods, which can be reused by other projects in the future.
Android project can simply call implementation project(":ninegag-shared-lib")
to include all KMM code as usual
iOS project will include the XCFramwork binary built from the project :ninegag-shared-lib
.
Step 1: Start from Android
Needless to say, Kotlin is Android’s first class language, the first intuition is to start with existing Android app first. Setup a KMM project that your existing Android app can include it as a dependency. That should be a easy first step.
Step 2: Making KMM project compilable on iOS
The next step is to include the KMM project into your existing iOS project.
We took a while to research for the project structure. The sample project given did not suit our use case. At last we found an article on how to consume KMM library on iOS. The idea is simple, we treat the logic written in KMM as a library, and then both Android and iOS can just include that library like other libraries.
Hooray! You have done this step, as an Android developer, this was the most daunting part as I have no knowledge on iOS development and take time to learn. Moreover, I had to communicate with iOS teammates to understand their project structure, how to run the app on Xcode, etc.
Step 3: Start small with constants
Once step 2 is done, you may think about what to do next on code sharing. Implementing or migrating tracking constants to your KMM project will probably be one of the simplest thing you can do. Why? No code logic, no need to include 3rd party libraries, just constants. They can be easily used by iOS and let iOS developers to get a feel of what KMM is. :D
At the time we implement KMM, we were working on a side feature and we included many KMM related libraries to our codebase. For example, Ktor, for API call, SQLDelight for caching data to database, multiplatform-settings for KV store and so on.
Depends on your situation, you probably not need to include so many things as your start. Therefore, I suggest to keep it simple.
Step 4: Start to migrate your common utils to KMM
I believe there are many util functions in your codebase that are not bounded to Android framework. For example, a util that converts a password to a hash, generate API signatures, or generate a colour code base on the user id or name, etc. These are all good candidates to move to your KMM project.
Step 5: Try writing or migrate business logic in KMM
Now, you should be more confident with KMM. You may start to migrate business logic or writing a new one with KMM, exploring doing some asynchronous stuff with coroutines!
This is the trickiest part according to my experience due to the time we implemented it as there were bugs that crashed iOS app with the Kotlin version that we were using (1.6.x).
Next
Once we started using KMM, I am thinking to share things as much as possible such that KMM project will act as a single source of truth. Here are some ideas for you:
- Network, API — Ktor
- Database — SQLDelight
- Resource sharing (String resources, Localization, Color code scheme) — moko-resources
- Encryption/Decryption — Krypto
Here is a curated list of KMM libraries you may check for: https://github.com/terrakok/kmm-awesome
Challenges — Project setup
Of course, there were lots of challenges encountered during KMM integration. Here are a list of items that I found challenging:
On iOS side
- What is Podfile?
- How libraries are related?
- How their dependencies work?
- How Cocoapods works?
- What is XCFramework
- and so on and iOS devleopers are not familiar with Gradle stuff
On KMM’s Gralde setup side
- How the Gradle script should be setup to produce a framework that can be consumed by iOS?
Challenges — Development
- Not familiar with Swift
- Calling coroutines in iOS
- Strange variable names on iOS (e.g.
Kotlinx_coroutines_coreCoroutineDispatcher
) - Auto-complete in Xcode shows lots of unrelated variables
Challenges — Workflow
As in the past, Android and iOS team has different set of workflows, e.g. in PR creation, branch naming, commit messages, CI provess and so on. With KMM’s integration, it also create an initiative to align our workflow such that we can unify and align the two teams into a single mobile team! (Ultimate goal)
Conclusion
In my opinion, KMM is definitely the future of mobile development as we are seeing more and more support coming. Though the learning curve is deep, it is a worthy investment, as KMM not only help us to share our code, but it also make us to think about how to align our workflow. As at the end of the day, we code to ship features to our users, how we can ship it faster and provide a consistent experience to our users is the ultimate goal.