Next steps: Kotlin Multiplatform and the Jetpack ecosystem

What Kotlin Multiplatform shares and what it does not, Compose Multiplatform, common Jetpack libraries worth adopting, and staying current.

Kotlin Multiplatform in practice

LayerShare it?Note
Domain models and business rulesYesPure Kotlin, no platform APIs - the easiest win
Networking and serialisationYesKtor or a multiplatform wrapper, kotlinx.serialization
PersistencePartlySQLDelight shares the queries; the driver is per platform
Coroutine and flow logicYeskotlinx.coroutines is multiplatform
UISometimesCompose Multiplatform for Android, iOS and desktop; SwiftUI still common on iOS
Platform integrationNoNotifications, permissions, sensors stay native
Build and release toolingNoGradle, Xcode, Play and App Store pipelines remain separate
// commonMain: a repository with no platform dependency
class BookRepository(
    private val api: BookApi,
    private val store: BookStore,
) {
    suspend fun sync(): Result<List<Book>> = runCatching {
        val books = api.fetch().map(BookDto::toDomain)
        store.save(books)
        books
    }
}

// expect/actual declares the platform seam
expect class PlatformClock() {
    fun nowMillis(): Long
}

// androidMain
actual class PlatformClock actual constructor() {
    actual fun nowMillis(): Long = System.currentTimeMillis()
}

// iosMain
// actual class PlatformClock actual constructor() {
//     actual fun nowMillis(): Long = NSDate().timeIntervalSince1970.toLong() * 1000
// }
  • Share the layer with no platform dependencies first. A shared module that reaches into Android types is worse than no shared module: it needs a platform abstraction for every call.
  • expect and actual should be a thin seam, ideally a handful of declarations, not a shadow of the Android SDK.
  • Compose Multiplatform is production-ready for Android, desktop and iOS, and the iOS interop has improved considerably - but the debugging and tooling experience is not identical across targets.
  • A KMP project has two build systems in the room. Budget for the Xcode side: signing, provisioning and the iOS build in CI are not free.
💡
Start with one shared module and one feature. Sharing a whole app across platforms at once is how projects end up fighting the tooling instead of shipping. The pieces that share well are the ones that were already testable without Android.

Jetpack libraries worth adopting

LibrarySolvesWatch out for
CameraXCamera preview, capture and analysisLifecycle binding and device-specific quirks
Paging 3Incremental lists with a remote or local sourceThe load state handling is genuinely fiddly
DataStoreTyped, async preferencesOne instance per file, or you get a corruption error
SplashScreenA correct, themeable launch screenDesign it to match the first frame to avoid a visible jump
App StartupInitialisers that run once at launchKeep initialisation off the main thread
Compose Material 3 adaptiveLayouts for phones, foldables and tabletsWindow size classes are the API to actually use
Baseline ProfilesPrecompiled startup pathsRegenerate after large code or navigation changes
// adaptive layout without branching on device type
@Composable
fun BookScreen(windowSizeClass: WindowSizeClass, state: BookUiState) {
    when (windowSizeClass.widthSizeClass) {
        WindowWidthSizeClass.COMPACT -> BookListOnly(state)
        WindowWidthSizeClass.MEDIUM -> BookListAndPreview(state)
        else -> BookListPreviewAndDetail(state)
    }
}

// compute it from the activity window
val windowSizeClass = calculateWindowSizeClass(activity)

Foldables, tablets and ChromeOS all run the same app. Branching on screen width rather than device model is the difference between an app that adapts and an app that looks stretched.

Staying current

  • Follow the Android release notes for behaviour changes, and the yearly behaviour-changes page for the target SDK you are moving to. The permission and background-execution rules change regularly.
  • Test on the current and previous two API levels. Supporting an old minSdk is a product decision; testing against it is not optional.
  • Read the Compose performance and stability documentation once you have a screen that feels slow - it explains exactly which parameter types break skipping.
  • Keep one instrumented test suite and one macrobenchmark running in CI. They are the two things that catch regressions the JVM tests cannot see.
  • Prefer the AndroidX library over a hand-rolled solution for anything involving the platform lifecycle, permissions or background execution. Those are the areas where the framework knows things you do not.
  • Read the official architecture guide and pick the parts that fit your team. The samples are a reference implementation, not a mandate.

The stack that has settled over the last few years - Kotlin, Compose, coroutines and Flow, ViewModel, Room, Hilt, Retrofit or Ktor, WorkManager - is stable and well documented. Getting fluent in those is worth more than chasing each release.

FAQ

Should I rewrite my app with Compose?
No. Migrate screen by screen using ComposeView inside the existing view hierarchy, and start with new screens. A wholesale rewrite risks the parts of the app that work and are not otherwise changing.
Is Kotlin Multiplatform worth it for a small team?
When you have both an Android and an iOS app and the business logic is non-trivial, sharing the domain and data layers usually pays for the tooling cost. For a single-platform app it is pure overhead.

Android projects and activities Performance, accessibility and Play quality

Last refreshed 2026-09-18.