# Kotlin Multiplatform 2026 : partager du code Android/iOS > Comment Kotlin Multiplatform partage la logique métier entre Android et iOS en 2026 : configuration Gradle, expect/actual, réseau Ktor, interface Compose Multiplatform et questions d'entretien KMP courantes. - 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) permet à une seule base de code Kotlin d'alimenter la logique métier des applications Android et iOS, tout en laissant chaque plateforme conserver son interface entièrement native, ses performances et l'accès à son SDK. En 2026, KMP n'est plus une expérimentation : la technologie est stable, officiellement soutenue par [Google](https://developer.android.com/kotlin/multiplatform) et déployée en production chez Netflix, McDonald's, Forbes et Cash App. Ce dossier détaille le fonctionnement réel du partage de code, la configuration d'un module partagé et les compromis à comprendre avant un entretien KMP. > **Ce que KMP partage réellement** > > Kotlin Multiplatform partage de la logique compilée, pas un runtime. Le Kotlin commun compile en bytecode JVM pour Android et en binaire natif (via LLVM) pour iOS. Il n'y a ni pont JavaScript, ni webview embarquée, ni couche de réflexion : le code partagé s'exécute donc à vitesse native sur les deux plateformes. ## Comment Kotlin Multiplatform partage le code sans sacrifier les performances natives Le mécanisme central repose sur une hiérarchie de source sets. `commonMain` contient le Kotlin indépendant de la plateforme : réseau, sérialisation, règles métier et view models. Les source sets de plateforme comme `androidMain` et `iosMain` regroupent le code qui touche aux API natives. Le compilateur produit un artefact JVM à partir de `commonMain` et `androidMain` pour Android, et un framework natif à partir de `commonMain` et `iosMain` pour iOS. En 2026, Kotlin Multiplatform privilégie le partage de ce qui bénéficie réellement d'une source de vérité unique : modèles, clients d'API, cache et validation. L'interface peut être partagée avec Compose Multiplatform ou rester entièrement native avec SwiftUI, écran par écran. Ce principe (« partager la logique, choisir l'interface ») explique pourquoi les équipes adoptent KMP sans réécrire leurs applications existantes. Kotlin Multiplatform est une technologie Kotlin qui compile un module partagé vers plusieurs cibles (Android sur la JVM, iOS en natif, ainsi que le bureau et le web) : la logique métier est écrite une seule fois pendant que chaque plateforme conserve son interface native et l'accès complet à son SDK. Google la prend désormais en charge comme une option de premier plan, et des bibliothèques Jetpack comme Room, DataStore, ViewModel et Paging proposent des artefacts KMP. Décider de ce qui appartient au module partagé est la décision d'architecture la plus importante d'un projet KMP. Le tableau ci-dessous reflète la façon dont la plupart des équipes en production tracent la ligne en 2026 : tout ce qui se situe sous la couche de présentation est un bon candidat au partage, tandis que tout ce qui touche aux conventions UX de la plateforme reste généralement natif. | Couche | Partager dans commonMain ? | Raison | |-------|---------------------|--------| | Modèles de données et DTO | Oui | Contrat d'API identique sur les deux plateformes | | Réseau et cache | Oui | Une seule pile HTTP, une seule surface de bugs | | Règles métier et validation | Oui | Le domaine ne diffère pas selon l'OS | | View models et état | Généralement | Compose et SwiftUI consomment tous deux l'état partagé | | Rendu de l'interface | Optionnel | Partagé avec Compose, ou natif écran par écran | | API de plateforme (caméra, biométrie) | Non | À exposer plutôt via expect/actual | Le gain augmente avec la part de la moitié inférieure de ce tableau que couvre une application. Les produits riches en données dotés d'interfaces légères partagent le plus ; les applications très sur mesure, portées par l'animation, partagent le moins. Aucun de ces extrêmes n'est mauvais, ce qui explique pourquoi KMP convient à une adoption progressive plutôt qu'à une réécriture radicale. ## Configurer le module partagé dans Gradle Un module KMP déclare ses cibles et ses source sets en DSL Kotlin. Le fichier `build.gradle.kts` suivant configure Android et les trois architectures iOS (appareil physique, simulateur Intel, simulateur Apple Silicon) et expose la sortie iOS sous la forme d'un framework nommé `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") } } } ``` Chaque cible tire un moteur Ktor spécifique à la plateforme (`okhttp` sur Android, `darwin` sur iOS) tandis que le code partagé ne dépend que de l'API commune `ktor-client-core`. L'ensemble des options est décrit dans la [documentation de Kotlin Multiplatform](https://kotlinlang.org/docs/multiplatform.html). ## Écrire du code spécifique à la plateforme avec expect et actual Certaine logique ne peut pas résider dans `commonMain` car elle nécessite une API de plateforme. Le mécanisme `expect`/`actual` déclare la forme dans le code commun et fournit une implémentation concrète par cible, résolue à la compilation sans aucun coût à l'exécution. ```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'implémentation Android lit depuis la classe `Build`, et l'implémentation iOS lit depuis `UIDevice`. Toutes deux respectent le même contrat, si bien que `commonMain` ne sait jamais à laquelle elle s'adresse. ```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 fournit des liaisons typées pour l'intégralité du SDK iOS : `UIDevice`, `NSUserDefaults` et les types Foundation sont donc appelables depuis Kotlin sans code de liaison écrit à la main. ## Partager la logique réseau et de sérialisation Le réseau est le code le plus rentable à partager, car les contrats d'API sont identiques sur les deux plateformes. Un seul client [Ktor](https://ktor.io/docs/) associé à `kotlinx.serialization` remplace deux implémentations parallèles. Les fonctions `suspend` s'appuient sur les [coroutines Kotlin](/blog/android/mastering-kotlin-coroutines), qui se traduisent proprement en `async`/`await` des deux côtés. ```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() } ``` Ce dépôt, sa logique de reprise et ses modèles de données sont écrits une seule fois. Un bug corrigé dans le parseur l'est simultanément sur Android et iOS, et c'est là que KMP rentabilise son coût de mise en place. Le même schéma s'étend à une base de données partagée avec SQLDelight, à un stockage clé-valeur partagé et à une injection de dépendances partagée avec Koin : toute la couche de données peut ainsi résider dans `commonMain` derrière des interfaces dont dépend l'interface utilisateur. Une erreur courante consiste à vouloir partager trop de choses trop tôt. La voie pragmatique consiste à commencer par un seul dépôt, à valider le build et le pipeline CI sur les deux plateformes, puis à migrer les fonctionnalités une par une à mesure que la confiance grandit. ## Construire une interface partagée avec Compose Multiplatform Compose Multiplatform a atteint le statut stable sur iOS en mai 2025 (version 1.8.0) et la version 1.11.0 à la mi-2026, avec la saisie de texte native et le rendu concurrent activé par défaut. Un `@Composable` dans `commonMain` s'affiche sur les deux plateformes, et les view models partagés s'accordent naturellement avec une [architecture MVVM ou 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)) } } } } ``` Le partage de l'interface est optionnel et se fait écran par écran. Une équipe peut partager toute l'interface avec [Compose Multiplatform](https://github.com/JetBrains/compose-multiplatform), conserver SwiftUI pour l'application entière et ne partager que la logique, ou combiner les deux. Les écrans qui dépendent d'une UX spécifique à la plateforme, comme les widgets ou les App Clips, restent généralement natifs. ## Consommer le code partagé depuis Swift sur iOS Le module partagé compile vers un framework que Swift importe comme n'importe quelle bibliothèque. La friction venait historiquement des fonctions `suspend` de Kotlin et de `Flow`, exposés sous forme d'API à callbacks peu pratiques. [SKIE](https://skie.touchlab.co/), de Touchlab, résout ce problème en générant du Swift idiomatique : `suspend` devient un `async`/`await` natif et `Flow` devient un `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")) ?? [] } } } ``` L'équipe iOS écrit du Swift et du SwiftUI ordinaires. Elle consomme les modèles et dépôts partagés exactement comme un package Swift natif, ce qui rend KMP invisible pour la majeure partie du code iOS. Deux aspérités méritent encore d'être mentionnées en entretien. D'abord les temps de build : la compilation Kotlin/Native est plus lente qu'un build Swift pur, même si le cache de build Gradle et la distribution d'un XCFramework précompilé atténuent le coût quotidien. Ensuite, le débogage à la frontière Swift-Kotlin s'améliore mais n'est pas encore aussi fluide que de rester dans un seul langage, une raison de plus pour les équipes de garder une surface partagée délibérée plutôt que tentaculaire. ## Questions d'entretien Kotlin Multiplatform à prévoir Comme les questions d'entretien sur KMP apparaissent de plus en plus dans les postes Android et mobiles, quelques classiques méritent d'être révisés en parallèle d'une [préparation aux entretiens Android](/technologies/android) plus large : - **En quoi expect/actual diffère-t-il d'une interface ?** expect/actual se résout à la compilation pour chaque cible, sans dispatch à l'exécution, et peut soutenir une classe, une fonction, une propriété ou un typealias. Une interface se résout dynamiquement à l'exécution. On utilise expect/actual pour les API de plateforme et les interfaces pour le polymorphisme au sein du code partagé. - **Comment Kotlin/Native gère-t-il la mémoire et les threads ?** Depuis Kotlin 1.7.20, le nouveau gestionnaire de mémoire a supprimé l'ancien modèle de gel des objets, si bien que l'état mutable partagé et les coroutines fonctionnent entre threads presque comme sur la JVM. - **Une application existante peut-elle adopter KMP de façon progressive ?** Oui. Le module partagé est livré sous forme d'AAR pour Android et de XCFramework pour iOS : un seul dépôt ou une seule fonctionnalité peut donc migrer à la fois, sans réécriture. - **Quand l'interface doit-elle rester native plutôt que d'utiliser Compose Multiplatform ?** Lorsqu'un écran s'appuie sur une UX spécifique à la plateforme ou lorsque l'équipe iOS possède et préfère contrôler la couche de présentation. > **Chaîne d'outils en 2026** > > Viser Kotlin 2.3 avec le compilateur K2, Ktor 3.x pour le réseau, SQLDelight pour une base de données partagée et Koin pour l'injection de dépendances. Pour l'interopérabilité iOS, ajouter SKIE tôt : l'intégrer après coup, une fois la couche Swift écrite, est bien plus pénible. ## Conclusion En 2026, Kotlin Multiplatform est un moyen pragmatique de réduire la logique dupliquée entre Android et iOS sans renoncer à l'interface ni aux performances natives : - Partager la logique qui possède une source de vérité unique (modèles, réseau, validation) et choisir une interface native ou Compose écran par écran. - Utiliser `expect`/`actual` pour les API de plateforme : la résolution se fait à la compilation, sans surcoût à l'exécution. - Écrire le réseau une seule fois avec Ktor et `kotlinx.serialization` pour que les corrections atterrissent sur les deux plateformes en même temps. - Adopter la technologie progressivement via un AAR et un XCFramework plutôt que de s'engager dans une réécriture. - Ajouter SKIE dès le départ pour que Swift consomme `suspend` et `Flow` sous forme de `async`/`await` et `AsyncSequence` natifs. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/fr/blog/android/kotlin-multiplatform-2026