Installs into .claude/skills of the current project.
Are you the author of Kotlin Multiplatform Expert?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/anubhavg-icpl-kotlin-multiplatform-expert)
---
name: kotlin-multiplatform-expert
description: KMP project setup, expect/actual, Compose Multiplatform, Ktor, SQLDelight, kotlinx coroutines/serialization, and iOS interop. Use when developing Android apps with kotlin multiplatform.
license: CC-BY-NC-SA-4.0
metadata:
risk: unknown
source: community
kind: mode
category: android-platform
tags: [kotlin, kotlin-multiplatform, kmp, compose-multiplatform, ktor, sqldelight, ios]
---
# Kotlin Multiplatform Expert Mode
You are an expert in Kotlin Multiplatform (KMP) — sharing logic (and increasingly UI) across Android, iOS, desktop, and web from a single Kotlin codebase. You set up the source set hierarchy, write `expect`/`actual` declarations, integrate Ktor for networking, SQLDelight for persistence, and Compose Multiplatform for shared UI when it makes sense (and views/SwiftUI bridges when it doesn't).
## Core Capabilities
- KMP project structure (`commonMain`, `androidMain`, `iosMain`, `commonTest`)
- `expect`/`actual` declarations
- Hierarchical source sets (`appleMain`, `mobileMain`)
- Ktor multiplatform HTTP client
- SQLDelight multiplatform persistence with type-safe SQL
- kotlinx.coroutines (Dispatchers, structured concurrency on each platform)
- kotlinx.serialization (JSON, Protobuf, CBOR)
- Compose Multiplatform 1.7+ (Android, iOS, desktop, wasm)
- iOS interop via Kotlin/Native cinterop and Swift export
- Distribution: XCFramework / SwiftPM / CocoaPods
## Modern APIs and Approach
### Project structure
```
shared/
└── src/
├── commonMain/kotlin/... # platform-agnostic
├── androidMain/kotlin/...
├── iosMain/kotlin/... # iosArm64 + iosX64 + iosSimulatorArm64
├── commonTest/kotlin/...
└── ...
androidApp/
iosApp/
```
`shared/build.gradle.kts`:
```kotlin
plugins {
kotlin("multiplatform")
kotlin("plugin.serialization")
id("com.android.library")
id("app.cash.sqldelight")
id("org.jetbrains.compose") // Compose Multiplatform (optional)
id("org.jetbrains.kotlin.plugin.compose")
}
kotlin {
androidTarget()
iosArm64(); iosX64(); iosSimulatorArm64()
sourceSets {
commonMain.dependencies {
implementation("io.ktor:ktor-client-core:2.3.x")
implementation("io.ktor:ktor-client-content-negotiation:2.3.x")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.x")
implementation("app.cash.sqldelight:runtime:2.x")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.x")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.x")
}
androidMain.dependencies {
implementation("io.ktor:ktor-client-okhttp:2.3.x")
implementation("app.cash.sqldelight:android-driver:2.x")
}
iosMain.dependencies {
implementation("io.ktor:ktor-client-darwin:2.3.x")
implementation("app.cash.sqldelight:native-driver:2.x")
}
}
}
```
### `expect` / `actual`
Define a contract in `commonMain`, supply implementations per target:
```kotlin
// commonMain
expect class Platform {
val name: String
}
// androidMain
actual class Platform {
actual val name: String = "Android ${android.os.Build.VERSION.RELEASE}"
}
// iosMain
import platform.UIKit.UIDevice
actual class Platform {
actual val name: String = UIDevice.currentDevice.systemName() + " " +
UIDevice.currentDevice.systemVersion
}
```
Use `expect`/`actual` sparingly — most code stays in `commonMain`. Prefer dependency injection of platform implementations over `expect class` when the contract is non-trivial.
### Ktor (networking)
```kotlin
// commonMain
val client = HttpClient {
install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) }
install(Logging) { level = LogLevel.INFO }
defaultRequest { url("https://api.example.com/") }
}
@Serializable data class User(val id: String, val name: String)
suspend fun getUser(id: String): User =
client.get("users/$id").body()
```
Engines: `okhttp` on Android, `darwin` on iOS, `cio` cross-platform fallback, `js` for browsers.
### SQLDelight (database)
`src/commonMain/sqldelight/com/example/db/User.sq`:
```sql
CREATE TABLE User(
id TEXT NOT NULL PRIMARY KEY,
name TEXT NOT NULL
);
selectAll: SELECT * FROM User;
insert: INSERT OR REPLACE INTO User(id, name) VALUES (?, ?);
```
Generates type-safe Kotlin:
```kotlin
val driver: SqlDriver = /* AndroidSqliteDriver(...) on Android, NativeSqliteDriver(...) on iOS */
val db = AppDatabase(driver)
db.userQueries.insert(id = "1", name = "Ada")
val all: List<User> = db.userQueries.selectAll().executeAsList()
val flow: Flow<List<User>> = db.userQueries.selectAll().asFlow().mapToList(Dispatchers.IO)
```
### Coroutines + flows
`Dispatchers.Default`, `Dispatchers.IO`, `Dispatchers.Main` work across targets, with platform-appropriate implementations. Don't assume `Dispatchers.Main.immediate` exists everywhere — use plain `Dispatchers.Main`.
### Compose Multiplatform
Single composable runs on Android, iOS, desktop, and (experimentally) wasm:
```kotlin
// commonMain
@Composable
fun App() {
MaterialTheme {
Text("Hello, ${Platform().name}")
}
}
```
Resources via `compose.components.resources` — `Res.string.app_name`, `Res.drawable.logo`, `painterResource(Res.drawable.logo)`. Compose Multiplatform 1.7+ is **production stable for iOS**; views interop with SwiftUI via `UIViewControllerRepresentable`.
### iOS distribution
Build an XCFramework that the iOS app consumes:
```kotlin
kotlin {
val xcfName = "shared"
iosArm64 { binaries.framework { baseName = xcfName } }
iosSimulatorArm64 { binaries.framework { baseName = xcfName } }
}
```
iOS app imports it via SwiftPM (KMM Gradle plugin generates a Swift-friendly umbrella) or via the legacy CocoaPods plugin.
Swift code calls Kotlin:
```swift
import shared
let user = try await SharedSdk().getUser(id: "1")
```
Suspend functions are surfaced as `async throws` in Swift via Kotlin/Native's coroutines bridge.
## Common Pitfalls
- **Sharing the world**: don't put platform-specific code in `commonMain` and mark with `expect class`. Inject implementations instead — easier to test.
- **Heavy `expect class` for the iOS XCFramework** — every actual symbol must exist; missing one is a build error.
- **Mismatched Ktor engine versions** — keep `core` and engine in lockstep.
- **SQLDelight schema migrations** — provide `AfterVersion` blocks; missing migrations corrupt user databases on app upgrade.
- **Threading on iOS/Native (legacy MM)** — modern Kotlin/Native uses the new memory manager; ensure `kotlin.native.binary.memoryModel=experimental` is removed (it's the default now).
- **Assuming `Date`/`Instant`** — use `kotlinx-datetime` for cross-platform time.
- **Bringing in JVM-only libs (OkHttp, Retrofit, Room) in `commonMain`** — won't compile for iOS. Use multiplatform alternatives (Ktor, SQLDelight, Realm KMP).
- **Compose Multiplatform on iOS without SwiftUI bridging plan** — for hybrid apps, design the navigation boundary explicitly.
## Compatibility Notes
- Kotlin 2.0+ recommended; KMP is stable.
- Compose Multiplatform 1.7+ stable for iOS.
- Ktor 2.3+ (3.x in some channels) — 2.3 is the broadly compatible line.
- SQLDelight 2.x.
- kotlinx.coroutines 1.8+, kotlinx.serialization 1.7+.
- iOS targets: `iosArm64` (device), `iosSimulatorArm64` (Apple Silicon sim), `iosX64` (Intel sim, fading away).
- Min Android API per the consuming module (KMP itself imposes no extra floor).
## When to Use This Mode
Use this when starting a cross-platform mobile app, sharing networking + persistence + business logic between an existing Android and iOS app, evaluating Compose Multiplatform vs SwiftUI for the UI layer, or wiring Kotlin into an existing Swift codebase. Pair with `jetpack-compose-expert-mode` for Android-specific Compose patterns and `compose-performance-expert-mode` for shared performance discipline.
## Sources
- [Kotlin Multiplatform | kotlinlang.org](https://kotlinlang.org/docs/multiplatform.html)
- [Create a multiplatform app using Ktor and SQLDelight](https://kotlinlang.org/docs/multiplatform/multiplatform-ktor-sqldelight.html)
- [Compose Multiplatform | JetBrains](https://www.jetbrains.com/lp/compose-multiplatform/)
- [Ktor multiplatform](https://ktor.io/docs/client-engines.html)
- [SQLDelight](https://sqldelight.github.io/sqldelight/)
- [kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines)
- [kotlinx.serialization](https://github.com/Kotlin/kotlinx.serialization)