Learning Roadmap
Suggested order for going through this track. Check off items as you go β your progress is saved locally in this browser, no account needed.
Topics
1 What Is Kotlin Multiplatform?
Kotlin Multiplatform (KMP) lets you write business logic once in Kotlin and share it across Android, iOS, desktop, and web, while keeping platform-specific code (or fully native UI) wherever you actually need it. Unlike Flutter or React Native, KMP doesn't ask you to rewrite your whole app in a new framework β you keep native Android (Kotlin/Compose) and native iOS (Swift/SwiftUI) UI if you want, and share only the layer that benefits most from being shared: networking, data models, validation, caching, and business rules.
By 2026, JetBrains and Google have converged on KMP as the mainstream way to share mobile logic β Google's own Android team has published production guidance for adopting it incrementally, module by module, rather than as an all-or-nothing rewrite.
2 Project Structure: commonMain, androidMain, iosMain
A KMP module splits code into source sets. commonMain holds code that compiles for every target platform. androidMain and iosMain hold platform-specific implementations that only compile for that target.
shared/
src/
commonMain/kotlin/ // shared business logic, models, repositories
androidMain/kotlin/ // Android-specific implementations
iosMain/kotlin/ // iOS-specific implementations
commonTest/kotlin/ // tests that run on every target
Your Android app module and iOS app target both depend on this shared module, pulling in the compiled logic for their platform.
3 expect/actual Declarations
When shared code needs a platform capability that doesn't have a common Kotlin API (like getting the current platform name, or accessing secure storage), you declare an expect signature in commonMain and provide an actual implementation in each platform source set.
// commonMain
expect fun getPlatformName(): String
// androidMain
actual fun getPlatformName(): String = "Android"
// iosMain
actual fun getPlatformName(): String = "iOS"
This is the escape hatch that makes KMP practical: 95% of your logic stays platform-agnostic in commonMain, and only the small platform-specific slivers need an actual per target.
4 Compose Multiplatform: Sharing UI Too
Compose Multiplatform (JetBrains' extension of Jetpack Compose) goes a step further than sharing just logic β it lets you share the actual UI code across Android, iOS, desktop, and web using the same declarative Compose APIs Android developers already know. As of 2026, Compose Multiplatform's iOS target is stable and used in production apps, though many teams still choose native SwiftUI for iOS and share only the logic layer via plain KMP β both are valid, and the choice depends on how much you value pixel-perfect native feel versus one shared UI codebase.
@Composable
fun GreetingCard(name: String) {
Card(modifier = Modifier.padding(16.dp)) {
Text("Hello, $name!", style = MaterialTheme.typography.headlineSmall)
}
}
// This same function renders on Android, iOS, and desktop.
5 Networking with Ktor Client
Ktor Client is JetBrains' multiplatform HTTP client β the standard choice for shared networking in KMP, playing the same role Retrofit plays in Android-only apps.
val client = HttpClient {
install(ContentNegotiation) { json() }
}
suspend fun fetchUser(id: Int): User =
client.get("https://api.example.com/users/$id").body()
This function lives in commonMain and works identically whether it's called from Android or iOS β no per-platform networking code needed.
6 Local Storage with SQLDelight
SQLDelight generates type-safe Kotlin APIs from plain .sq SQL files, and compiles to the native database driver on each platform (SQLite on Android, the same on iOS via a native driver) β giving you one shared local-persistence layer instead of Room on Android and Core Data on iOS.
-- User.sq
selectAll:
SELECT * FROM User;
insertUser:
INSERT INTO User(id, name) VALUES (?, ?);
SQLDelight then generates selectAll() and insertUser() Kotlin functions you call from shared code, fully type-checked at compile time.
7 Dependency Injection with Koin
Koin is a lightweight, reflection-free dependency injection framework that works across all KMP targets (Hilt is Android-only, so it isn't an option for shared modules).
val appModule = module {
single { HttpClient { install(ContentNegotiation) { json() } } }
single { UserRepository(get()) }
}
// usage in shared code
class UserRepository(private val client: HttpClient) { /* ... */ }
8 Coroutines & Flow Across Platforms
Kotlin Coroutines and Flow work natively in commonMain β the same suspend functions and Flow streams you'd write for Android work unchanged on iOS, since Kotlin/Native has full coroutines support. This means a repository that exposes a Flow<List<Item>> for reactive UI updates works identically on both platforms, with the native UI layer (Compose or SwiftUI) collecting it appropriately for each.
9 Calling Kotlin from Swift
The Kotlin/Native compiler produces an Objective-C framework that Swift can import directly. Public Kotlin classes, functions, and even suspend functions (translated into Swift's async/await-compatible completion handlers) become callable from Swift with a mostly-natural API surface.
// Swift, calling into the shared Kotlin module
import Shared
let repo = UserRepository()
repo.fetchUser(id: 1) { user, error in
if let user = user { print(user.name) }
}
Some Kotlin idioms (default arguments, sealed classes, inline value classes) translate imperfectly into Objective-C/Swift, so it's worth designing your shared module's public API with iOS interop in mind rather than exposing every Kotlin-only feature.
Practice Quiz
Test what you just learned about Kotlin Multiplatform.