---
name: kotlin-multiplatform-mobile
description: "Share business logic, networking, and storage across iOS + Android with KMM. Native UI on each side. Sets up KMM modules: shared Kotlin code, expect/actual for platform APIs, Ktor + SQLDelight, and the Xcode integration."
license: MIT
---

# Kotlin Multiplatform Mobile (KMM)

Share business logic, networking, and persistence between iOS and Android from a single Kotlin codebase, while keeping native UI on each platform (SwiftUI/UIKit on iOS, Jetpack Compose/Views on Android). This skill scaffolds a `shared` Kotlin module, wires up `expect`/`actual` declarations for platform-specific APIs, and sets up Ktor for HTTP and SQLDelight for typed local storage — including the Xcode framework integration so your iOS app can call shared code as a normal Swift module.

## When to use it

- You have (or want) separate native apps for iOS and Android but keep rewriting the same models, API clients, validation, and caching twice.
- You want a shared domain/data layer but refuse to compromise on platform-native UI.
- You're adding KMM to an existing app incrementally — sharing one feature (say, the networking layer) rather than rewriting everything.
- You need a typed SQLite layer and a multiplatform HTTP client that work identically on both targets.

## How it works

The skill generates a Gradle module applying the `org.jetbrains.kotlin.multiplatform` plugin with `androidTarget()` and the iOS targets (`iosArm64`, `iosSimulatorArm64`, `iosX64`). Source sets follow the standard `commonMain`, `androidMain`, `iosMain` layout. Platform differences are bridged with `expect`/`actual` — e.g. an `expect class Platform` resolved per target. Networking uses Ktor's `HttpClient` with the OkHttp engine on Android and the Darwin engine on iOS, plus `ContentNegotiation` and `kotlinx.serialization`. Persistence uses SQLDelight: `.sq` files generate type-safe queries, with `AndroidSqliteDriver` and `NativeSqliteDriver` supplied through an `expect fun` driver factory. For iOS consumption it configures an XCFramework (or CocoaPods via the `cocoapods` block) so Xcode imports the compiled `shared` framework.

## Usage

```kotlin
// shared/build.gradle.kts
kotlin {
    androidTarget()
    listOf(iosArm64(), iosSimulatorArm64()).forEach {
        it.binaries.framework { baseName = "Shared" }
    }
    sourceSets {
        commonMain.dependencies {
            implementation("io.ktor:ktor-client-core:2.3.12")
            implementation("io.ktor:ktor-client-content-negotiation:2.3.12")
            implementation("app.cash.sqldelight:runtime:2.0.2")
        }
    }
}
```

```kotlin
// commonMain — expect/actual driver
expect class DriverFactory { fun create(): SqlDriver }
```

```swift
// iOS — call shared code from Swift
import Shared
let repo = UserRepository(driverFactory: DriverFactory())
```

## Tips & gotchas

- iOS exposes Kotlin via Objective-C interop, so `suspend` functions become completion handlers — wrap them with `async`/`await` in Swift or use a library like KMP-NativeCoroutines for clean Flow/coroutine bridging.
- Run `./gradlew :shared:embedAndSignAppleFrameworkForXcode` (or use the CocoaPods integration) as an Xcode build phase; stale frameworks are a common "symbol not found" cause.
- Keep platform dependencies out of `commonMain` — anything Android- or Darwin-specific belongs behind `expect`/`actual` or in the platform source set.
- Apple Silicon simulators need `iosSimulatorArm64`; omitting it breaks simulator builds on M-series Macs.

## FAQ

**Does this share the UI too?** No — UI stays native (SwiftUI/Compose). KMM shares logic, networking, and storage only.

**Can I add it to an existing project?** Yes. Drop in the `shared` module, depend on it from the Android app, and integrate the framework into Xcode incrementally.

**Why SQLDelight over Room?** Room isn't multiplatform; SQLDelight generates typed queries from `.sq` files and ships drivers for both Android and native iOS.
