0Pricing
Kotlin Academy · Lesson

KMP Project Structure: commonMain, androidMain, iosMain

Understand the source set hierarchy and how code is shared across targets.

KMP Project Structure: commonMain, androidMain, iosMain is a free Kotlin Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Kotlin Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

What Is KMP?

Kotlin Multiplatform (KMP) lets you write shared Kotlin code that compiles to JVM, Android, iOS (via Kotlin/Native), and JS. Platform-specific code lives in separate source sets.

// Typical KMP module structure:
// shared/
//   src/
//     commonMain/kotlin/   <- shared code
//     androidMain/kotlin/  <- Android-only
//     iosMain/kotlin/      <- iOS-only
//     commonTest/kotlin/   <- shared tests
fun main() { println("KMP: one codebase, many targets") }

Source Set Hierarchy

Source sets form a hierarchy. commonMain is the root; androidMain and iosMain depend on it. Code in commonMain is visible to all targets.

// build.gradle.kts (Kotlin DSL)
kotlin {
    androidTarget()
    iosX64()
    iosArm64()
    iosSimulatorArm64()

    sourceSets {
        val commonMain by getting {
            dependencies { /* shared deps */ }
        }
        val androidMain by getting
        val iosMain by getting
    }
}

commonMain: Shared Logic

Put business logic, domain models, repositories, and use cases in commonMain. This code compiles for all targets.

// commonMain/kotlin/com/example/domain/User.kt
data class User(val id: String, val name: String, val email: String)

// commonMain/kotlin/com/example/repository/UserRepository.kt
interface UserRepository {
    suspend fun getUser(id: String): User?
    suspend fun saveUser(user: User)
}

androidMain: Android-Specific

androidMain can use Android SDK classes. Typical uses: Android-specific implementations of expect/actual declarations.

// androidMain/kotlin/com/example/platform/PlatformLogger.kt
import android.util.Log
class AndroidLogger : Logger {
    override fun log(msg: String) = Log.d("App", msg)
}
// androidMain/kotlin/com/example/di/PlatformModule.kt
// Koin or Hilt module that binds AndroidLogger to Logger

iosMain: iOS-Specific

iosMain has access to Apple frameworks via Kotlin/Native interop. Use it for iOS implementations of expect/actual APIs.

// iosMain/kotlin/com/example/platform/PlatformLogger.kt
import platform.Foundation.NSLog
class IosLogger : Logger {
    override fun log(msg: String) = NSLog(msg)
}
// iOS uses Kotlin/Native runtime; no JVM here

Gradle Configuration for iOS Targets

For iOS, you typically declare three targets: x64 (simulator on Intel), arm64 (device), and simulatorArm64 (simulator on Apple Silicon).

// build.gradle.kts
kotlin {
    iosX64()
    iosArm64()
    iosSimulatorArm64()
    sourceSets {
        val iosMain by creating {
            dependsOn(commonMain.get())
        }
        val iosX64Main by getting { dependsOn(iosMain) }
        val iosArm64Main by getting { dependsOn(iosMain) }
        val iosSimulatorArm64Main by getting { dependsOn(iosMain) }
    }
}

Shared Dependencies in commonMain

Add shared dependencies (Ktor, SQLDelight, kotlinx.serialization, Koin) to commonMain. Platform-specific drivers go in platform source sets.

// commonMain dependencies example:
// implementation("io.ktor:ktor-client-core:2.3.0")
// implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
// implementation("io.insert-koin:koin-core:3.4.0")

// androidMain:
// implementation("io.ktor:ktor-client-okhttp:2.3.0")
// iosMain:
// implementation("io.ktor:ktor-client-darwin:2.3.0")

commonTest: Shared Tests

Tests in commonTest run on all targets. Use Kotlin test library and write target-agnostic unit tests for shared business logic.

// commonTest/kotlin/UserRepositoryTest.kt
import kotlin.test.Test
import kotlin.test.assertEquals
class UserRepositoryTest {
    @Test
    fun testUserCreation() {
        val user = User("1", "Alice", "alice@example.com")
        assertEquals("Alice", user.name)
    }
}

Intermediate Source Sets

You can create intermediate source sets shared between a subset of targets, e.g., nativeMain for all native targets, or appleMain for iOS + macOS.

// Create appleMain shared between iOS and macOS:
sourceSets {
    val appleMain by creating { dependsOn(commonMain.get()) }
    val iosMain by getting { dependsOn(appleMain) }
    val macosMain by getting { dependsOn(appleMain) }
}

Project File Layout

A typical KMP shared module directory tree that separates concerns clearly.

// shared/
//   build.gradle.kts
//   src/
//     commonMain/kotlin/
//       domain/           User.kt, Post.kt
//       repository/       UserRepository.kt
//       usecase/          GetUserUseCase.kt
//     androidMain/kotlin/
//       repository/       RoomUserRepository.kt
//     iosMain/kotlin/
//       repository/       CoreDataUserRepository.kt
//     commonTest/kotlin/  GetUserUseCaseTest.kt

CocoaPods / SPM Integration

The KMP Gradle plugin generates an XCFramework (or CocoaPods pod) that you add to your Xcode project, exposing the shared Kotlin code as a native framework.

// build.gradle.kts for CocoaPods:
kotlin {
    cocoapods {
        name = "Shared"
        version = "1.0"
        ios.deploymentTarget = "14.0"
        framework { baseName = "Shared" }
    }
}
// Run: ./gradlew :shared:podPublishReleaseXCFramework

Quick Check

Where should shared business logic (domain models, use cases) be placed in a KMP project?

Recap

commonMain holds shared logic (domain, repositories, use cases). androidMain/iosMain hold platform implementations. commonTest runs tests on all targets. Dependencies are split: shared APIs in commonMain, platform drivers in platform source sets.

Frequently asked questions

Is the “KMP Project Structure: commonMain, androidMain, iosMain” lesson free?

Yes — the full text of “KMP Project Structure: commonMain, androidMain, iosMain” is free to read here on the web, and the Kotlin Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Kotlin Academy course, upgrade to CoddyKit PRO.

What will I learn in “KMP Project Structure: commonMain, androidMain, iosMain”?

Understand the source set hierarchy and how code is shared across targets. You practise Kotlin Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Kotlin Academy?

No prior experience is required. Kotlin Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “KMP Project Structure: commonMain, androidMain, iosMain” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Kotlin Academy lesson?

Yes. Every Kotlin Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. KMP Project Structure: commonMain, androidMain, iosMain
  2. expect/actual Mechanism for Platform APIs
  3. Sharing Repository and Use Case Layers
  4. Dependency Injection in KMP with Koin
← Back to Kotlin Academy