Project Structure & Source Sets
The shape of a KMP project
A typical KMP project has a shared module plus the platform apps. The shared module is where the magic happens; the Android and iOS apps consume it.
project/
├── androidApp/ # the Android app (Jetpack Compose, etc.)
├── iosApp/ # the iOS app (SwiftUI, Xcode project)
└── shared/ # the Kotlin Multiplatform module
└── src/
├── commonMain/ # shared Kotlin for ALL platforms
├── androidMain/ # Android-only Kotlin
└── iosMain/ # iOS-only Kotlin
Source sets explained
- commonMain — code that works on every target. Put as much here as possible: models, networking, business logic.
- androidMain — code that can use Android APIs (e.g.
Context). - iosMain — code that can call Apple/iOS APIs.
- commonTest / androidTest / iosTest — tests, mirroring the same split.
Code in commonMain can only use multiplatform libraries — it can’t reference Android or iOS APIs directly (that’s what the platform source sets are for).
Configuring targets in Gradle
The shared module’s build.gradle.kts declares which platforms you target and which libraries each source set uses.
kotlin {
androidTarget()
iosArm64() // real iPhones
iosSimulatorArm64() // the simulator on Apple silicon
sourceSets {
commonMain.dependencies {
implementation("io.ktor:ktor-client-core:2.3.12")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1")
}
androidMain.dependencies {
implementation("io.ktor:ktor-client-okhttp:2.3.12")
}
iosMain.dependencies {
implementation("io.ktor:ktor-client-darwin:2.3.12")
}
}
}
Notice the same Ktor client is in common, but each platform adds its own engine (OkHttp on Android, Darwin on iOS) — a very common KMP pattern.
How each app uses the shared module
- Android — adds
sharedas a normal Gradle dependency and calls its classes directly. - iOS — Gradle builds the shared module into a native framework that the Xcode project imports; Swift then calls it like any library.
Getting started quickly
Use the official Kotlin Multiplatform wizard (or the Android Studio KMP plugin) to generate this whole structure for you, pre-configured — far easier than setting it up by hand.
Common mistakes
- Trying to use an Android API in
commonMain(it won’t compile for iOS). - Forgetting to add a platform-specific engine/dependency (e.g. the Ktor Darwin engine for iOS).
- Setting up the project by hand instead of using the wizard.
Summary: A KMP project has asharedmodule withcommonMain(works everywhere) plusandroidMain/iosMainfor platform code. Configure targets and per-platform dependencies in Gradle, and let each app consume the shared module natively.