Contents
1. 開発環境の要件とセットアップ
1-1️⃣ 推奨ツール一覧(2024 年版)
| カテゴリ | 製品・プラグイン | 安定版バージョン例 | 入手/設定方法 |
|---|---|---|---|
| IDE | Android Studio (Flamingo 系列) | 2023.1.2 | IDE の Help ▸ Check for Updates から最新版を取得 |
| IntelliJ IDEA / Fleet(任意) | 2023.3.x 系列 | JetBrains公式サイトからダウンロード | |
| Kotlin | Kotlin コンパイラ | 1.9.10 | Gradle が自動解決。kotlin("jvm") version "1.9.10" を記述 |
| KMP Gradle Plugin | org.jetbrains.kotlin.multiplatform | 2.0.20 | plugins { id("org.jetbrains.kotlin.multiplatform") version "2.0.20" } |
| Xcode | Xcode (Command Line Tools 含む) | 15.3 以上 | App Store → Xcode → Preferences ▸ Locations で CLT を選択 |
| Android SDK | compileSdk / targetSdk | 34 | android { compileSdk = 34 } として Gradle に設定 |
ポイント
バージョンは「互換性が保証された組み合わせ」を意識してください。たとえば Kotlin 1.9 系は KMP Plugin 2.x 系と相性が良く、Android SDK 34 は最新の Jetpack Compose と問題なく連携します。
1-2️⃣ 環境構築手順
1. IDE とプラグインの準備
|
1 2 3 |
① Android Studio(または IntelliJ IDEA)をインストール ② 起動 → Preferences ▸ Plugins → 「Kotlin Multiplatform」が有効か確認 |
2. macOS の開発ツール設定(iOS 向け)
| 手順 | コマンド例 | 説明 |
|---|---|---|
| Command Line Tools をインストール | xcode-select --install |
初回だけ実行すれば OK |
| パス確認 | xcode-select -p |
/Applications/Xcode.app/Contents/Developer が返ってくることを確認 |
| Xcode のバージョン | xcodebuild -version |
15.3 以上かチェック |
3. Kotlin バージョンの検証
|
1 2 3 |
$ kotlin -version Kotlin version 1.9.10-release-123 (JVM) |
ポイント
上記が表示されれば、Gradle が期待通りに Kotlin を解決できる状態です。
2. KMP プロジェクトの作成手順
2-1️⃣ Wizard(ウィザード)で雛形を生成
- File ▸ New ▸ Project → Kotlin Multiplatform を選択
- プロジェクト名・保存先を入力し Next
- Target platforms で Android と iOS にチェック
- UI の共有方針を選択
| オプション | 内容 |
|---|---|
| Do not share UI | iOS は SwiftUI、Android は Jetpack Compose を個別実装 |
| Share UI | Compose Multiplatform で共通 UI を実装(※後述シナリオ①) |
- 完了すると
sharedモジュールと以下のフォルダ構造が自動生成されます。
|
1 2 3 4 5 |
shared/ ├─ commonMain/ ├─ androidMain/ └─ iosX64 / iosArm64/ |
ポイント
Wizard が作成するbuild.gradle.ktsには必要なプラグインと基本的な sourceSet 定義がすでに組み込まれています。
2-2️⃣ 手動でプロジェクトを構築したい場合
|
1 2 3 4 5 6 7 8 9 10 11 12 |
plugins { kotlin("multiplatform") version "2.0.20" id("com.android.library") } kotlin { androidTarget() iosX64() iosArm64() sourceSets { /* 省略 */ } } |
ポイント
手動構成でも上記プラグインと target 設定さえ揃えば、Wizard と同等の雛形が得られます。
3. Gradle 設定とソースセット構成
3-1️⃣ 基本的な build.gradle.kts(抜粋)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
plugins { kotlin("multiplatform") version "2.0.20" id("com.android.library") id("org.jetbrains.kotlin.native.cocoapods") version "2.0.20" // iOS 連携用(任意) } kotlin { androidTarget() iosX64() iosArm64() // ↓ Gradle の新しい targetHierarchy を利用 targetHierarchy.default() sourceSets { val commonMain by getting { dependencies { implementation(kotlin("stdlib-common")) implementation("io.ktor:ktor-client-core:2.3.4") implementation("com.squareup.sqldelight:runtime:1.5.5") // 安定版 implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") } } val androidMain by getting { dependencies { implementation(kotlin("stdlib")) implementation("io.ktor:ktor-client-android:2.3.4") implementation("com.squareup.sqldelight:android-driver:1.5.5") } } // iOS 用共通 sourceSet val iosMain by creating { dependsOn(commonMain) dependencies { implementation("io.ktor:ktor-client-ios:2.3.4") implementation("com.squareup.sqldelight:native-driver:1.5.5") } } // 実機 / シミュレータ別に依存関係を継承 val iosX64Main by getting { dependsOn(iosMain) } val iosArm64Main by getting { dependsOn(iosMain) } } } android { compileSdk = 34 namespace = "com.example.shared" } |
ポイント
-targetHierarchy.default()を使うと、iOS の実機/シミュレータ間でコードを自動共有でき、iosMainに依存関係を集約できます。
- SQLDelight は 1.5.5 が安定版として公開されており、runtime,android-driver,native-driverのすべてが利用可能です。
3-2️⃣ 期待/実装(expect / actual)でプラットフォーム固有コードを分離
|
1 2 3 4 5 6 |
// commonMain expect class KeyValueStore { fun putString(key: String, value: String) fun getString(key: String): String? } |
|
1 2 3 4 5 6 7 |
// iosMain actual class KeyValueStore { private val defaults = NSUserDefaults.standardUserDefaults actual fun putString(key: String, value: String) { defaults.setObject(value, forKey = key) } actual fun getString(key: String): String? = defaults.stringForKey(key) } |
|
1 2 3 4 5 6 7 |
// androidMain actual class KeyValueStore(private val ctx: Context) { private val prefs = ctx.getSharedPreferences("app_prefs", MODE_PRIVATE) actual fun putString(key: String, value: String) { prefs.edit().putString(key, value).apply() } actual fun getString(key: String): String? = prefs.getString(key, null) } |
ポイント
expect/actualによる抽象化は、コンパイル時に正しい実装が選択されるためランタイムエラーのリスクを低減します。
4. 共有コードの実装パターン
4-1️⃣ データ層(Ktor + SQLDelight)
API クライアント(commonMain)
|
1 2 3 4 5 6 7 8 9 10 11 12 |
import io.ktor.client.* import io.ktor.client.request.* class ApiClient { private val client = HttpClient { // 共通のタイムアウトや JSON シリアライズ設定を記述 } suspend fun fetchPosts(): List<Post> = client.get("https://jsonplaceholder.typicode.com/posts") } |
DB ドライバ(commonMain)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import com.squareup.sqldelight.db.SqlDriver import com.example.shared.db.AppDatabase expect fun getSqlDriver(): SqlDriver class Repository { private val db = AppDatabase(getSqlDriver()) suspend fun insertPost(post: Post) = db.postQueries.insert( id = post.id, title = post.title, body = post.body ) } |
iOS / Android の実装例
|
1 2 3 4 |
// iosMain actual fun getSqlDriver(): SqlDriver = NativeSqliteDriver(AppDatabase.Schema, "app.db") |
|
1 2 3 4 |
// androidMain actual fun getSqlDriver(): SqlDriver = AndroidSqliteDriver(AppDatabase.Schema, context, "app.db") |
ポイント
expect/actualによって DB ドライバだけをプラットフォームごとに差し替え、ビジネスロジックは commonMain に集約できます。
4-2️⃣ 非同期処理とリアクティブ UI(Coroutines + Flow)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import kotlinx.coroutines.flow.* class PostRepository( private val api: ApiClient, private val db: Repository ) { fun getPosts(): Flow<List<Post>> = flow { val remote = api.fetchPosts() remote.forEach { db.insertPost(it) } emit(remote) }.map { it.sortedByDescending { post -> post.id } } } |
ポイント
Flowを返すことで UI 側はcollectAsStateだけで最新データをリアルタイムに取得できます。
5. プラットフォーム固有コードと UI 統合シナリオ
5-1️⃣ シナリオ① ― Compose Multiplatform で UI を共有
共通 UI(commonMain)
|
1 2 3 4 5 6 7 8 9 10 |
@Composable fun PostList(viewModel: PostViewModel) { val posts by viewModel.posts.collectAsState(initial = emptyList()) LazyColumn { items(posts) { post -> Text(text = post.title, style = MaterialTheme.typography.bodyLarge) } } } |
Android 側エントリーポイント
|
1 2 3 4 5 6 7 8 |
class MainActivity : ComponentActivity() { private val viewModel: PostViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { PostList(viewModel) } } } |
iOS 側エントリーポイント
|
1 2 3 4 5 6 7 8 9 10 11 12 |
import SwiftUI import shared // Kotlin framework struct ContentView: View { private let viewModel = PostViewModel() var body: some View { ComposeView(controller: viewModel.composeController()) } } // `composeController()` は Kotlin 側で UI をラップした UIViewController を返す拡張関数 |
ポイント
- Android と iOS の両方で同一の Compose コードが走り、UI の一貫性と保守コスト削減が実現します。
- ただし iOS では現在(2024 年)「Compose for iOS」はベータ版であり、パフォーマンス要件や App Store 審査への影響を事前に確認してください。
5-2️⃣ シナリオ② ― ネイティブ UI を個別実装しロジックだけ共有
| プラットフォーム | 実装ファイル例 |
|---|---|
| Android | androidApp/src/main/kotlin/com/example/ui/PostsScreen.kt(Jetpack Compose) |
| iOS | iosApp/PostsView.swift(SwiftUI) |
SwiftUI 側サンプル
|
1 2 3 4 5 6 7 8 9 10 |
struct PostsView: View { @StateObject private var vm = PostViewModel() var body: some View { List(vm.posts) { post in Text(post.title) } .onAppear { vm.loadPosts() } } } |
Jetpack Compose 側サンプル
|
1 2 3 4 5 6 7 8 9 10 |
@Composable fun PostsScreen(viewModel: PostViewModel = viewModel()) { val posts by viewModel.posts.collectAsState(initial = emptyList()) LazyColumn { items(posts) { post -> Text(post.title, style = MaterialTheme.typography.bodyMedium) } } } |
ポイント
UI がプラットフォームごとに最適化されるため、ユーザー体験が向上します。ロジックはsharedモジュールのPostViewModelに委譲するだけで済みます。
6. ビルド・デバッグフロー、トラブルシューティング、CI/CD
6-1️⃣ Android と iOS のビルドコマンド
| タスク | コマンド例 | 説明 |
|---|---|---|
| Android デバッグ | ./gradlew :androidApp:assembleDebug && adb install -r androidApp/build/outputs/apk/debug/androidApp-debug.apk |
IDE がなくても端末へ直接インストール可能 |
| iOS シミュレータ用フレームワーク | ./gradlew linkDebugFrameworkIosX64 |
Xcode のシミュレータで利用できる .framework を生成 |
| iOS 実機用フレームワーク | ./gradlew linkDebugFrameworkIosArm64 |
実機テスト用にビルド(署名が必要) |
ポイント
Gradle のタスクは Android と iOS が同一プロジェクト内で統合されているため、どちらかを変更したらもう片方のビルドも自動的に再評価されます。
6-2️⃣ よくあるエラーと対処法
| エラーメッセージ | 主な原因 | 解決策 |
|---|---|---|
| KMP Gradle Plugin バージョン不整合 | plugins { id("org.jetbrains.kotlin.multiplatform") version "2.0.10" } 等の古い記述 |
build.gradle.kts のプラグイン行を 2.0.20 に更新 |
| Xcode CLI ツールが未設定 | xcode-select -p が空 |
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer を実行 |
| iOS フレームワークの署名失敗 | codesign エラー、プロビジョニング不足 |
Xcode の Signing & Capabilities で正しいチームとプロファイルを選択、もしくはローカルテスト時は ./gradlew linkDebugFrameworkIosX64 -PdisableSigning=true |
| SQLDelight が見つからない | ライブラリバージョンが誤っている | implementation("com.squareup.sqldelight:runtime:1.5.5") など、Maven Central に掲載されている正しいバージョンに修正 |
6-3️⃣ GitHub Actions を使った CI/CD の実装例
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
name: KMP CI on: push: branches: [ main ] pull_request: jobs: android: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up JDK 17 uses: actions/setup-java@v3 with: distribution: temurin java-version: 17 - name: Build Android Debug APK run: ./gradlew :androidApp:assembleDebug - name: Upload APK artifact uses: actions/upload-artifact@v4 with: name: android-debug-apk path: androidApp/build/outputs/apk/debug/*.apk ios: runs-on: macos-14 # Apple Silicon ランナー(Xcode 15 がプリインストール) steps: - uses: actions/checkout@v4 - name: Build iOS Framework (Simulator) run: ./gradlew linkDebugFrameworkIosX64 - name: Upload iOS framework uses: actions/upload-artifact@v4 with: name: ios-framework-sim path: shared/build/bin/iosX64/debugFramework/*.framework |
ポイント
- Android ビルドは Linux ランナーで完結し、iOS ビルドは macOS ランナーが必要です。
-upload-artifactにより生成物をプルリクエストのチェック結果として確認でき、品質ゲートに組み込みやすくなります。
7. 次のステップと学習リソース
| リンク | 内容 |
|---|---|
| Kotlin Multiplatform Documentation | 最新ツールチェーン・Gradle 設定例が網羅的に掲載されています。 |
| Android Developers – KMP Codelab | 手を動かしながら実践できるサンプルプロジェクトが提供されています。 |
| SQLDelight Official Guide | データベーススキーマの書き方とマルチプラットフォームでの生成コード解説があります。 |
| Compose Multiplatform Samples | iOS へデプロイする際の注意点やパフォーマンス測定手順が掲載されています。 |
実践アドバイス
1. 本稿の手順で「HelloKMP」アプリを作成し、Android エミュレータと iOS シミュレータの両方で動かす。
2.PostRepositoryのテストコード(JVM テスト)を書き、CI に組み込んで成功することを確認。
3. プロジェクトが安定したら、Compose Multiplatform と ネイティブ UI のどちらが自チームに適しているか評価し、次のフェーズへ移行してください。
以上で、Kotlin Multiplatform を用いた Android/iOS 同時開発の全体像と実装・運用手順をご紹介しました。