Skonfiguruj pierwszy projekt KMP: Android, iOS i komputer stacjonarny z platformą Kotlin Multiplatform
Skonfigurowanie od podstaw projektu wieloplatformowego Kotlin jest jednym z najbardziej technicznych etapów całej podróży KMP.
W przeciwieństwie do Flutter gdzie flutter create produkuje wszystko, czego potrzebujesz, lub React Native gdzie
Expo radzi sobie ze złożonością, KMP wymaga solidnego zrozumienia Gradle, struktury zestawu źródeł,
oraz mechanizm oczekiwań/rzeczywistości, zanim będziesz mógł napisać pierwszą linię wspólnej logiki.
Ten przewodnik poprowadzi Cię od tworzenia pustego projektu do działającej aplikacji z kodem udostępniane pomiędzy systemami Android i iOS, pokazujące każdy krok konfiguracji ze szczegółowymi objaśnieniami. Na koniec będziesz miał projekt z poprawną strukturą, skonfigurowanym katalogiem wersji Gradle i pierwsze oczekiwane/rzeczywiste funkcje działają.
Warunki wstępne
- Android Studio Hedgehog (2023.1.1) lub nowszy z zainstalowaną wtyczką KMP
- Xcode 15+ (do zbudowania aplikacji na iOS — tylko na macOS)
- JDK 17+ skonfigurowany jako JAVA_HOME
- Podstawowa znajomość Kotlina i Gradle
Krok 1: Utwórz projekt za pomocą kreatora KMP
Najszybszym sposobem na rozpoczęcie jest użycie narzędzia Kreator wieloplatformowy Kotlin dostępne
pod adresem kmp.jetbrains.com lub bezpośrednio z Android Studio poprzez
Plik → Nowy → Nowy projekt → Aplikacja wieloplatformowa 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
Krok 2: Katalog wersji (libs.versions.toml)
Il Katalog wersji Gradle centralizuje zarządzanie wersjami wszystkich zależności projektu w jednym pliku TOML. Jest to najlepsza praktyka zalecana przez JetBrains dla projektów 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" }
Krok 3: Skrypt budowania modułu współdzielonego
Il build.gradle.kts modułu współdzielonego jest najważniejszym plikiem konfiguracyjnym
projektu. Definiuje cele kompilacji, zestawy źródłowe i zależności każdego z nich:
// 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")
}
}
}
Krok 4: Zrozumienie zbiorów źródłowych
I zestaw źródłowy są podstawową strukturą KMP. Każdy zestaw źródłowy jest jeden
katalogi kodu Kotlina z jego zależnościami i zestawy źródłowe mogą zależeć od innych
poprzez hierarchię 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
W systemie plików projektu struktura katalogów odzwierciedla zestawy źródłowe:
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
Krok 5: Pierwsze funkcje oczekiwane/rzeczywiste
Mechanizm oczekiwać/faktycznie oraz jak KMP radzi sobie z różnicami specyficznymi dla platformy.
expect zadeklaruj API we wspólnym kodzie, actual wdraża to dla każdego
platforma. Zacznijmy od klasycznego przykładu: uzyskania informacji o aktualnej platformie:
// 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
}
Drugi, bardziej praktyczny przykład: generowanie identyfikatora UUID (który wykorzystuje różne interfejsy API na platformę):
// 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()
Krok 6: Skonfiguruj aplikację na Androida
Aplikacja na Androida to standardowy moduł Gradle zależny od modułu współdzielonego. Konfiguracja i proste:
// 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")
}
Krok 7: Skonfiguruj aplikację na iOS
Integracja z iOS wymaga skonfigurowania Xcode tak, aby zawierał skompilowaną platformę KMP.
Udostępniony moduł jest wkompilowany w framework iOS (Shared.xcframework) to
jest połączony z aplikacją 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
W pliku Swift aplikacji na iOS framework importuje się jak zwykły framework 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()
}
}
}
Krok 8: Pierwszy test modułu współdzielonego
Sprawdź, czy wszystko działa, pisząc i uruchamiając test jednostkowy w module współdzielonym.
Testowanie typowych głównych zastosowań kotlin.test który działa na wszystkich platformach:
// 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
Typowe problemy i rozwiązania
- Błąd „Nie znaleziono zestawu narzędzi Kotlin/Native”: Upewnij się, że tak JDK 17+ skonfigurowany jako JAVA_HOME i że Gradle pobrał zestaw narzędzi Kotlin/Native (z tego powodu pierwsza kompilacja jest wolniejsza).
-
Błąd „nie znaleziono rzeczywistej deklaracji”: Używasz
expectbez podania odpowiedniegoactualdla wszystkie skonfigurowane cele. Sprawdź, czy rzeczywista klasa/funkcja istnieje we wszystkich zestawach źródłowych skonfigurowanych wkotlin { }. - Nieaktualny framework iOS: Po zmianach w kodzie Kotlina, wyczyść kompilację Xcode (Cmd+Shift+K) i skompiluj ponownie. Ramy muszą być ponownie skompilowany za pomocą Gradle, zanim Xcode będzie mógł to zobaczyć.
- Kompatybilność bibliotek: Nie wszystkie biblioteki Java działają iOS. Zawsze używaj bibliotek ze znacznikiem „KMP” lub „Kotlin Multiplatform” w pliku README.
Struktura Projektu Końcowego
Na końcu tej konfiguracji Twój projekt ma następującą strukturę operacyjną:
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
Wnioski i dalsze kroki
Skonfigurowałeś kompletny projekt KMP z systemem Android, iOS i modułem współdzielonym. Krzywa Na początku krzywa uczenia się Gradle i zestawów źródłowych jest stroma, ale strukturę masz stworzone, solidne i skalowalne dla projektów korporacyjnych.
Następny artykuł poświęcony jestwspółdzielona architektura modułowa: jak używać oczekiwań/rzeczywistych w przypadku bardziej złożonych wzorców, jak ustawić Koin dla zależności wtrysk wieloplatformowy i jak ustrukturyzować kod, aby był łatwy do testowania w izolacji.
Seria: Kotlin Multiplatform — jedna baza kodu, wszystkie platformy
- Artykuł 1: KMP w 2026 r. — Architektura, establishment i ekosystem
- Artykuł 2 (ten): Skonfiguruj pierwszy projekt KMP — Android, iOS i komputer stacjonarny
- Artykuł 3: Architektura modułów współdzielonych — oczekiwana/rzeczywista, interfejsy i DI
- Artykuł 4: Sieć wieloplatformowa z klientem Ktor
- Artykuł 5: Trwałość wieloplatformowa z SQLDelight
- Artykuł 6: Twórz na wielu platformach — wspólny interfejs użytkownika na Androidzie i iOS
- Artykuł 7: Zarządzanie stanem KMP — ViewModel i Kotlin Flows
- Artykuł 8: Testowanie KMP — test jednostkowy, test integracyjny i test interfejsu użytkownika
- Artykuł 9: Swift Export — Idiomatyczna współpraca z iOS
- Artykuł 10: CI/CD dla projektów KMP — GitHub Actions i Fastlane
- Artykuł 11: Studium przypadku — aplikacja Fintech z KMP w produkcji







