Configurați primul proiect KMP: Android, iOS și desktop cu Multiplatform Kotlin
Configurarea unui proiect Kotlin Multiplatform de la zero este unul dintre cei mai tehnici pași ai întregii călătorii KMP.
Spre deosebire de Flutter unde flutter create produce tot ce ai nevoie, sau React Native unde
Expo gestionează complexitatea, KMP necesită o înțelegere solidă a Gradle, a structurii setului sursă,
și mecanismul aștept/actual înainte de a putea scrie prima linie a logicii partajate.
Acest ghid vă duce de la crearea proiectului gol la o aplicație de lucru cu cod partajat între Android și iOS, arătând fiecare pas de configurare cu explicații detaliate. La final vei avea un proiect cu structura corectă, catalogul de versiuni Gradle configurat și primele funcțiile așteptate/actuale funcționează.
Cerințe preliminare
- Android Studio Hedgehog (2023.1.1) sau o versiune ulterioară cu pluginul KMP instalat
- Xcode 15+ (pentru a crea aplicația iOS - numai pe macOS)
- JDK 17+ configurat ca JAVA_HOME
- Cunoștințe de bază despre Kotlin și Gradle
Pasul 1: Creați proiectul cu expertul KMP
Cel mai rapid mod de a începe este să utilizați Kotlin Multiplatform Wizard disponibile
la adresa kmp.jetbrains.com sau direct din Android Studio prin
Fișier → Nou → Proiect nou → Aplicația multiplatformă 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
Pasul 2: Catalogul versiunilor (libs.versions.toml)
Il Catalogul versiunilor Gradle centralizează gestionarea versiunilor tuturor dependențe de proiect într-un singur fișier TOML. Este cea mai bună practică recomandată de JetBrains pentru proiecte 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" }
Pasul 3: Scriptul de construire a modulului partajat
Il build.gradle.kts al modulului partajat este cel mai important fișier de configurare
a proiectului. Definește țintele de construcție, seturile de surse și dependențele fiecăruia:
// 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")
}
}
}
Pasul 4: Înțelegerea setului sursă
I set sursă ele sunt structura fundamentală a KMP. Fiecare set de surse este unul
directoarele de cod Kotlin cu dependențele sale și seturile sursă pot depinde de altele
prin ierarhie 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
În sistemul de fișiere al proiectului, structura directoarelor reflectă seturile surse:
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
Pasul 5: Primele funcții așteptate/actuale
Mecanismul aștept/actual și modul în care KMP gestionează diferențele specifice platformei.
expect declara API-ul în cod comun, actual îl implementează pentru fiecare
platforma. Să începem cu un exemplu clasic: obținerea de informații despre platforma 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
}
Un al doilea exemplu mai practic: generarea UUID (care utilizează diferite API-uri pe 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()
Pasul 6: Configurați aplicația Android
Aplicația Android este un modul Gradle standard care depinde de modulul partajat. Configurația si simplu:
// 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")
}
Pasul 7: Configurați aplicația iOS
Integrarea iOS necesită să configurați Xcode pentru a include cadrul KMP compilat.
Modulul partajat este compilat într-un cadru iOS (Shared.xcframework) că
este legat de aplicația 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
În fișierul Swift al aplicației iOS, cadrul se importă ca un cadru Swift obișnuit:
// 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()
}
}
}
Pasul 8: Primul test al modulului partajat
Verificați dacă totul funcționează scriind și rulând un test unitar în modulul partajat.
Testarea în comunUtilizări principale kotlin.test care funcționează pe toate platformele:
// 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
Probleme și soluții comune
- Eroare „Kotlin/Lanțul de instrumente nativ nu a fost găsit”: Asigurați-vă că aveți JDK 17+ configurat ca JAVA_HOME și că Gradle a descărcat lanțul de instrumente Kotlin/Native (prima versiune este mai lentă din acest motiv).
-
eroare „declarația reală nu a fost găsită”: Tu folosești
expectfără a furniza unul corespunzătoractualpentru toate țintele configurate. Verificați dacă clasa/funcția reală există în toate seturile de surse configurate înkotlin { }. - Cadru iOS învechit: După modificări ale codului Kotlin, curățați versiunea Xcode (Cmd+Shift+K) și recompilați. Cadrul trebuie să fie recompilat cu Gradle înainte ca Xcode să îl poată vedea.
- Compatibilitate bibliotecă: Nu toate bibliotecile Java funcționează iOS. Utilizați întotdeauna biblioteci cu eticheta „KMP” sau „Kotlin Multiplatform” în README.
Structura Proiectului Final
La sfârșitul acestei configurații, proiectul dumneavoastră are această structură operațională:
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
Concluzii și pașii următori
Ați configurat un proiect KMP complet cu Android, iOS și modulul partajat. Curba Curba de învățare a seturilor Gradle și sursă este abruptă la început, dar structura pe care o aveți creat și robust și scalabil pentru proiecte de întreprindere.
Următorul articol analizeazăarhitectura modulului partajat: cum să folosiți aștept/actual pentru modele mai complexe, cum să setați Koin pentru dependență injecție multiplatformă și cum să structurați codul pentru a fi ușor de testat în izolare.
Seria: Kotlin Multiplatform — O bază de cod, toate platformele
- Articolul 1: KMP în 2026 — Arhitectură, stabilire și ecosistem
- Articolul 2 (acesta): Configurați primul proiect KMP - Android, iOS și desktop
- Articolul 3: Arhitectura de module partajate — așteptare/actuală, interfețe și DI
- Articolul 4: Rețea multiplatformă cu Ktor Client
- Articolul 5: Persistența multiplatformă cu SQLDelight
- Articolul 6: Compuneți Multiplatform – UI partajat pe Android și iOS
- Articolul 7: Managementul statului KMP — ViewModel și Kotlin Flows
- Articolul 8: Testare KMP — Test unitar, Test de integrare și Test UI
- Articolul 9: Swift Export — Idiomatic Interop cu iOS
- Articolul 10: CI/CD pentru proiecte KMP — GitHub Actions și Fastlane
- Articolul 11: Studiu de caz — Aplicație Fintech cu KMP în producție







