| name | kmp-gradle |
| description | Gradle Kotlin DSL patterns for KMP — version catalogs, hierarchical source sets, convention plugins, assembling XCFrameworks and fat frameworks. Use when wiring or refactoring a KMP build. |
KMP Gradle
Instructions
KMP builds should use the Kotlin DSL, version catalogs, and convention plugins in build-logic/. Hand-edited apply plugin: blocks and hard-coded versions do not scale.
1. Version catalog — gradle/libs.versions.toml
[versions]
kotlin = "2.0.21"
agp = "8.5.2"
compose = "1.7.0"
ktor = "2.3.12"
sqldelight = "2.0.2"
coroutines = "1.9.0"
serialization = "1.7.3"
datetime = "0.6.1"
koin = "4.0.0"
[plugins]
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
kotlinSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
composeMultiplatform= { id = "org.jetbrains.compose", version.ref = "compose" }
androidLibrary = { id = "com.android.library", version.ref = "agp" }
androidApplication = { id = "com.android.application", version.ref = "agp" }
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" }
[libraries]
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization" }
kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "datetime" }
koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }
turbine = { module = "app.cash.turbine:turbine", version = "1.1.0" }
2. Convention plugin
build-logic/
├── settings.gradle.kts
└── convention/
├── build.gradle.kts
└── src/main/kotlin/
└── kmp-library.gradle.kts
plugins {
id("org.jetbrains.kotlin.multiplatform")
id("com.android.library")
}
kotlin {
androidTarget {
compilerOptions.jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
}
jvm()
listOf(iosArm64(), iosSimulatorArm64(), iosX64()).forEach {
it.binaries.framework { baseName = project.name; isStatic = true }
}
applyDefaultHierarchyTemplate()
sourceSets.commonTest.dependencies { implementation(kotlin("test")) }
}
android {
namespace = "com.example.${project.name}"
compileSdk = 35
defaultConfig.minSdk = 24
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
}
Feature modules become:
plugins { id("kmp-library") }
kotlin.sourceSets.commonMain.dependencies {
implementation(projects.domain)
}
3. Hierarchical source sets
Kotlin 1.9.20+ provides the default hierarchy — stop wiring dependsOn manually. Only customize when genuinely needed:
kotlin {
applyDefaultHierarchyTemplate {
common {
group("mobile") {
withAndroidTarget()
withIos()
}
group("jvmLike") {
withJvm()
withAndroidTarget()
}
}
}
}
4. XCFramework assembly
import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XCFramework
val xcf = XCFramework("Shared")
kotlin {
listOf(iosArm64(), iosSimulatorArm64(), iosX64()).forEach { target ->
target.binaries.framework {
baseName = "Shared"
isStatic = true
xcf.add(this)
export(projects.featureAuth)
export(projects.featureFeed)
}
}
}
Run:
./gradlew :shared:assembleSharedReleaseXCFramework
Output: shared/build/XCFrameworks/release/Shared.xcframework.
5. Fat framework (legacy, only for CocoaPods pre-XCFramework)
tasks.register<FatFrameworkTask>("fatFrameworkDebug") {
baseName = "Shared"
from(
kotlin.targets.getByName<KotlinNativeTarget>("iosArm64").binaries.getFramework("DEBUG"),
kotlin.targets.getByName<KotlinNativeTarget>("iosSimulatorArm64").binaries.getFramework("DEBUG"),
)
destinationDir = layout.buildDirectory.dir("fat-framework/debug").get().asFile
}
Prefer XCFrameworks for new projects.
6. gradle.properties essentials
org.gradle.jvmargs=-Xmx4g -XX:+UseParallelGC -Dfile.encoding=UTF-8
org.gradle.caching=true
org.gradle.parallel=true
org.gradle.configuration-cache=true
kotlin.code.style=official
kotlin.mpp.stability.nowarn=true
kotlin.mpp.enableCInteropCommonization=true
kotlin.native.cacheKind=none # for CI; enable locally for faster dev
kotlin.native.ignoreDisabledTargets=true
7. Useful tasks
./gradlew :shared:build # everything for the local JVM + Android
./gradlew :shared:allTests # test every target
./gradlew :shared:podPublishXCFramework # if using kotlin cocoapods plugin
./gradlew :shared:linkReleaseFrameworkIosArm64 # just iOS device
Checklist