关于 kotlin-multiplatform
kotlin-multiplatform 是面向 Kotlin 多平台项目开发的技能指南,供需要在 Android、iOS、桌面和 Web 之间共享业务逻辑的开发者使用。内容涉及项目结构、平台实现、网络、存储、界面集成和测试。
主要功能
- 01多平台项目结构示例
- 02expect/actual 平台实现模式
- 03Ktor 网络请求
- 04SQLDelight 本地存储
- 05Compose Multiplatform 共享界面
- 06共享代码测试指导
适用场景
- 01规划共享业务逻辑与平台代码
- 02为多平台应用实现网络和本地存储
- 03测试 common 模块中的共享代码
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ travisjneuman/ .claude --skill kotlin-multiplatform安装后在 Claude Code 中输入/kotlin-multiplatform确认已加载。
详细介绍
Kotlin Multiplatform 技能
在 Android、iOS、桌面端和 Web 之间共享业务逻辑,并可选择共享 UI。
项目结构
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.kts
expect/actual 模式
// 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() }
}
主要库
| 库 | 用途 | 支持多平台? |
|---|---|---|
| Ktor | HTTP 客户端 | 是 |
| kotlinx.serialization | JSON 解析 | 是 |
| kotlinx.coroutines | 异步/并发 | 是 |
| SQLDelight | 本地数据库 | 是 |
| Koin | 依赖注入 | 是 |
| Compose Multiplatform | 共享 UI | 是 |
| kotlinx.datetime | 日期/时间 | 是 |
| Napier | 日志记录 | 是 |
使用 Ktor 进行网络通信
// 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,
)
使用 SQLDelight 进行本地存储
-- 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 = ?;
Compose Multiplatform UI
// 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) })
}
}
}
}
}
iOS 集成
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
})
}
}
CocoaPods 或 SPM 集成
// build.gradle.kts
kotlin {
iosX64()
iosArm64()
iosSimulatorArm64()
cocoapods {
summary = "Shared module"
homepage = "https://example.com"
ios.deploymentTarget = "16.0"
framework { baseName = "shared" }
}
}
测试
// commonTest
class UserRepositoryTest {
private val fakeApi = FakeApiClient()
private val repository = UserRepository(fakeApi)
@Test
fun fetchUsersReturnsListFromApi() = runTest {
fakeApi.setUsers(listOf(User("1", "Alice", "[email protected]")))
val users = repository.getUsers()
assertEquals(1, users.size)
assertEquals("Alice", users.first().name)
}
}
最佳实践
- 共享业务逻辑(网络通信、存储、模型)——如有需要,保留原生平台 UI
- 谨慎使用
expect/actual——优先使用接口,并通过 DI 提供平台实现 - 保持共享模块精简——避免引入依赖平台特性过重的依赖项
- 在
commonTest中测试共享代码——它会在所有目标平台上运行 - 对于不要求原生外观的新项目,使用 Compose Multiplatform
相关资源
~/.claude/skills/android-development/SKILL.md- Android 模式~/.claude/skills/ios-development/SKILL.md- iOS 模式agent-flutter-developer技能 - 跨平台替代方案
共享逻辑,尊重平台。KMP 让你两全其美。
评论
支持 Markdown