π Migrating an Android App Module to Kotlin Multiplatform: Full Technical Guide
βΈ»
π 1. Objective
Transform a standard Android application module into a Kotlin Multiplatform module, while: β’ Preserving Android application functionality β’ Preparing clean future expansion for other platforms (e.g., iOS, Desktop) β’ Aligning to Kotlin 1.9+ and Android SourceSet Layout V2
βΈ»
π 2. Initial Module Setup
A typical Android-only app module looks like:
sampleapp/ βββ src/ βββ main/ β βββ java/ β βββ res/ β βββ AndroidManifest.xml βββ androidTest/ βββ test/
with build.gradle.kts based on com.android.application plugin.
βΈ»
π§© 3. Apply Plugins
Replace the plugins block in build.gradle.kts:
plugins { kotlin(“multiplatform”) id(“com.android.application”) }
βΈ»
βοΈ 4. Define Multiplatform Targets
Inside kotlin {}, configure:
kotlin { androidTarget()
iosArm64()
iosSimulatorArm64()
sourceSets {
val commonMain by getting
val commonTest by getting
val androidMain by getting
val androidUnitTest by getting
val androidInstrumentedTest by getting
}
}
Important: β’ androidTarget() is for Android β’ iosArm64(), iosSimulatorArm64() prepare for iOS native later β’ SourceSets renamed for Kotlin 1.9+: androidUnitTest, androidInstrumentedTest
βΈ»
## π₯ 5. Restructure Dependencies
Inside each source set, use only implementation():
sourceSets { val commonMain by getting { dependencies { // Common/shared dependencies } } val commonTest by getting { dependencies { implementation(kotlin(“test”)) } } val androidMain by getting { dependencies { implementation(libs.androidx.core.ktx) implementation(libs.androidx.appcompat) implementation(libs.material) implementation(libs.androidx.constraintlayout) implementation(libs.androidx.navigation.fragment.ktx) implementation(libs.androidx.navigation.ui.ktx) } } val androidUnitTest by getting { dependencies { implementation(libs.junit) } } val androidInstrumentedTest by getting { dependencies { implementation(libs.androidx.junit) implementation(libs.androidx.espresso.core) } } }
π« Do not use testImplementation inside sourceSets{} (wrong in KMP).
βΈ»
π 6. Refactor Source Folder Layout
Move and rename folders to match KMP expectations:
Create androidMain structure
mkdir -p src/androidMain/kotlin mkdir -p src/androidMain/res
Move app code
git mv src/main/java/* src/androidMain/kotlin/ git mv src/main/res/* src/androidMain/res/ git mv src/main/AndroidManifest.xml src/androidMain/AndroidManifest.xml
Move unit tests
git mv src/test src/androidUnitTest
Move instrumentation tests
git mv src/androidTest src/androidInstrumentedTest
Create shared code skeletons
mkdir -p src/commonMain/kotlin mkdir -p src/commonTest/kotlin
βΈ»
π 7. Update gradle.properties
Explicitly opt-in for Android SourceSet Layout V2:
kotlin.android.sourceSetLayoutVersion=2
(Prevents future Gradle warnings.)
βΈ»
π 8. Android Block
Keep your android {} configuration as it was, fully preserved:
android { namespace = “your.package.name” compileSdk = 35
defaultConfig {
applicationId = "your.package.name"
minSdk = 24
targetSdk = 35
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
buildFeatures {
viewBinding = true
}
}
βΈ»
π 9. Common Pitfalls & Errors
Error Cause Fix Unresolved reference: androidTarget() Missing/incorrect KMP plugin Ensure kotlin(“multiplatform”) applied Unresolved reference: testImplementation inside sourceSets Wrong usage inside KMP Replace with implementation() KotlinSourceSet with name ‘androidTest’ not found Kotlin 1.9+ layout change Rename androidTest β androidUnitTest Gradle sync fails due to missing folders Incorrect move commands Create required folders manually Android app not runnable after migration Misplaced Manifest or missing androidMain setup Check src/androidMain/AndroidManifest.xml
βΈ»
β 10. Final Folder Structure
sampleapp/ βββ src/ βββ androidMain/ β βββ kotlin/ β βββ res/ β βββ AndroidManifest.xml βββ androidUnitTest/ β βββ kotlin/ βββ androidInstrumentedTest/ β βββ kotlin/ βββ commonMain/ β βββ kotlin/ βββ commonTest/ βββ kotlin/
βΈ»
π 11. Result
β’ The Android app remains fully runnable (no loss of execution).
β’ Future platform expansions (iOS, Desktop) now only require adding code under commonMain/.
β’ Android-specific code isolated under androidMain/.
βΈ»
π― Conclusion
Migrating from Android-only to Kotlin Multiplatform is highly structured and fully achievable without breaking your app, provided you: β’ Update plugins and sourceSets correctly β’ Move folders following KMP conventions β’ Adjust dependencies inside the right sourceSet
β Migration lays a robust foundation for truly multi-platform mobile development.
βΈ»
βοΈ Bonus Tip (Optional Next Steps)
β’ Set up CocoaPods plugin (id("org.jetbrains.kotlin.native.cocoapods")) if you plan to integrate iOS directly.
β’ Add Desktop target with jvm() if you want a Desktop client.
βΈ»