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
| Layer | Share it? | Note |
|---|---|---|
| Domain models and business rules | Yes | Pure Kotlin, no platform APIs - the easiest win |
| Networking and serialisation | Yes | Ktor or a multiplatform wrapper, kotlinx.serialization |
| Persistence | Partly | SQLDelight shares the queries; the driver is per platform |
| Coroutine and flow logic | Yes | kotlinx.coroutines is multiplatform |
| UI | Sometimes | Compose Multiplatform for Android, iOS and desktop; SwiftUI still common on iOS |
| Platform integration | No | Notifications, permissions, sensors stay native |
| Build and release tooling | No | Gradle, 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.
expectandactualshould 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
| Library | Solves | Watch out for |
|---|---|---|
| CameraX | Camera preview, capture and analysis | Lifecycle binding and device-specific quirks |
| Paging 3 | Incremental lists with a remote or local source | The load state handling is genuinely fiddly |
| DataStore | Typed, async preferences | One instance per file, or you get a corruption error |
| SplashScreen | A correct, themeable launch screen | Design it to match the first frame to avoid a visible jump |
| App Startup | Initialisers that run once at launch | Keep initialisation off the main thread |
| Compose Material 3 adaptive | Layouts for phones, foldables and tablets | Window size classes are the API to actually use |
| Baseline Profiles | Precompiled startup paths | Regenerate 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
minSdkis 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.
Related
Android projects and activities Performance, accessibility and Play quality
Last refreshed 2026-09-18.