KMM Journey — Migrating Native Android App to Kotlin Multiplatform Apps KMM(iOS & Android)

-Suresh Maidaragi

Engineering @ PhysicsWallah
4 min readNov 25, 2024

Rationale behind choosing Kotlin Mutliplatform (KMM)

  • Leveraging existing Android code to expedite the development of iOS features.
  • Enabling shared UI and logic, which significantly accelerates development and release cycles.
  • Reducing development and maintenance efforts.
  • Seamless interoperability between Android (Kotlin/Java) and iOS (Swift/Objective-C).

How KMM Helped to solve bandwidth crunch?

We have lessor iOS devs then Android devs, So we decided to use Android devs existing knowledge on Jetpack compose + Kotlin to build KMM apps.

We used existing compose+ kotlin experts into KMM with minimal effort on learning started migrating features.

We picked feature which is completely built on jetpack compose and then we started migrating the layer by layer of Native Android App code to Compose Multiplatform.

Strategy for Migrating Existing Android Compose Code to Compose Multiplatform

We deconstructed the Native Android app’s features into distinct layers — Analytics, Dependency Injection (DI), Network, Data, and UI — and organized the migration process accordingly. We first created a standalone KMM project with a structure analogous to the Native Android packages and began by migrating non-Android components such as Analytics, DI, and Data. This was followed by Android-specific components like Composable’s, Navigation, and ViewModels.

Recent updates from JetBrains have made Android Jetpack components usable on Multiplatform, which significantly eased our migration process since these components were already integral to our Native Android apps.

What Are The Challenges Faced During Migration?

The migrated code functioned seamlessly, mirroring the performance of Native Android & iOS. However, in addition to the KMM migration, we also utilised the .framework generated from KMM directly in the iOS app, which presented three significant challenges:

  1. Huge device memory consumption on all iOS devices.
  2. Post generating framework from KMM code, .framework size is of around ~40MB which is unnecessarily introducing more space into existing app.
  3. Buildconfigs and build flavours in case of ios is bit pain to setup and use. So we ended up using BuildKonfig library

when we said memory issues, you might be interested to know in which case are this issue occurring, in below paragraph, I have tried to keep it in brief.

Post migration we found app is consuming the a lot of memory in iPhones & iPads, as we are trying to load images with just ~3MB, still everytime we load screen, memory footprint increasing, and we were totally blocked for release, so then we reached few of jetbrains dev, their suggestion helped,

Areas Where Memory Spikes Occurred:

  • Loading images
  • Repeatedly rendering bottom sheet Composables on the same screen.

image: shows memory consumption on iPad 6th gen

Solutions Implemented:

  • Integrated the Nuke image loading library in Native Swift, which efficiently handles memory caching and clearing.
  • Upgraded to the latest versions of Compose and Kotlin.
  • Customised garbage collection (GC) options in gradle.properties:
buildkonfig.flavor=staging
kotlin.native.binary.mimallocUseCompaction=true
kotlin.native.binary.objcDisposeOnMain=false
-Xbinary=disableAllocatorOverheadEstimate=true
-Xallocator=custom
kotlin.native.internal.GC.threshold=5
Xbinary=concurrentWeakSweep=true
kotlin.native.internal.GC.collect()=true

Framework Size:
The iOS .framework generated from KMM was approximately 45MB, comprising 25-30MB from shared UI components like Skia, video, and audio players, with the remaining 15MB attributed to our codebase and resources.

What are the tools & services we used to build/migrate KMM apps

  • Programming Languages: Kotlin & Swift
  • UI: Compose Multiplatform and SwiftUI
  • Data Storage/Access: RoomDB and Datastore
  • Network Requests: Ktor & Ktorfit
  • Dependency Injection: Koin
  • Analytics: Crashlytics and 3rd-party analytics tools
  • Architecture: MVI & Clean Architecture
  • Logging: Napier
  • Image Loading: Compose image loading library
  • Jetpack Components: JetBrains Jetpack Compose ViewModel, Navigation, Lifecycle

post migration the very first question raise’s is

How can I generate iOS build/framework from KMM project & use it in existing native iOS project?

  • Android Studio provides built-in functionality to generate .xcframework for iOS and .aar for Android when creating libraries for KMM projects.
  • The assemble option produces libraries and frameworks for specific platforms.

assemble option would leads to create libraries and frameworks for specific platforms

Afterward, open the fat-framework with Finder, zip the pitaraShared.framework, convert the .framework to .xcframework, and integrate it into the native iOS app.

Conclusion on Migrating Native Android Code to KMM

Based on our experience, migrating existing code written in Compose requires minimal effort.

However, if your codebase relies on multiple XML layouts, Fragments, and Activities, we recommend migrating to Compose first before transitioning to KMM.

In terms of performance and user experience, we observed similar behaviour between native apps and KMM apps.

--

--

Engineering @ PhysicsWallah
Engineering @ PhysicsWallah

No responses yet