---
id: mobile-kmp-deep
title: Kotlin Multiplatform — shared business logic
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [mobile, kmp, kotlin, vibe-coding]
tech_stack: { language: "Kotlin", applicable_to: ["iOS", "Android"] }
applied_in: []
aliases: [KMP, Kotlin Multiplatform, KMM, Compose Multiplatform, expect/actual, shared module]
---

# Kotlin Multiplatform Deep

> Kotlin 가 iOS + Android + Web + Desktop 동시. **Shared business logic + native UI**. KMM 가 mobile, Compose Multiplatform 가 UI 도. JetBrains official.

## 📖 핵심 개념
- Common code: Kotlin (shared).
- Platform-specific: expect / actual.
- iOS = Kotlin → Native (LLVM).
- Android = Kotlin → JVM (default).

## 💻 코드 패턴

### Project structure
```
my-app/
├── shared/                # Shared module
│   ├── commonMain/        # Common Kotlin
│   ├── androidMain/       # Android-only
│   ├── iosMain/           # iOS-only
│   └── jsMain/            # Web (optional)
├── androidApp/            # Android app
├── iosApp/                # iOS Xcode project
└── build.gradle.kts
```

### shared/build.gradle.kts
```kotlin
kotlin {
    androidTarget()
    iosX64()
    iosArm64()
    iosSimulatorArm64()
    
    cocoapods {
        summary = "Shared module"
        homepage = "..."
        ios.deploymentTarget = "16.0"
        framework {
            baseName = "shared"
            isStatic = true
        }
    }
    
    sourceSets {
        commonMain.dependencies {
            implementation("io.ktor:ktor-client-core:2.3.7")
            implementation("io.ktor:ktor-client-content-negotiation:2.3.7")
            implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
        }
        androidMain.dependencies {
            implementation("io.ktor:ktor-client-okhttp:2.3.7")
        }
        iosMain.dependencies {
            implementation("io.ktor:ktor-client-darwin:2.3.7")
        }
    }
}
```

### Shared model
```kotlin
// commonMain/User.kt
@Serializable
data class User(
    val id: String,
    val name: String,
    val email: String,
)
```

### Shared API client
```kotlin
// commonMain/UserRepository.kt
class UserRepository(private val client: HttpClient) {
    suspend fun fetchUser(id: String): User {
        return client.get("https://api.example.com/users/$id").body()
    }
}
```

### expect / actual
```kotlin
// commonMain/Platform.kt
expect fun platform(): String

// androidMain/Platform.kt
actual fun platform(): String = "Android ${android.os.Build.VERSION.SDK_INT}"

// iosMain/Platform.kt
import platform.UIKit.UIDevice
actual fun platform(): String = "iOS ${UIDevice.currentDevice.systemVersion}"
```

→ Common 가 abstract, platform 가 concrete.

### Network (Ktor)
```kotlin
// commonMain
val client = HttpClient {
    install(ContentNegotiation) {
        json(Json { ignoreUnknownKeys = true })
    }
    install(Logging) { level = LogLevel.HEADERS }
}

suspend fun fetchUsers(): List<User> {
    return client.get("https://...").body()
}
```

→ HTTP / JSON / serialization 가 common.

### DB (SQLDelight)
```kotlin
// shared/build.gradle.kts
plugins {
    id("app.cash.sqldelight") version "2.0.0"
}

sqldelight {
    databases {
        create("AppDb") {
            packageName.set("com.example.shared.db")
        }
    }
}
```

```sql
-- shared/src/commonMain/sqldelight/com/example/User.sq
CREATE TABLE User (
    id TEXT PRIMARY KEY,
    name TEXT NOT NULL
);

selectAll:
SELECT * FROM User;

insert:
INSERT OR REPLACE INTO User VALUES (?, ?);
```

```kotlin
// commonMain
val driver: SqlDriver = ...  // platform 별
val db = AppDb(driver)
db.userQueries.insert("1", "Alice")
val users = db.userQueries.selectAll().executeAsList()
```

→ Type-safe SQL. iOS / Android 둘 다.

### Coroutines + Flow
```kotlin
// commonMain
class UserViewModel(private val repo: UserRepository) {
    val users: Flow<List<User>> = flow {
        emit(repo.fetchUsers())
    }
}
```

→ Flow 가 iOS 에서 도 사용 (KMP-NativeCoroutines).

### iOS 사용 (Swift)
```swift
// iosApp
import shared

class UserListVM: ObservableObject {
    @Published var users: [User] = []
    private let repo = UserRepository(client: ...)
    
    func load() async {
        do {
            let result = try await repo.fetchUsers()
            await MainActor.run { self.users = result }
        } catch {
            print(error)
        }
    }
}
```

→ Kotlin class 가 Swift 에서 native 처럼.

### Async on iOS (KMP-NativeCoroutines)
```kotlin
// commonMain
@NativeCoroutines
suspend fun fetchUsers(): List<User> = ...
```

```swift
// iOS
import KMPNativeCoroutinesAsync
let users = try await asyncFunction(for: repo.fetchUsersNative())
```

→ Suspend → Swift async/await.

### Compose Multiplatform (UI도)
```kotlin
// commonMain (Compose UI)
@Composable
fun UserCard(user: User) {
    Card {
        Column(Modifier.padding(16.dp)) {
            Text(user.name, style = MaterialTheme.typography.h6)
            Text(user.email)
        }
    }
}
```

→ iOS + Android 가 같은 UI.
→ iOS Compose 가 stable (2024+).

### vs SwiftUI / Jetpack Compose only
```
KMP shared logic + native UI:
- Native 친화 (각 platform UI)
- 작은 cost.

Compose Multiplatform:
- Single UI codebase
- iOS 도 Compose
- "iOS 답지" 의 약간

→ Logic 만 = KMP.
UI 도 = Compose Multiplatform.
```

### vs Flutter / React Native
```
Flutter / RN:
- Single 언어 (Dart / JS)
- Single UI codebase
- Native bridge

KMP:
- Native 언어 (Kotlin + Swift)
- Native UI (보통)
- Logic 만 share

→ KMP 가 native-friendly.
RN/Flutter 가 빠른 dev.
```

### Pros
```
- Native iOS UX.
- Logic share = bug 1번 fix.
- Type-safe (Kotlin).
- Coroutines (async).
- Native interop.
```

### Cons
```
- iOS dev 가 Kotlin 이해.
- Library 가 작음 (vs RN).
- Build time (LLVM iOS = 느린).
- Tooling 가 RN/Flutter 보다 약함.
```

### Real-world
- **Cash App** (Square): 큰 KMP user.
- **Netflix**: 일부 mobile.
- **Philips**: hue lighting.
- **VMware**: Workspace ONE.
- **Touchlab**: KMM consultancy.

### LiveData / StateFlow → Swift
```kotlin
// commonMain
class ViewModel {
    val state: StateFlow<UserState> = ...
}
```

```swift
// iOS
ViewModel().state.collect { state in
    // ...
}
```

→ StateFlow 가 Combine publisher 처럼.

### Image loading (Coil 3 multiplatform)
```kotlin
// Compose Multiplatform
AsyncImage(
    model = "https://...",
    contentDescription = null,
)
```

### Settings (Multiplatform)
```kotlin
// com.russhwolf:multiplatform-settings
val settings: Settings = ...
settings.putString("theme", "dark")
val theme = settings.getString("theme", "light")
```

### Test
```kotlin
// commonTest
class UserRepoTest {
    @Test
    fun fetchesUser() = runTest {
        val user = repo.fetchUser("1")
        assertEquals("Alice", user.name)
    }
}
```

→ Common test 가 모든 platform.

### Migration to KMP
```
1. Existing Android app.
2. shared module 추가.
3. 1 logic (e.g. API client) 이동.
4. iOS 가 framework 사용.
5. 점진 — 1 module 씩.
```

→ Strangler fig 식.

### Build
```bash
# Gradle (Android)
./gradlew :androidApp:installDebug

# iOS
cd iosApp && pod install
xed iosApp.xcworkspace
# Xcode build & run.

# Release framework
./gradlew :shared:linkReleaseFrameworkIosArm64
```

### Future
```
- Compose Multiplatform iOS 가 stable + 인기 ↑.
- KMP 의 toolchain 빠름.
- More libraries multiplatform.
- "Kotlin everywhere".
```

## 🤔 의사결정 기준
| 상황 | 추천 |
|---|---|
| Native UX 중요 + share logic | KMP (Kotlin shared, Swift/Kotlin UI) |
| Single UI codebase | Compose Multiplatform |
| 빠른 prototype | RN / Flutter |
| 큰 회사 + Android 강 | KMP |
| iOS dev 가 Swift 만 | KMP shared logic only |
| Web 도 share | KMP / RN Web |
| Performance critical | Native or KMP |

## ❌ 안티패턴
- **모든 거 share (UI 까지)**: native UX 잃음.
- **Platform-specific 가 common**: leak.
- **Coroutines + Swift 직접**: KMP-NativeCoroutines 사용.
- **iOS framework 가 큰**: bundle 폭발 — minimize.
- **Only 1 platform test**: 다른 platform 깨짐.
- **Big bang migration**: gradual.

## 🤖 LLM 활용 힌트
- expect / actual 가 KMP 의 핵심.
- Ktor / SQLDelight / kotlinx 가 multiplatform-ready.
- Compose Multiplatform = UI 까지 share.
- KMP-NativeCoroutines 가 Swift async/await.

## 🔗 관련 문서
- [[Mobile_KMP_Compose]]
- [[Mobile_Flutter_Patterns]]
- [[Android_Kotlin_Coroutines_Scopes]]
