Migrating an Android app to iOS with KMP — Part III: UI and Compose Multiplatform

Igor Escodro
ProAndroidDev
Published in
6 min readMay 6, 2024

--

Moving forward in the Migrating an Android app to iOS with KMP series, we are now focusing in creating a multiplatform user interface and thanks to Compose Multiplatform, migrating the code from Jetpack Compose is fairly simple, with a few caveats we will cover in this article.

Since there are a lot of details in each of the parts of the migration, I won’t deep dive on all of them. The migration code is shared in the sections below, but if you want to know more about specifics, let me know and we can follow up in an upcoming article.

This article is part of a series of migrating an existing Android app to run on iOS using Kotlin Multiplatform. You can access the other articles in the following links:

Architecture

To recap, let’s take a look at the application architecture after our last article, where we migrated all the data sources to KMP. We are close to have a full-fledged application running in both Android and iOS, we are only missing the user interface.

Alkaa’s simplified architecture

Compose multiplatform

The teams in Google and JetBrains are doing a great job in ensuring that the operability between Jetpack and Compose multiplatform works seamlessly. Almost every single Composable in the application simply worked when I migrate to multiplatform. We still need to handle the platform specifics such as notifications, permissions, resources, and soon-to-be-stable libraries such as navigation, ViewModel and Parcelable.

For simpler screens, the main changes during the migration were minimum, being mostly how to get resources such as strings and icons. Yes, even the imports stays the same (androidx.compose.*).

Codes comparison in GitHub to demonstrate the small changes

Resources

Speaking about resources, even though we don’t have an embedded way to get resources in the Compose Multiplatform framework yet, we have some great libraries from the community. The one used in Alkaa is the amazing IceRock’s Moko Resources.

After a quick setup, Moko Resources work very similar to the default Android resources, using .xml files and <string/> tags. The name convention and localization support is also similar, relying on in different folders with the locale code.

Moko Resources folder structure

With everything in place, using the resources is as simple as in Jetpack Compose, using the stringResource():

Keep in mind that using resources out of the context of Compose, we would need to provide different implementations per platform since iOS and Android uses different parameters.

Additional considerations during the setup

The code with the initial setup and module migration can be found here.

Native implementations

As expected, some parts of the code need to be implemented using native libraries, while we don’t have multiplatform alternatives. In the case of Alkaa the native implementations are mainly alarms, notifications and home screen widget.

The fun part of Kotlin Multiplatform is that even the Swift/Objective-C native code can be developed using Kotlin. Being an Android/Kotlin developer, this made my life much easier. Also, I need to give a lot of credit to GitHub Copilot that helped on this journey to create Kotlin code for iOS.

Here’s an example of native iOS implementation in Kotlin:

iOS APIs shining in Kotlin code

Navigation

At the time of Alkaa’s migration, we didn’t have the Android Jetpack Navigation available and searching for alternatives, I found Voyager and immediately love it. Voyager is a pragmatic approach to navigation, and it is a perfect match for Alkaa’s simplistic navigation graph.

If you are used to Koin, the migration to Voyager will be very easy

Voyager follows a similar DSL approach to Koin which makes multimodule and encapsulation a breeze. The full migration from Jetpack to Voyager can be found here and here.

ViewModel

The ViewModel is another one of the Android libraries that are recently ported to Multiplatform, so I ended up using another Moko library called Moko MVVM. It not only adds support to ViewModel, but also LiveData, View Binding and Data Binding if you are still using it.

The migration was basically switching imports

The migration is straightforward, but platform-specific injections will be needed for Android and iOS.

Dynamic features

One interesting challenged I faced while migrating Alkaa was how to handle the on-demand delivery module. To summarize, in Android, we can set up modules to be downloaded on the fly during app execution to save app size. Even though iOS have a similar feature, I didn’t want to deep dive on it and decided to make it always available for the iOS variant. For my surprise, it was much easier than I thought.

Since KMP allows a different set of dependencies for each platform, it was easy to split them. For Android, we still use our “Split Install” library to download the dynamic module; for iOS we can directly depend on it, making it available right away.

The full implementation can be found here.

Running the apps 🚀

As mentioned in previous articles, having a good modularization and separation of concerns allowed each layer and feature to be ported independently. For most part of the development, Alkaa had a few of features ported and two navigation graphs (Android and KMP) at the same time.

After porting all the features, we have an application that is very similar in the two platforms, with minimum platform-specific code. Keep in mind that Alkaa is a very simple application in features and scope, but gives a good idea on how much we can reuse as it is from the existing Android codebase.

Alkaa running in Android (left) and iOS (right)
Less than 1% of the code is Swift today

What’s next?

I am very happy that this series is getting outdated due to the amazing job that the Google developers are doing to port more AndroidX libraries to support multiplatform. Here’s a small recap on the libraries with KMP support:

If you don’t follow John, do yourself a favor. Also check his amazing GitHub repositories

As always, the full code is available in Alkaa’s repository and if you want to dive in the 97 commits that migrate the app from Jetpack to Compose Multiplatform, that’s the pull request:

In the next article, we are covering how the pipeline was updated to generate both the Android and iOS apps and publish in the store automatically. I will also share some closing thoughts about this amazing journey. If there is something else you want me to cover, please leave a comment and I’ll do my best to address them. 😊

Thank you so much for reading my article! ❤️

--

--