A KMP project organizes code into source sets: commonMain holds platform-independent business logic, while androidMain, iosMain, and similar hold platform-specific code. Where shared code genuinely needs something platform-specific (reading a file, accessing a platform API), commonMain declares an expect function or class with no body, and each platform's own source set supplies the matching actual implementation — the compiler links them together per target. As of Kotlin 2.4 (2026), KMP tooling has continued maturing significantly, with faster builds, smaller compiled binaries, and improved Kotlin/Native interop specifically highlighted as ongoing focus areas.
expect: declaring what shared code needs
expect has no body at all — it's purely a declaration, a contract that says "every platform building this project must supply a real implementation of exactly this signature."
expect fun getPlatformName(): String // no body — just a contract
fun greet(): String {
return "Hello from ${getPlatformName()}" // shared logic, calling the expect declaration
}actual: each platform's real implementation
Each platform provides its own genuinely different actual implementation — shared code in commonMain never needs to know which one actually ran; it just calls getPlatformName() and gets the correct platform-specific answer.
actual fun getPlatformName(): String = "Android"actual fun getPlatformName(): String = "iOS"What's actually shared vs platform-specific in practice
Business logic, data models, and networking code are typically 100% shared in commonMain; UI code is usually platform-specific (Jetpack Compose for Android, SwiftUI for iOS) or built with Compose Multiplatform, which extends Compose itself to render across platforms — the split depends heavily on how much UI-sharing a given project actually wants.