A KMP module targeting iOS usually compiles for three targets — iosX64, iosArm64, iosSimulatorArm64 — because the simulator and device use different architectures. Code that's identical across all three shouldn't have to live in commonMain next to your Android code, and it shouldn't have to be copy-pasted into three source sets either.
#The old way
Before the default hierarchy template, sharing iOS-only code across those three targets meant manually declaring an intermediate source set and wiring dependsOn edges by hand in build.gradle.kts for every module that needed it — easy to get wrong and easy to forget on a new module.
#The new way
kotlin {
androidTarget()
iosX64()
iosArm64()
iosSimulatorArm64()
sourceSets {
commonMain.dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")
}
androidMain.dependencies {
implementation("androidx.startup:startup-runtime:1.1.1")
}
iosMain.dependencies {
implementation("io.ktor:ktor-client-darwin:2.3.11")
}
}
}
No dependsOn calls anywhere in this file. Since Kotlin 1.9.20, the Gradle plugin applies the default hierarchy template automatically: declaring iosX64(), iosArm64(), and iosSimulatorArm64() is enough for the plugin to create an intermediate iosMain source set, wire all three native targets to depend on it, and wire iosMain itself to depend on commonMain.
#Why it matters
iosMainis real, typed Kotlin you can put code and dependencies in — not just a convention- The template also creates
appleMainandnativeMainfurther up the tree if you add macOS or watchOS targets later, without any Gradle changes on your part - Removes an entire class of "why did this compile on iosArm64 but not iosSimulatorArm64" bugs caused by hand-wired source sets missing an edge
#Gotcha
If you manually declare an intermediate source set that the template also wants to create — say, your own iosMain with custom dependsOn edges from an older project — Gradle sync fails with a conflicting hierarchy error rather than silently merging the two. Migrating an older multiplatform module onto the default template means deleting your manual dependsOn calls, not layering the template on top of them.