kotlin-multiplatform
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseKotlin Multiplatform Skill
Kotlin Multiplatform 开发技能
Shared business logic and optional shared UI across Android, iOS, desktop, and web.
在Android、iOS、桌面和Web平台间共享业务逻辑,也可选择共享UI。
Project Structure
项目结构
project/
├── composeApp/ # Shared Compose UI (if using CMP)
│ └── src/
│ ├── commonMain/ # Shared UI code
│ ├── androidMain/ # Android-specific UI
│ ├── iosMain/ # iOS-specific UI
│ └── desktopMain/ # Desktop-specific UI
├── shared/ # Shared business logic (KMP)
│ └── src/
│ ├── commonMain/ # Shared code
│ │ └── kotlin/
│ │ ├── data/ # Repositories, data sources
│ │ ├── domain/ # Use cases, models
│ │ └── platform/ # expect declarations
│ ├── androidMain/ # actual implementations
│ ├── iosMain/ # actual implementations
│ └── commonTest/ # Shared tests
├── androidApp/ # Android entry point
├── iosApp/ # iOS entry point (Xcode project)
├── build.gradle.kts
└── settings.gradle.ktsproject/
├── composeApp/ # 共享Compose UI(若使用CMP)
│ └── src/
│ ├── commonMain/ # 共享UI代码
│ ├── androidMain/ # Android特定UI代码
│ ├── iosMain/ # iOS特定UI代码
│ └── desktopMain/ # 桌面端特定UI代码
├── shared/ # 共享业务逻辑(KMP)
│ └── src/
│ ├── commonMain/ # 共享代码
│ │ └── kotlin/
│ │ ├── data/ # 仓库、数据源
│ │ ├── domain/ # 用例、模型
│ │ └── platform/ # expect声明
│ ├── androidMain/ # actual实现
│ ├── iosMain/ # actual实现
│ └── commonTest/ # 共享测试代码
├── androidApp/ # Android入口
├── iosApp/ # iOS入口(Xcode项目)
├── build.gradle.kts
└── settings.gradle.ktsexpect/actual Pattern
expect/actual 模式
kotlin
// commonMain - expect declaration
expect class PlatformContext
expect fun getPlatformName(): String
expect fun createHttpClient(): HttpClient
// androidMain - actual implementation
actual class PlatformContext(val context: android.content.Context)
actual fun getPlatformName(): String = "Android ${Build.VERSION.SDK_INT}"
actual fun createHttpClient(): HttpClient = HttpClient(OkHttp) {
install(ContentNegotiation) { json() }
}
// iosMain - actual implementation
actual class PlatformContext
actual fun getPlatformName(): String = UIDevice.currentDevice.systemName()
actual fun createHttpClient(): HttpClient = HttpClient(Darwin) {
install(ContentNegotiation) { json() }
}kotlin
// commonMain - 期望声明
expect class PlatformContext
expect fun getPlatformName(): String
expect fun createHttpClient(): HttpClient
// androidMain - 实际实现
actual class PlatformContext(val context: android.content.Context)
actual fun getPlatformName(): String = "Android ${Build.VERSION.SDK_INT}"
actual fun createHttpClient(): HttpClient = HttpClient(OkHttp) {
install(ContentNegotiation) { json() }
}
// iosMain - 实际实现
actual class PlatformContext
actual fun getPlatformName(): String = UIDevice.currentDevice.systemName()
actual fun createHttpClient(): HttpClient = HttpClient(Darwin) {
install(ContentNegotiation) { json() }
}Key Libraries
核心库
| Library | Purpose | Multiplatform? |
|---|---|---|
| Ktor | HTTP client | Yes |
| kotlinx.serialization | JSON parsing | Yes |
| kotlinx.coroutines | Async/concurrency | Yes |
| SQLDelight | Local database | Yes |
| Koin | Dependency injection | Yes |
| Compose Multiplatform | Shared UI | Yes |
| kotlinx.datetime | Date/time | Yes |
| Napier | Logging | Yes |
| 库 | 用途 | 支持跨平台? |
|---|---|---|
| Ktor | HTTP客户端 | 是 |
| kotlinx.serialization | JSON解析 | 是 |
| kotlinx.coroutines | 异步/并发处理 | 是 |
| SQLDelight | 本地数据库 | 是 |
| Koin | 依赖注入 | 是 |
| Compose Multiplatform | 共享UI | 是 |
| kotlinx.datetime | 日期/时间处理 | 是 |
| Napier | 日志记录 | 是 |
Networking with Ktor
基于Ktor的网络请求
kotlin
// commonMain
class ApiClient(private val httpClient: HttpClient) {
suspend fun getUsers(): List<User> {
return httpClient.get("https://api.example.com/users").body()
}
suspend fun createUser(input: CreateUserInput): User {
return httpClient.post("https://api.example.com/users") {
contentType(ContentType.Application.Json)
setBody(input)
}.body()
}
}
@Serializable
data class User(
val id: String,
val name: String,
val email: String,
)kotlin
// commonMain
class ApiClient(private val httpClient: HttpClient) {
suspend fun getUsers(): List<User> {
return httpClient.get("https://api.example.com/users").body()
}
suspend fun createUser(input: CreateUserInput): User {
return httpClient.post("https://api.example.com/users") {
contentType(ContentType.Application.Json)
setBody(input)
}.body()
}
}
@Serializable
data class User(
val id: String,
val name: String,
val email: String,
)Local Storage with SQLDelight
基于SQLDelight的本地存储
sql
-- src/commonMain/sqldelight/com/example/UserQueries.sq
CREATE TABLE user (
id TEXT NOT NULL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL,
cached_at INTEGER NOT NULL
);
selectAll:
SELECT * FROM user ORDER BY name;
insertOrReplace:
INSERT OR REPLACE INTO user (id, name, email, cached_at)
VALUES (?, ?, ?, ?);
deleteById:
DELETE FROM user WHERE id = ?;sql
-- src/commonMain/sqldelight/com/example/UserQueries.sq
CREATE TABLE user (
id TEXT NOT NULL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL,
cached_at INTEGER NOT NULL
);
-- 查询全部:
SELECT * FROM user ORDER BY name;
-- 插入或替换:
INSERT OR REPLACE INTO user (id, name, email, cached_at)
VALUES (?, ?, ?, ?);
-- 根据ID删除:
DELETE FROM user WHERE id = ?;Compose Multiplatform UI
Compose Multiplatform UI开发
kotlin
// commonMain - Shared composable
@Composable
fun UserListScreen(viewModel: UserListViewModel) {
val users by viewModel.users.collectAsState()
val isLoading by viewModel.isLoading.collectAsState()
Scaffold(
topBar = { TopAppBar(title = { Text("Users") }) }
) { padding ->
if (isLoading) {
CircularProgressIndicator(modifier = Modifier.padding(padding))
} else {
LazyColumn(modifier = Modifier.padding(padding)) {
items(users) { user ->
UserRow(user = user, onClick = { viewModel.onUserClick(user.id) })
}
}
}
}
}kotlin
// commonMain - 共享可组合项
@Composable
fun UserListScreen(viewModel: UserListViewModel) {
val users by viewModel.users.collectAsState()
val isLoading by viewModel.isLoading.collectAsState()
Scaffold(
topBar = { TopAppBar(title = { Text("Users") }) }
) { padding ->
if (isLoading) {
CircularProgressIndicator(modifier = Modifier.padding(padding))
} else {
LazyColumn(modifier = Modifier.padding(padding)) {
items(users) { user ->
UserRow(user = user, onClick = { viewModel.onUserClick(user.id) })
}
}
}
}
}iOS Integration
iOS 集成
Swift Interop
Swift 互操作
swift
// iosApp - Using shared Kotlin code from Swift
import shared
class UserViewController: UIViewController {
private let viewModel = UserListViewModel()
override func viewDidLoad() {
super.viewDidLoad()
viewModel.users.collect(collector: FlowCollector { users in
// Update UI with users
})
}
}swift
// iosApp - 在Swift中使用共享Kotlin代码
import shared
class UserViewController: UIViewController {
private let viewModel = UserListViewModel()
override func viewDidLoad() {
super.viewDidLoad()
viewModel.users.collect(collector: FlowCollector { users in
// 根据用户列表更新UI
})
}
}CocoaPods or SPM Integration
CocoaPods 或 SPM 集成
kotlin
// build.gradle.kts
kotlin {
iosX64()
iosArm64()
iosSimulatorArm64()
cocoapods {
summary = "Shared module"
homepage = "https://example.com"
ios.deploymentTarget = "16.0"
framework { baseName = "shared" }
}
}kotlin
// build.gradle.kts
kotlin {
iosX64()
iosArm64()
iosSimulatorArm64()
cocoapods {
summary = "Shared module"
homepage = "https://example.com"
ios.deploymentTarget = "16.0"
framework { baseName = "shared" }
}
}Testing
测试
kotlin
// commonTest
class UserRepositoryTest {
private val fakeApi = FakeApiClient()
private val repository = UserRepository(fakeApi)
@Test
fun fetchUsersReturnsListFromApi() = runTest {
fakeApi.setUsers(listOf(User("1", "Alice", "alice@test.com")))
val users = repository.getUsers()
assertEquals(1, users.size)
assertEquals("Alice", users.first().name)
}
}kotlin
// commonTest
class UserRepositoryTest {
private val fakeApi = FakeApiClient()
private val repository = UserRepository(fakeApi)
@Test
fun fetchUsersReturnsListFromApi() = runTest {
fakeApi.setUsers(listOf(User("1", "Alice", "alice@test.com")))
val users = repository.getUsers()
assertEquals(1, users.size)
assertEquals("Alice", users.first().name)
}
}Best Practices
最佳实践
- Share business logic (networking, storage, models) — keep platform UI native if needed
- Use /
expectsparingly — prefer interfaces with platform implementations via DIactual - Keep the shared module thin — avoid pulling in platform-heavy dependencies
- Test shared code in — it runs on all targets
commonTest - Use Compose Multiplatform for new projects where native look isn't critical
- 共享业务逻辑(网络、存储、模型)——若有需求,平台UI可保留原生实现
- 谨慎使用/
expect——优先通过依赖注入使用带平台实现的接口actual - 保持共享模块轻量化——避免引入平台相关的重型依赖
- 在中测试共享代码——可在所有目标平台运行
commonTest - 若原生界面风格不是核心需求,新项目可使用Compose Multiplatform
Related Resources
相关资源
- - Android patterns
~/.claude/skills/android-development/SKILL.md - - iOS patterns
~/.claude/skills/ios-development/SKILL.md - - Alternative cross-platform
~/.claude/agents/flutter-developer.md
Share logic, respect platforms. KMP gives you the best of both worlds.
- - Android开发模式
~/.claude/skills/android-development/SKILL.md - - iOS开发模式
~/.claude/skills/ios-development/SKILL.md - - 跨平台开发替代方案
~/.claude/agents/flutter-developer.md
共享逻辑,尊重平台特性。KMP 助你兼顾跨平台与原生体验。