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.

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 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.
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.
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.
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.
// 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.
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
}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 più kotlinx.serialization sostituisce due implementazioni parallele. Le funzioni suspend si basano sulle coroutine Kotlin, che si mappano in modo pulito su async/await su entrambi i lati.
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<Question> =
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.
Pronto a superare i tuoi colloqui su Android?
Pratica con i nostri simulatori interattivi, flashcards e test tecnici.
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.
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<Question>) {
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, 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 di Touchlab lo risolve generando Swift idiomatico: suspend diventa async/await nativo e Flow diventa AsyncSequence.
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:
- 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.
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/actualper 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
suspendeFlowcomeasync/awaiteAsyncSequencenativi.
Inizia a praticare!
Metti alla prova le tue conoscenze con i nostri simulatori di colloquio e test tecnici.
Tag
Condividi
Articoli correlati

Kotlin 2.3 per Android: Destrutturazione Basata sui Nomi, KMP e Domande da Colloquio 2026
Domande da colloquio su Kotlin 2.3 riguardanti la destrutturazione basata sui nomi, Kotlin Multiplatform, parametri di contesto, coroutine e Flow. Preparazione ai colloqui per sviluppatori Android nel 2026 con esempi di codice reali.

Kotlin Flow vs StateFlow vs SharedFlow: domande da colloquio Android nel 2026
Le domande su Kotlin Flow vs StateFlow vs SharedFlow che gli intervistatori Android pongono nel 2026, con risposte chiare, una tabella comparativa e codice pronto per la produzione.

Android 16 nel 2026: Nuove API, Modalità Desktop e Domande per i Colloqui
Android 16 porta cambiamenti radicali per gli sviluppatori: rendering edge-to-edge obbligatorio, modalità desktop, notifiche ProgressStyle e navigazione predittiva back. Un deep-dive tecnico con esempi di codice e domande per i colloqui.