最初の KMP プロジェクトを構成する: Kotlin マルチプラットフォームを使用した Android、iOS、およびデスクトップ
Kotlin マルチプラットフォーム プロジェクトを最初からセットアップすることは、KMP の取り組み全体の中で最も技術的な手順の 1 つです。
Flutter とは異なり、 flutter create 必要なものすべてを生成するか、React Native で
Expo は複雑さを処理しますが、KMP では Gradle、ソース セットの構造、
共有ロジックの最初の行を作成する前に、期待/実際のメカニズムを説明します。
このガイドでは、空のプロジェクトの作成からコードを含む動作するアプリケーションまでを説明します。 Android と iOS の間で共有され、各構成手順が詳細な説明とともに示されます。 最後に、正しい構造、構成された Gradle バージョン カタログ、および最初のプロジェクトを持つプロジェクトが完成します。 期待/実際の機能が動作する。
前提条件
- KMP プラグインがインストールされている Android Studio Hedgehog (2023.1.1) 以降
- Xcode 15+ (iOS アプリのビルド用 — macOS のみ)
- JDK 17+はJAVA_HOMEとして構成されています
- Kotlin と Gradle の基本的な知識
ステップ 1: KMP ウィザードを使用してプロジェクトを作成する
最も早く始める方法は、 Kotlin マルチプラットフォーム ウィザード 利用可能
住所で kmp.jetbrains.com または Android Studio から直接
ファイル → 新規 → 新しいプロジェクト → Kotlin マルチプラットフォーム アプリ。
# Alternativa da riga di comando con il KMP Wizard CLI (2026)
# Installa il plugin KMP di IntelliJ/Android Studio dal marketplace
# oppure usa il sito web:
# 1. Vai su https://kmp.jetbrains.com
# 2. Configura: nome progetto, package, target (Android, iOS, Desktop)
# 3. Scarica il progetto e aprilo in Android Studio
# Struttura generata dal wizard:
my-kmp-app/
├── composeApp/ # (se scegli Compose Multiplatform)
│ └── build.gradle.kts
├── shared/ # Il modulo condiviso principale
│ ├── src/
│ │ ├── commonMain/
│ │ ├── androidMain/
│ │ └── iosMain/
│ └── build.gradle.kts
├── androidApp/ # App Android standalone
│ └── build.gradle.kts
├── iosApp/ # App iOS standalone (Swift)
│ └── iosApp.xcodeproj
├── gradle/
│ └── libs.versions.toml # Version catalog
├── build.gradle.kts # Root build script
└── settings.gradle.kts
ステップ 2: バージョン カタログ (libs.versions.toml)
Il Gradle バージョン カタログ すべてのバージョン管理を一元化します プロジェクトの依存関係を単一の TOML ファイルにまとめます。 JetBrains が推奨するベスト プラクティスです KMP プロジェクトの場合:
# gradle/libs.versions.toml
[versions]
kotlin = "2.0.20"
kotlinx-coroutines = "1.9.0"
kotlinx-serialization = "1.7.3"
ktor = "2.3.12"
sqldelight = "2.0.2"
koin = "3.5.6"
agp = "8.5.2"
compose-multiplatform = "1.7.0"
kotlinx-datetime = "0.6.1"
[libraries]
# Kotlin
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
# Coroutines
kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" }
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }
# Serialization
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
# Ktor
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" }
ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" }
ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" }
ktor-serialization-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
# SQLDelight
sqldelight-runtime = { module = "app.cash.sqldelight:runtime", version.ref = "sqldelight" }
sqldelight-android-driver = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" }
sqldelight-native-driver = { module = "app.cash.sqldelight:native-driver", version.ref = "sqldelight" }
sqldelight-coroutines = { module = "app.cash.sqldelight:coroutines-extensions", version.ref = "sqldelight" }
# Koin DI
koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }
koin-android = { module = "io.insert-koin:koin-android", version.ref = "koin" }
# DateTime
kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinx-datetime" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
android-library = { id = "com.android.library", version.ref = "agp" }
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
compose-multiplatform = { id = "org.jetbrains.compose", version.ref = "compose-multiplatform" }
sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" }
ステップ 3: 共有モジュールのビルド スクリプト
Il build.gradle.kts 共有モジュールの設定ファイルは最も重要です
プロジェクトの。ビルド ターゲット、ソース セット、およびそれぞれの依存関係を定義します。
// shared/build.gradle.kts
plugins {
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.serialization)
alias(libs.plugins.sqldelight)
}
kotlin {
// Target Android
androidTarget {
compilations.all {
kotlinOptions {
jvmTarget = "17"
}
}
}
// Target iOS (arm64 per device, x64 per simulator Intel, simulatorArm64 per M1/M2)
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach { iosTarget ->
iosTarget.binaries.framework {
baseName = "Shared"
isStatic = true
}
}
// Target Desktop JVM (opzionale)
jvm("desktop")
sourceSets {
// Codice comune a tutte le piattaforme
commonMain.dependencies {
implementation(libs.kotlinx.coroutines.core)
implementation(libs.kotlinx.serialization.json)
implementation(libs.ktor.client.core)
implementation(libs.ktor.client.content.negotiation)
implementation(libs.ktor.serialization.json)
implementation(libs.sqldelight.runtime)
implementation(libs.sqldelight.coroutines)
implementation(libs.koin.core)
implementation(libs.kotlinx.datetime)
}
// Test comuni
commonTest.dependencies {
implementation(libs.kotlin.test)
}
// Android-specific
androidMain.dependencies {
implementation(libs.ktor.client.okhttp)
implementation(libs.sqldelight.android.driver)
implementation(libs.kotlinx.coroutines.android)
}
// iOS-specific
val iosMain by getting {
// Su iOS non puoi usare "iosMain" direttamente se hai piu target iOS
// Usa una convenzione condivisa per i tre target iOS
}
val iosX64Main by getting
val iosArm64Main by getting
val iosSimulatorArm64Main by getting
// Source set condiviso per tutti i target iOS
create("iosMain") {
dependsOn(commonMain.get())
iosX64Main.dependsOn(this)
iosArm64Main.dependsOn(this)
iosSimulatorArm64Main.dependsOn(this)
dependencies {
implementation(libs.ktor.client.darwin)
implementation(libs.sqldelight.native.driver)
}
}
}
}
android {
namespace = "com.example.mykmpapp.shared"
compileSdk = 35
defaultConfig {
minSdk = 26
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
}
sqldelight {
databases {
create("AppDatabase") {
packageName.set("com.example.mykmpapp.db")
}
}
}
ステップ 4: ソース セットを理解する
I ソースセット それらは KMP の基本構造です。各ソースセットは 1 つです
directories of Kotlin code with its dependencies, and source sets may depend on others
階層を通して dependsOn:
// Gerarchia dei source set tipica
commonMain
├── androidMain (usa librerie Android: OkHttp, Android SQLite)
├── iosMain (usa librerie Darwin: URLSession, iOS SQLite)
│ ├── iosX64Main
│ ├── iosArm64Main
│ └── iosSimulatorArm64Main
└── desktopMain (usa librerie JVM: OkHttp, SQLite JDBC)
// Tutti i source set "eredita" le dipendenze di commonMain
// androidMain puo usare tutto di commonMain + dipendenze Android-specific
プロジェクト ファイル システムでは、ディレクトリ構造はソース セットを反映します。
shared/src/
├── commonMain/
│ └── kotlin/
│ └── com/example/mykmpapp/
│ ├── data/
│ ├── domain/
│ └── Platform.kt # expect declaration
├── androidMain/
│ └── kotlin/
│ └── com/example/mykmpapp/
│ └── Platform.android.kt # actual per Android
└── iosMain/
└── kotlin/
└── com/example/mykmpapp/
└── Platform.ios.kt # actual per iOS
ステップ5: 最初のexpect/actual関数
仕組み 期待/実際 KMP がプラットフォーム固有の違いをどのように処理するかについても説明します。
expect 共通コードで API を宣言します。 actual それぞれに実装します
プラットフォーム。古典的な例から始めましょう: 現在のプラットフォームに関する情報を取得します。
// commonMain/kotlin/com/example/mykmpapp/Platform.kt
expect class PlatformInfo() {
val name: String
val version: String
val isDebug: Boolean
}
// Funzione che usa l'implementazione platform-specific
fun greeting(): String = "Running on ${PlatformInfo().name} ${PlatformInfo().version}"
// androidMain/kotlin/com/example/mykmpapp/Platform.android.kt
import android.os.Build
actual class PlatformInfo {
actual val name: String = "Android ${Build.VERSION.RELEASE}"
actual val version: String = Build.VERSION.SDK_INT.toString()
actual val isDebug: Boolean = BuildConfig.DEBUG
}
// iosMain/kotlin/com/example/mykmpapp/Platform.ios.kt
import platform.UIKit.UIDevice
actual class PlatformInfo {
actual val name: String = UIDevice.currentDevice.systemName()
actual val version: String = UIDevice.currentDevice.systemVersion
actual val isDebug: Boolean = Platform.isDebugBinary
}
2 番目のより実践的な例: UUID の生成 (プラットフォームごとに異なる API を使用します):
// commonMain: dichiarazione expect
expect fun generateUUID(): String
// androidMain: implementazione con java.util.UUID
actual fun generateUUID(): String = java.util.UUID.randomUUID().toString()
// iosMain: implementazione con NSUUID di iOS
import platform.Foundation.NSUUID
actual fun generateUUID(): String = NSUUID().UUIDString()
ステップ 6: Android アプリを構成する
Android アプリは、共有モジュールに依存する標準 Gradle モジュールです。構成 そしてシンプルです:
// androidApp/build.gradle.kts
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
}
android {
namespace = "com.example.mykmpapp.android"
compileSdk = 35
defaultConfig {
applicationId = "com.example.mykmpapp"
minSdk = 26
targetSdk = 35
versionCode = 1
versionName = "1.0"
}
buildFeatures {
compose = true
}
}
dependencies {
// Dipende dal modulo shared
implementation(projects.shared)
// UI Android (Compose o View-based)
implementation(platform("androidx.compose:compose-bom:2026.01.00"))
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.material3:material3")
implementation("androidx.activity:activity-compose:1.9.3")
}
ステップ 7: iOS アプリを構成する
iOS を統合するには、コンパイルされた KMP フレームワークを含めるように Xcode を構成する必要があります。
共有モジュールは iOS フレームワークにコンパイルされます (Shared.xcframework)それ
Xcode アプリにリンクされています。
# Script di build iOS da aggiungere come Xcode Build Phase:
# "Run Script" - da aggiungere in Build Phases di Xcode
cd "$SRCROOT/.."
# Compila il framework KMP per il target iOS corrente
if [ "$PLATFORM_NAME" = "iphonesimulator" ]; then
if [ "$ARCHS" = "arm64" ]; then
TARGET="iosSimulatorArm64"
else
TARGET="iosX64"
fi
else
TARGET="iosArm64"
fi
./gradlew "shared:link${TARGET}DebugFrameworkIos${TARGET^}" \
-Pkotlin.native.useEmbeddableCompilerJar=true
# Il framework viene copiato automaticamente da Gradle
iOS アプリの Swift ファイルでは、フレームワークは通常の Swift フレームワークと同様にインポートします。
// iosApp/ContentView.swift
import SwiftUI
import Shared // Il framework KMP compilato
struct ContentView: View {
@State private var greeting = ""
var body: some View {
VStack {
Text(greeting)
.padding()
Button("Refresh") {
greeting = Greeting().greeting()
}
}
.onAppear {
// Chiama il codice Kotlin dal framework condiviso
greeting = PlatformInfoKt.greeting()
}
}
}
ステップ 8: 共有モジュールの最初のテスト
共有モジュールで単体テストを作成して実行し、すべてが機能することを確認します。
よくあるテスト主な用途 kotlin.test これはすべてのプラットフォームで動作します。
// commonTest/kotlin/com/example/mykmpapp/PlatformTest.kt
import kotlin.test.Test
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
class PlatformTest {
@Test
fun testPlatformInfoNotNull() {
val info = PlatformInfo()
assertNotNull(info.name)
assertNotNull(info.version)
}
@Test
fun testGreetingContainsPlatform() {
val greet = greeting()
assertTrue(greet.contains("Running on"), "Greeting dovrebbe contenere 'Running on'")
}
@Test
fun testUUIDFormat() {
val uuid = generateUUID()
// UUID formato: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
assertTrue(uuid.length == 36, "UUID dovrebbe avere 36 caratteri")
assertTrue(uuid.count { it == '-' } == 4, "UUID dovrebbe avere 4 trattini")
}
}
# Esegui i test sul target JVM (piu veloce, per CI)
./gradlew shared:jvmTest
# Esegui i test su Android (emulatore o device)
./gradlew shared:connectedAndroidTest
# Esegui i test iOS (solo su macOS)
./gradlew shared:iosSimulatorArm64Test
よくある問題と解決策
- 「Kotlin/ネイティブ ツールチェーンが見つかりません」エラー: 持っていることを確認してください JDK 17 以降が JAVA_HOME として構成されており、Gradle がツールチェーンをダウンロードしていること Kotlin/ネイティブ (この理由により、最初のビルドは遅くなります)。
-
「実際の宣言が見つかりません」エラー: あなたが使っているのは
expect対応するものを提供せずにactualのために 設定されているすべてのターゲット。実際のクラス/関数が存在することを確認する で構成されたすべてのソースセットでkotlin { }. - 古い iOS フレームワーク: Kotlin コードを変更した後、 Xcode ビルドをクリーンアップし (Cmd+Shift+K)、再コンパイルします。フレームワークは次のとおりである必要があります Xcode が認識する前に Gradle で再コンパイルされます。
- ライブラリの互換性: すべての Java ライブラリが動作するわけではありません iOS。必ず、README の「KMP」または「Kotlin Multiplatform」タグが付いたライブラリを使用してください。
最終プロジェクトの構造
この構成を完了すると、プロジェクトは次のような運用構造になります。
my-kmp-app/
├── gradle/
│ └── libs.versions.toml # Version catalog centralizzato
├── shared/ # Modulo KMP condiviso
│ ├── src/
│ │ ├── commonMain/kotlin/ # Logica condivisa
│ │ ├── commonTest/kotlin/ # Test condivisi
│ │ ├── androidMain/kotlin/ # Android-specific
│ │ └── iosMain/kotlin/ # iOS-specific
│ └── build.gradle.kts
├── androidApp/ # App Android
│ ├── src/main/...
│ └── build.gradle.kts
├── iosApp/ # App iOS (Xcode project)
│ ├── iosApp/
│ │ ├── ContentView.swift
│ │ └── iOSApp.swift
│ └── iosApp.xcodeproj
├── build.gradle.kts # Root (plugin declarations)
└── settings.gradle.kts # Moduli inclusi
結論と次のステップ
Android、iOS、および共有モジュールを使用して完全な KMP プロジェクトをセットアップしました。カーブ Gradle とソース セットの学習曲線は最初は急ですが、その構造は エンタープライズ プロジェクト向けに作成され、堅牢かつスケーラブルです。
次の記事では、これについて詳しく説明します。共有モジュールアーキテクチャ: より複雑なパターンにexpect/actualを使用する方法、依存関係にKoinを設定する方法 マルチプラットフォーム インジェクション、および簡単にテストできるようにコードを構造化する方法 孤立して。
シリーズ: Kotlin マルチプラットフォーム — 1 つのコードベース、すべてのプラットフォーム
- 記事 1: 2026 年の KMP — アーキテクチャ、確立、およびエコシステム
- 第 2 条 (本): 最初の KMP プロジェクトを構成する — Android、iOS、およびデスクトップ
- 第 3 条: 共有モジュール アーキテクチャ — 期待/実際、インターフェイスおよび DI
- 第 4 条: Ktor クライアントを使用したマルチプラットフォーム ネットワーキング
- 記事 5: SQLDelight によるマルチプラットフォームの永続性
- 第 6 条: マルチプラットフォームの構成 — Android と iOS での共有 UI
- 記事 7: 状態管理 KMP — ViewModel と Kotlin フロー
- 第 8 条: KMP テスト — 単体テスト、統合テスト、UI テスト
- 第 9 条: 迅速なエクスポート — iOS との慣用的な相互運用性
- 第 10 条: KMP プロジェクトの CI/CD — GitHub Actions と Fastlane
- 第 11 条: ケーススタディ — KMP を使用したフィンテック アプリの運用中







