Bryn Flow

🎯 Kotlin Multiplatform (KMP) Cheat Sheet

The syntax and setup you look up over and over. Bookmark this page.

← KMP Track

Source Set Layout

shared/src/
  commonMain/kotlin/    // shared code, all targets
  androidMain/kotlin/   // Android-only implementations
  iosMain/kotlin/       // iOS-only implementations
  commonTest/kotlin/    // tests run on every target

// build.gradle.kts (shared module)
kotlin {
    androidTarget()
    iosX64(); iosArm64(); iosSimulatorArm64()
    sourceSets {
        commonMain.dependencies {
            implementation("io.ktor:ktor-client-core:2.3.x")
        }
    }
}

expect / actual

// commonMain
expect class PlatformStorage {
    fun save(key: String, value: String)
    fun read(key: String): String?
}

// androidMain
actual class PlatformStorage(private val ctx: Context) {
    actual fun save(key: String, value: String) { /* SharedPreferences */ }
    actual fun read(key: String): String? { /* ... */ }
}

// iosMain
actual class PlatformStorage {
    actual fun save(key: String, value: String) { /* NSUserDefaults */ }
    actual fun read(key: String): String? { /* ... */ }
}

Ktor Client

val client = HttpClient {
    install(ContentNegotiation) { json() }
}

suspend fun getUsers(): List<User> =
    client.get("https://api.example.com/users").body()

suspend fun createUser(u: User): User =
    client.post("https://api.example.com/users") {
        contentType(ContentType.Application.Json)
        setBody(u)
    }.body()

SQLDelight

-- Task.sq
CREATE TABLE Task (
  id INTEGER NOT NULL PRIMARY KEY,
  title TEXT NOT NULL,
  done INTEGER NOT NULL DEFAULT 0
);

selectAll:
SELECT * FROM Task;

insertTask:
INSERT INTO Task(id, title, done) VALUES (?, ?, ?);

// Kotlin usage (generated API)
val tasks = database.taskQueries.selectAll().executeAsList()

Koin

val appModule = module {
    single { HttpClient { install(ContentNegotiation) { json() } } }
    single { UserRepository(get()) }
    factory { UserViewModel(get()) }
}

fun initKoin() {
    startKoin { modules(appModule) }
}

class UserRepository(private val client: HttpClient)

Compose Multiplatform

@Composable
fun UserList(users: List<User>) {
    LazyColumn {
        items(users) { user ->
            Text(user.name, style = MaterialTheme.typography.bodyLarge)
        }
    }
}
// Same function, same file — renders on Android, iOS, and desktop.

Swift Interop

// Swift, importing the compiled shared framework
import Shared

let repo = UserRepository()

repo.fetchUser(id: 1) { user, error in
    if let user { print(user.name) }
}

// Kotlin sealed class → Swift enum-like pattern match
switch onEnum(of: result) {
case .ok(let ok): print(ok.value)
case .err(let err): print(err.message)
}