# Kotlin Multiplatform 2026: condividere codice Android e iOS > Come Kotlin Multiplatform condivide nel 2026 la logica di business tra Android e iOS: setup Gradle, expect/actual, Ktor, Compose Multiplatform e domande da colloquio KMP. - Published: 2026-06-26 - Updated: 2026-07-06 - Author: SharpSkill - Tags: Kotlin Multiplatform, KMP, Android, iOS, Cross-platform - Reading time: 10 min --- Kotlin Multiplatform (KMP) permette a un'unica base di codice Kotlin di alimentare la logica di business sia delle app Android sia di quelle iOS, mentre ogni piattaforma mantiene la propria interfaccia completamente nativa, le prestazioni e l'accesso al proprio SDK. Nel 2026 KMP non è più un esperimento: è stabile, supportato ufficialmente da [Google](https://developer.android.com/kotlin/multiplatform) e in produzione presso Netflix, McDonald's, Forbes e Cash App. Questo approfondimento spiega come funziona davvero la condivisione del codice, come configurare un modulo condiviso e quali compromessi conviene comprendere prima di un colloquio su KMP. > **Cosa condivide davvero KMP** > > Kotlin Multiplatform condivide logica compilata, non un runtime. Il codice Kotlin comune viene compilato in bytecode JVM per Android e in un binario nativo (tramite LLVM) per iOS. Non c'è alcun bridge JavaScript, nessuna webview incorporata e nessuno strato di reflection, quindi il codice condiviso gira a velocità nativa su entrambe le piattaforme. ## Come Kotlin Multiplatform condivide il codice senza sacrificare le prestazioni native Il meccanismo centrale è una gerarchia di source set. `commonMain` contiene Kotlin indipendente dalla piattaforma: networking, serializzazione, regole di business e view model. I source set di piattaforma come `androidMain` e `iosMain` contengono codice che accede alle API della piattaforma. Il compilatore produce un artefatto JVM da `commonMain` più `androidMain` per Android e un framework nativo da `commonMain` più `iosMain` per iOS. Nel 2026 Kotlin Multiplatform predilige la condivisione di ciò che trae davvero vantaggio da un'unica fonte di verità: modelli, client API, caching e validazione. L'interfaccia può essere condivisa con Compose Multiplatform oppure lasciata completamente nativa con SwiftUI, schermata per schermata. Questo modello ("condividi la logica, scegli l'interfaccia") è il motivo per cui i team adottano KMP senza riscrivere le app esistenti. Kotlin Multiplatform è una tecnologia Kotlin che compila un unico modulo condiviso verso più target (Android sulla JVM, iOS come nativo, oltre a desktop e web), così la logica di business viene scritta una sola volta mentre ogni piattaforma conserva la propria interfaccia nativa e il pieno accesso all'SDK. Google ora la supporta come opzione di prima classe e librerie Jetpack come Room, DataStore, ViewModel e Paging offrono artefatti KMP. Decidere cosa appartiene al modulo condiviso è la scelta architetturale più importante di un progetto KMP. La tabella seguente mostra come la maggior parte dei team di produzione traccia la linea nel 2026: tutto ciò che sta sotto lo strato di presentazione è un forte candidato alla condivisione, mentre tutto ciò che tocca le convenzioni UX della piattaforma resta di norma nativo. | Strato | Condividere in commonMain? | Motivo | |-------|---------------------|--------| | Modelli dati e DTO | Sì | Contratto API identico su entrambe le piattaforme | | Networking e caching | Sì | Un solo stack HTTP, una sola superficie di bug | | Regole di business e validazione | Sì | Il dominio non cambia in base al sistema operativo | | View model e stato | Di solito | Compose e SwiftUI consumano entrambi lo stato condiviso | | Rendering dell'interfaccia | Facoltativo | Condiviso con Compose o nativo per schermata | | API di piattaforma (fotocamera, biometria) | No | Esporle invece tramite expect/actual | Il beneficio cresce in base a quanto un'app copre della metà inferiore di quella tabella. I prodotti ricchi di dati con interfacce leggere condividono di più; le app fortemente personalizzate e guidate dalle animazioni condividono di meno. Nessuno dei due estremi è sbagliato, ed è per questo che KMP si presta a un'adozione incrementale più che a una riscrittura tutto-o-niente. ## Configurare il modulo condiviso in Gradle Un modulo KMP dichiara i propri target e source set in Kotlin DSL. Il seguente `build.gradle.kts` configura Android e le tre architetture iOS (dispositivo fisico, simulatore Intel, simulatore Apple Silicon) ed espone l'output iOS come framework denominato `Shared`. ```kotlin // shared/build.gradle.kts plugins { kotlin("multiplatform") kotlin("plugin.serialization") id("com.android.library") } kotlin { // Android target compiles commonMain + androidMain to JVM bytecode androidTarget() // Three iOS targets: device, Intel sim, Apple Silicon sim listOf(iosArm64(), iosX64(), iosSimulatorArm64()).forEach { target -> target.binaries.framework { baseName = "Shared" // becomes "import Shared" in Swift isStatic = true } } sourceSets { commonMain.dependencies { implementation("io.ktor:ktor-client-core:3.3.0") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2") } androidMain.dependencies { implementation("io.ktor:ktor-client-okhttp:3.3.0") } iosMain.dependencies { implementation("io.ktor:ktor-client-darwin:3.3.0") } } } ``` Ogni target include un engine Ktor specifico della piattaforma (`okhttp` su Android, `darwin` su iOS), mentre il codice condiviso dipende solo dall'API comune `ktor-client-core`. L'insieme completo delle opzioni è disponibile nella [documentazione di Kotlin Multiplatform](https://kotlinlang.org/docs/multiplatform.html). ## Scrivere codice specifico per piattaforma con expect e actual Parte della logica non può risiedere in `commonMain` perché richiede un'API di piattaforma. Il meccanismo `expect`/`actual` dichiara la forma nel codice comune e fornisce un'implementazione concreta per ogni target, risolta in fase di compilazione senza alcun costo a runtime. ```kotlin // commonMain/kotlin/Platform.kt // commonMain declares what it needs but cannot implement here expect class Platform() { val name: String val osVersion: String } // Shared logic can now depend on Platform freely fun analyticsUserAgent(): String { val platform = Platform() return "SharpSkill/" + platform.name + " " + platform.osVersion } ``` L'implementazione Android legge dalla classe `Build`, mentre quella iOS legge da `UIDevice`. Entrambe soddisfano lo stesso contratto, quindi `commonMain` non sa mai con quale delle due sta comunicando. ```kotlin // androidMain/kotlin/Platform.kt import android.os.Build // actual supplies the Android-specific implementation actual class Platform actual constructor() { actual val name: String = "Android" actual val osVersion: String = Build.VERSION.RELEASE } ``` ```kotlin // iosMain/kotlin/Platform.kt import platform.UIKit.UIDevice // actual uses UIKit directly, no Objective-C bridging code required actual class Platform actual constructor() { actual val name: String = "iOS" actual val osVersion: String = UIDevice.currentDevice.systemVersion } ``` Kotlin/Native fornisce binding tipizzati per l'intero SDK iOS, quindi `UIDevice`, `NSUserDefaults` e i tipi Foundation sono richiamabili da Kotlin senza codice glue scritto a mano. ## Condividere la logica di networking e serializzazione Il networking è il codice dal valore più alto da condividere, perché i contratti API sono identici su entrambe le piattaforme. Un singolo client [Ktor](https://ktor.io/docs/) più `kotlinx.serialization` sostituisce due implementazioni parallele. Le funzioni `suspend` si basano sulle [coroutine Kotlin](/blog/android/mastering-kotlin-coroutines), che si mappano in modo pulito su `async`/`await` su entrambi i lati. ```kotlin // commonMain/kotlin/data/InterviewRepository.kt import io.ktor.client.HttpClient import io.ktor.client.call.body import io.ktor.client.plugins.contentnegotiation.ContentNegotiation import io.ktor.client.request.get import io.ktor.serialization.kotlinx.json.json import kotlinx.serialization.Serializable // One serializable model, zero duplication across platforms @Serializable data class Question(val id: String, val prompt: String, val difficulty: String) // One HttpClient, one JSON parser, one repository for both apps class InterviewRepository { private val client = HttpClient { install(ContentNegotiation) { json() } } suspend fun fetchQuestions(tech: String): List = client.get("https://api.sharpskill.dev/questions/" + tech).body() } ``` Questo repository, la sua logica di retry e i suoi modelli dati vengono scritti una sola volta. Un bug corretto nel parser è corretto su Android e iOS contemporaneamente, ed è qui che KMP ripaga il costo di configurazione. Lo stesso schema si estende a un database condiviso con SQLDelight, all'archiviazione key-value condivisa e alla dependency injection condivisa con Koin, così l'intero strato dati può risiedere in `commonMain` dietro interfacce da cui dipende la UI. Un errore comune è cercare di condividere troppo troppo presto. La strada pragmatica è iniziare con un solo repository, verificare la pipeline di build e CI su entrambe le piattaforme, quindi spostare le funzionalità una alla volta man mano che cresce la fiducia. ## Costruire un'interfaccia condivisa con Compose Multiplatform Compose Multiplatform ha raggiunto lo stato stabile su iOS a maggio 2025 (versione 1.8.0) e ha raggiunto la 1.11.0 a metà 2026, con input di testo nativo e rendering concorrente attivo per impostazione predefinita. Un `@Composable` in `commonMain` viene renderizzato su entrambe le piattaforme, e i view model condivisi si abbinano naturalmente a un'[architettura MVVM o MVI](/blog/android/mvvm-vs-mvi-architecture). ```kotlin // commonMain/kotlin/ui/QuestionList.kt import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.Card import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.compose.foundation.layout.padding // This list renders identically on Android and iOS @Composable fun QuestionList(questions: List) { LazyColumn { items(questions) { question -> Card(modifier = Modifier.padding(8.dp)) { Text(text = question.prompt, modifier = Modifier.padding(16.dp)) } } } } ``` Condividere l'interfaccia è facoltativo e avviene per schermata. Un team può condividere l'intera interfaccia con [Compose Multiplatform](https://github.com/JetBrains/compose-multiplatform), mantenere SwiftUI per tutta l'app e condividere solo la logica, oppure combinare entrambe le cose. Le schermate che dipendono da UX specifica della piattaforma, come i widget o gli App Clip, restano di norma native. ## Consumare il codice condiviso da Swift su iOS Il modulo condiviso viene compilato in un framework che Swift importa come qualsiasi libreria. L'attrito, storicamente, riguardava le funzioni `suspend` di Kotlin e `Flow`, che affioravano come scomode API a callback. [SKIE](https://skie.touchlab.co/) di Touchlab lo risolve generando Swift idiomatico: `suspend` diventa `async`/`await` nativo e `Flow` diventa `AsyncSequence`. ```swift // iosApp/ContentView.swift import SwiftUI import Shared // the KMP framework, baseName = "Shared" struct ContentView: View { @State private var questions: [Question] = [] private let repository = InterviewRepository() var body: some View { List(questions, id: \.id) { question in Text(question.prompt) } .task { // With SKIE, the Kotlin suspend fun is a Swift async fun questions = (try? await repository.fetchQuestions(tech: "android")) ?? [] } } } ``` Il team iOS scrive Swift e SwiftUI ordinari. Consuma modelli e repository condivisi esattamente come farebbe con un pacchetto Swift nativo, il che mantiene KMP invisibile alla maggior parte della base di codice iOS. Due spigolosità restano degne di menzione in un colloquio. Primo, i tempi di build: la compilazione Kotlin/Native è più lenta di una build Swift pura, anche se la cache di build di Gradle e la distribuzione di un XCFramework precompilato attenuano il costo quotidiano. Secondo, il debug attraverso il confine tra Swift e Kotlin sta migliorando ma non è ancora fluido come restare in un'unica lingua, il che è un ulteriore motivo per cui i team mantengono la superficie condivisa deliberata anziché dispersiva. ## Domande da colloquio su Kotlin Multiplatform da aspettarsi Poiché le domande da colloquio su KMP compaiono sempre più spesso nei ruoli Android e mobile, vale la pena ripassarne alcune ricorrenti insieme a una più ampia [preparazione ai colloqui Android](/technologies/android): - **In che modo expect/actual differisce da un'interfaccia?** expect/actual si risolve in fase di compilazione per ogni target, senza dispatch a runtime, e può sostenere una classe, una funzione, una property o un typealias. Un'interfaccia si risolve dinamicamente a runtime. expect/actual va usato per le API di piattaforma e le interfacce per il polimorfismo all'interno del codice condiviso. - **Come gestisce Kotlin/Native la memoria e il threading?** Dalla versione Kotlin 1.7.20 il nuovo memory manager ha rimosso il vecchio modello di object-freezing, quindi lo stato mutabile condiviso e le coroutine funzionano tra i thread più o meno come sulla JVM. - **Un'app esistente può adottare KMP in modo incrementale?** Sì. Il modulo condiviso viene distribuito come AAR per Android e come XCFramework per iOS, quindi è possibile migrare un repository o una funzionalità alla volta senza una riscrittura. - **Quando conviene mantenere l'interfaccia nativa invece di usare Compose Multiplatform?** Quando una schermata si appoggia a UX specifica della piattaforma o quando il team iOS possiede e preferisce controllare lo strato di presentazione. > **Toolchain nel 2026** > > Come riferimento si punta a Kotlin 2.3 con il compilatore K2, Ktor 3.x per il networking, SQLDelight per un database condiviso e Koin per la dependency injection. Per l'interoperabilità iOS, conviene aggiungere SKIE presto; integrarlo dopo aver scritto lo strato Swift è molto più doloroso. ## Conclusione Nel 2026 Kotlin Multiplatform è un modo pragmatico per ridurre la logica duplicata tra Android e iOS senza rinunciare all'interfaccia nativa o alle prestazioni: - Condividere la logica che ha un'unica fonte di verità (modelli, networking, validazione) e scegliere per ogni schermata un'interfaccia nativa o Compose. - Usare `expect`/`actual` per le API di piattaforma: si risolve in fase di compilazione senza overhead a runtime. - Scrivere il networking una sola volta con Ktor e `kotlinx.serialization`, così le correzioni arrivano su entrambe le piattaforme in una volta. - Adottare in modo incrementale tramite un AAR e un XCFramework anziché impegnarsi in una riscrittura. - Aggiungere SKIE fin dall'inizio, così Swift consuma `suspend` e `Flow` come `async`/`await` e `AsyncSequence` nativi. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/it/blog/android/kotlin-multiplatform-2026