[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -0,0 +1,398 @@
|
||||
---
|
||||
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]]
|
||||
Reference in New Issue
Block a user