# Kotlin Multiplatform 2026: code delen tussen Android en iOS > Hoe Kotlin Multiplatform in 2026 bedrijfslogica deelt tussen Android en iOS: Gradle-setup, expect/actual, Ktor, Compose Multiplatform en KMP-sollicitatievragen. - 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) laat één Kotlin-codebase de bedrijfslogica van zowel Android- als iOS-apps aandrijven, terwijl elk platform zijn volledig native interface, prestaties en toegang tot de eigen SDK behoudt. In 2026 is KMP geen experiment meer: het is stabiel, officieel ondersteund door [Google](https://developer.android.com/kotlin/multiplatform) en draait in productie bij Netflix, McDonald's, Forbes en Cash App. Dit diepgaande artikel laat zien hoe het delen van code echt werkt, hoe een gedeelde module wordt geconfigureerd en welke afwegingen de moeite waard zijn vóór een KMP-sollicitatiegesprek. > **Wat KMP daadwerkelijk deelt** > > Kotlin Multiplatform deelt gecompileerde logica, geen runtime. Gemeenschappelijke Kotlin-code wordt voor Android gecompileerd naar JVM-bytecode en voor iOS (via LLVM) naar een native binary. Er is geen JavaScript-bridge, geen ingebedde webview en geen reflectielaag, waardoor gedeelde code op beide platforms op native snelheid draait. ## Hoe Kotlin Multiplatform code deelt zonder native prestaties op te geven Het kernmechanisme is een hiërarchie van source sets. `commonMain` bevat platformonafhankelijke Kotlin: networking, serialisatie, bedrijfsregels en view-modellen. Platform-source-sets zoals `androidMain` en `iosMain` bevatten code die platform-API's aanspreekt. De compiler produceert een JVM-artefact uit `commonMain` plus `androidMain` voor Android en een native framework uit `commonMain` plus `iosMain` voor iOS. Kotlin Multiplatform kiest er in 2026 voor om te delen wat echt baat heeft bij één bron van waarheid: modellen, API-clients, caching en validatie. De interface kan met Compose Multiplatform gedeeld worden of scherm voor scherm volledig native met SwiftUI blijven. Dit model ("deel de logica, kies de interface") is de reden waarom teams KMP invoeren zonder bestaande apps te herschrijven. Kotlin Multiplatform is een Kotlin-technologie die één gedeelde module naar meerdere targets compileert (Android op de JVM, iOS als native, plus desktop en web), zodat de bedrijfslogica één keer wordt geschreven terwijl elk platform zijn native interface en volledige SDK-toegang behoudt. Google ondersteunt het nu als eersteklas optie, en Jetpack-bibliotheken zoals Room, DataStore, ViewModel en Paging bieden KMP-artefacten. Beslissen wat in de gedeelde module thuishoort, is de belangrijkste architectuurkeuze in een KMP-project. De onderstaande tabel laat zien hoe de meeste productieteams de grens in 2026 trekken: alles onder de presentatielaag is een sterke kandidaat om te delen, terwijl alles wat platformspecifieke UX-conventies raakt meestal native blijft. | Laag | Delen in commonMain? | Reden | |-------|---------------------|--------| | Datamodellen en DTO's | Ja | Identiek API-contract op beide platforms | | Networking en caching | Ja | Eén HTTP-stack, één foutoppervlak | | Bedrijfsregels en validatie | Ja | Het domein verschilt niet per besturingssysteem | | View-modellen en state | Meestal | Compose en SwiftUI gebruiken beide de gedeelde state | | Interface-rendering | Optioneel | Gedeeld met Compose of native per scherm | | Platform-API's (camera, biometrie) | Nee | In plaats daarvan via expect/actual beschikbaar maken | De opbrengst schaalt mee met hoeveel van de onderste helft van die tabel een app beslaat. Datazware producten met lichte interfaces delen het meest; sterk op maat gemaakte, animatiegedreven apps delen het minst. Geen van beide uitersten is verkeerd, en daarom past KMP bij stapsgewijze invoering in plaats van een alles-of-niets-herschrijving. ## De gedeelde module configureren in Gradle Een KMP-module declareert zijn targets en source sets in Kotlin DSL. De volgende `build.gradle.kts` stelt Android en de drie iOS-architecturen in (fysiek toestel, Intel-simulator, Apple Silicon-simulator) en stelt de iOS-output beschikbaar als framework met de naam `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") } } } ``` Elk target haalt een platformspecifieke Ktor-engine binnen (`okhttp` op Android, `darwin` op iOS), terwijl de gedeelde code alleen afhangt van de gemeenschappelijke `ktor-client-core`-API. De volledige set opties staat in de [Kotlin Multiplatform-documentatie](https://kotlinlang.org/docs/multiplatform.html). ## Platformspecifieke code schrijven met expect en actual Sommige logica kan niet in `commonMain` staan omdat ze een platform-API nodig heeft. Het `expect`/`actual`-mechanisme declareert de vorm in gemeenschappelijke code en levert per target een concrete implementatie, opgelost tijdens het compileren zonder runtimekosten. ```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 } ``` De Android-implementatie leest uit de klasse `Build` en de iOS-implementatie uit `UIDevice`. Beide voldoen aan hetzelfde contract, dus `commonMain` weet nooit met welke van de twee het praat. ```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 levert getypeerde bindings voor de volledige iOS-SDK, waardoor `UIDevice`, `NSUserDefaults` en Foundation-typen vanuit Kotlin aanroepbaar zijn zonder handgeschreven glue-code. ## Networking- en serialisatielogica delen Networking is de code met de hoogste waarde om te delen, omdat API-contracten op beide platforms identiek zijn. Eén [Ktor](https://ktor.io/docs/)-client plus `kotlinx.serialization` vervangt twee parallelle implementaties. De `suspend`-functies steunen op [Kotlin-coroutines](/blog/android/mastering-kotlin-coroutines), die aan beide kanten netjes op `async`/`await` worden afgebeeld. ```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() } ``` Dit repository, de bijbehorende retry-logica en de datamodellen worden één keer geschreven. Een bug die in de parser wordt opgelost, is tegelijk op Android en iOS opgelost, en juist daar verdient KMP de opzetkosten terug. Hetzelfde patroon strekt zich uit tot een gedeelde database met SQLDelight, gedeelde key-value-opslag en gedeelde dependency injection met Koin, zodat de hele datalaag in `commonMain` kan liggen achter interfaces waarvan de UI afhankelijk is. Een veelgemaakte fout is te vroeg te veel willen delen. De pragmatische route is te beginnen met één repository, de build- en CI-pipeline op beide platforms te bewijzen en vervolgens functies één voor één te verplaatsen naarmate het vertrouwen groeit. ## Gedeelde interface bouwen met Compose Multiplatform Compose Multiplatform bereikte in mei 2025 de stabiele status op iOS (versie 1.8.0) en kwam medio 2026 uit op 1.11.0, met native tekstinvoer en standaard ingeschakelde concurrent rendering. Een `@Composable` in `commonMain` rendert op beide platforms, en gedeelde view-modellen passen van nature bij een [MVVM- of MVI-architectuur](/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)) } } } } ``` De interface delen is optioneel en gebeurt per scherm. Een team kan de volledige interface delen met [Compose Multiplatform](https://github.com/JetBrains/compose-multiplatform), voor de hele app bij SwiftUI blijven en alleen de logica delen, of beide combineren. Schermen die afhangen van platformspecifieke UX, zoals widgets of App Clips, blijven meestal native. ## Gedeelde code gebruiken vanuit Swift op iOS De gedeelde module wordt gecompileerd tot een framework dat Swift importeert als elke andere bibliotheek. Het wrijvingspunt was van oudsher dat Kotlin-`suspend`-functies en `Flow` als onhandige callback-API's naar boven kwamen. [SKIE](https://skie.touchlab.co/) van Touchlab lost dit op door idiomatisch Swift te genereren: `suspend` wordt native `async`/`await` en `Flow` wordt `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")) ?? [] } } } ``` Het iOS-team schrijft gewone Swift en SwiftUI. Het gebruikt gedeelde modellen en repositories precies zoals het een native Swift-package zou gebruiken, waardoor KMP onzichtbaar blijft voor het grootste deel van de iOS-codebase. Twee ruwe kantjes zijn in een sollicitatiegesprek nog het vermelden waard. Ten eerste de buildtijden: Kotlin/Native-compilatie is trager dan een pure Swift-build, al verzachten de Gradle-buildcache en de distributie van een vooraf gebouwd XCFramework de dagelijkse kosten. Ten tweede verbetert het debuggen over de grens tussen Swift en Kotlin, maar het is nog niet zo naadloos als in één taal blijven, wat nog een reden is waarom teams het gedeelde oppervlak bewust beperkt houden in plaats van het te laten uitdijen. ## Kotlin Multiplatform-sollicitatievragen om te verwachten Omdat KMP-sollicitatievragen steeds vaker opduiken in Android- en mobiele functies, is het de moeite waard er een paar terugkerende te oefenen naast een bredere [Android-sollicitatievoorbereiding](/technologies/android): - **Waarin verschilt expect/actual van een interface?** expect/actual wordt tijdens het compileren per target opgelost, zonder runtime-dispatch, en kan een klasse, functie, property of typealias onderbouwen. Een interface wordt dynamisch tijdens runtime opgelost. Gebruik expect/actual voor platform-API's en interfaces voor polymorfisme binnen gedeelde code. - **Hoe gaat Kotlin/Native om met geheugen en threading?** Sinds Kotlin 1.7.20 heeft de nieuwe memory manager het oude object-freezing-model afgeschaft, waardoor gedeelde muteerbare state en coroutines over threads heen grotendeels werken zoals op de JVM. - **Kan een bestaande app KMP stapsgewijs invoeren?** Ja. De gedeelde module wordt geleverd als AAR voor Android en als XCFramework voor iOS, zodat één repository of functie per keer kan migreren zonder herschrijving. - **Wanneer moet de interface native blijven in plaats van Compose Multiplatform te gebruiken?** Wanneer een scherm leunt op platformspecifieke UX of wanneer het iOS-team de presentatielaag bezit en die liever zelf beheert. > **Toolchain in 2026** > > Als richtpunt gelden Kotlin 2.3 met de K2-compiler, Ktor 3.x voor networking, SQLDelight voor een gedeelde database en Koin voor dependency injection. Voeg voor iOS-interoperabiliteit SKIE vroeg toe; het achteraf inbouwen nadat de Swift-laag is geschreven, is veel pijnlijker. ## Conclusie Kotlin Multiplatform is in 2026 een pragmatische manier om dubbele logica tussen Android en iOS te verminderen zonder native interface of prestaties op te geven: - Deel de logica met één bron van waarheid (modellen, networking, validatie) en kies per scherm voor native of Compose-interface. - Gebruik `expect`/`actual` voor platform-API's; het wordt tijdens het compileren opgelost zonder runtime-overhead. - Schrijf networking één keer met Ktor en `kotlinx.serialization`, zodat correcties op beide platforms tegelijk aankomen. - Voer stapsgewijs in via een AAR en XCFramework in plaats van je vast te leggen op een herschrijving. - Voeg SKIE vanaf het begin toe, zodat Swift `suspend` en `Flow` gebruikt als native `async`/`await` en `AsyncSequence`. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/nl/blog/android/kotlin-multiplatform-2026