kotlin-multiplatform について
kotlin-multiplatform は、Kotlin Multiplatform プロジェクトを開発し、Android、iOS、デスクトップ、Web 間でビジネスロジックを共有する開発者向けのスキルガイドです。プロジェクト構成、プラットフォーム別実装、ネットワーク、ストレージ、UI 連携、テストを扱います。
主な機能
- 01マルチプラットフォーム構成例
- 02expect/actual による実装パターン
- 03Ktor によるネットワーク処理
- 04SQLDelight によるローカル保存
- 05Compose Multiplatform の共有 UI
- 06共有コードのテスト指針
活用シーン
- 01共有ロジックとプラットフォーム固有コードの設計
- 02マルチプラットフォームアプリへの通信と保存の実装
- 03common モジュールの共有コードのテスト
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ travisjneuman/ .claude --skill kotlin-multiplatformインストール後、Claude Code で/kotlin-multiplatformを入力して読み込まれたか確認します。
詳細
Kotlin Multiplatform スキル
Android、iOS、デスクトップ、ウェブ間でビジネスロジックを共有し、必要に応じて 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 対応