Kotlin Multiplatform 2026: Code für Android und iOS teilen

Wie Kotlin Multiplatform 2026 Geschäftslogik zwischen Android und iOS teilt: Gradle-Setup, expect/actual, Ktor, Compose Multiplatform und KMP-Interviewfragen.

Kotlin Multiplatform 2026 teilt Code zwischen Android- und iOS-Apps

Kotlin Multiplatform (KMP) ermöglicht es, mit einer einzigen Kotlin-Codebasis die Geschäftslogik von Android- und iOS-Apps zu betreiben, während jede Plattform ihre vollständig native Oberfläche, Performance und ihren SDK-Zugriff behält. 2026 ist KMP kein Experiment mehr: Die Technologie ist stabil, wird offiziell von Google unterstützt und läuft produktiv bei Netflix, McDonald's, Forbes und Cash App. Dieser ausführliche Beitrag zeigt, wie das Code-Sharing tatsächlich funktioniert, wie sich ein gemeinsames Modul konfigurieren lässt und welche Kompromisse vor einem KMP-Interview verstanden werden sollten.

Was KMP tatsächlich teilt

Kotlin Multiplatform teilt kompilierte Logik, keine Laufzeitumgebung. Gemeinsamer Kotlin-Code wird für Android zu JVM-Bytecode und für iOS (über LLVM) zu einer nativen Binärdatei kompiliert. Es gibt keine JavaScript-Brücke, keine eingebettete Webview und keine Reflection-Schicht, sodass geteilter Code auf beiden Plattformen mit nativer Geschwindigkeit läuft.

Wie Kotlin Multiplatform Code teilt, ohne native Performance zu opfern

Der Kernmechanismus ist eine Hierarchie von Source-Sets. commonMain enthält plattformunabhängigen Kotlin-Code: Networking, Serialisierung, Geschäftsregeln und View-Models. Plattform-Source-Sets wie androidMain und iosMain enthalten Code, der auf Plattform-APIs zugreift. Der Compiler erzeugt aus commonMain plus androidMain ein JVM-Artefakt für Android und aus commonMain plus iosMain ein natives Framework für iOS.

Kotlin Multiplatform setzt 2026 darauf, das zu teilen, was wirklich von einer einzigen Quelle der Wahrheit profitiert: Modelle, API-Clients, Caching und Validierung. Die Oberfläche kann mit Compose Multiplatform geteilt oder Bildschirm für Bildschirm vollständig nativ mit SwiftUI belassen werden. Dieses Modell ("die Logik teilen, die UI wählen") ist der Grund, warum Teams KMP einführen, ohne bestehende Apps neu zu schreiben.

Kotlin Multiplatform ist eine Kotlin-Technologie, die ein gemeinsames Modul zu mehreren Zielen kompiliert (Android auf der JVM, iOS als nativ, dazu Desktop und Web), sodass die Geschäftslogik einmal geschrieben wird, während jede Plattform ihre native Oberfläche und vollen SDK-Zugriff behält. Google unterstützt die Technologie inzwischen als erstklassige Option, und Jetpack-Bibliotheken wie Room, DataStore, ViewModel und Paging bieten KMP-Artefakte an.

Zu entscheiden, was in das gemeinsame Modul gehört, ist die wichtigste architektonische Entscheidung in einem KMP-Projekt. Die folgende Tabelle zeigt, wie die meisten Produktionsteams 2026 die Grenze ziehen: Alles unterhalb der Präsentationsschicht ist ein starker Kandidat zum Teilen, während alles, was plattformspezifische UX-Konventionen berührt, meist nativ bleibt.

| Schicht | In commonMain teilen? | Grund | |-------|---------------------|--------| | Datenmodelle und DTOs | Ja | Identischer API-Vertrag auf beiden Plattformen | | Networking und Caching | Ja | Ein HTTP-Stack, eine Fehlerquelle | | Geschäftsregeln und Validierung | Ja | Die Domäne unterscheidet sich nicht je Betriebssystem | | View-Models und State | Meistens | Compose und SwiftUI nutzen beide den geteilten State | | UI-Rendering | Optional | Geteilt mit Compose oder nativ je Bildschirm | | Plattform-APIs (Kamera, Biometrie) | Nein | Stattdessen über expect/actual bereitstellen |

Der Nutzen skaliert damit, wie viel von der unteren Hälfte dieser Tabelle eine App abdeckt. Datenlastige Produkte mit schlanken Oberflächen teilen am meisten, hochgradig maßgeschneiderte, animationsgetriebene Apps am wenigsten. Kein Extrem ist falsch, weshalb sich KMP eher für eine schrittweise Einführung als für ein Alles-oder-nichts-Rewrite eignet.

Das gemeinsame Modul in Gradle konfigurieren

Ein KMP-Modul deklariert seine Ziele und Source-Sets in Kotlin DSL. Die folgende build.gradle.kts richtet Android und die drei iOS-Architekturen ein (physisches Gerät, Intel-Simulator, Apple-Silicon-Simulator) und stellt die iOS-Ausgabe als Framework namens Shared bereit.

shared/build.gradle.ktskotlin
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")
        }
    }
}

Jedes Ziel bindet eine plattformspezifische Ktor-Engine ein (okhttp unter Android, darwin unter iOS), während der geteilte Code nur von der gemeinsamen ktor-client-core-API abhängt. Der vollständige Optionsumfang findet sich in der Kotlin-Multiplatform-Dokumentation.

Plattformspezifischen Code mit expect und actual schreiben

Manche Logik kann nicht in commonMain liegen, weil sie eine Plattform-API benötigt. Der expect/actual-Mechanismus deklariert die Signatur im gemeinsamen Code und liefert eine konkrete Implementierung pro Ziel, aufgelöst zur Kompilierzeit ohne Laufzeitkosten.

commonMain/kotlin/Platform.ktkotlin
// 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
}

Die Android-Implementierung liest aus der Build-Klasse, die iOS-Implementierung aus UIDevice. Beide erfüllen denselben Vertrag, sodass commonMain nie weiß, mit welcher es gerade spricht.

androidMain/kotlin/Platform.ktkotlin
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
}
iosMain/kotlin/Platform.ktkotlin
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 liefert typisierte Bindings für das gesamte iOS-SDK, sodass UIDevice, NSUserDefaults und Foundation-Typen aus Kotlin ohne handgeschriebenen Glue-Code aufrufbar sind.

Networking- und Serialisierungslogik teilen

Networking ist der Code mit dem höchsten Nutzen beim Teilen, weil die API-Verträge auf beiden Plattformen identisch sind. Ein einzelner Ktor-Client plus kotlinx.serialization ersetzt zwei parallele Implementierungen. Die suspend-Funktionen setzen auf Kotlin-Coroutines, die sich auf beiden Seiten sauber auf async/await abbilden lassen.

commonMain/kotlin/data/InterviewRepository.ktkotlin
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()
}

Dieses Repository, seine Retry-Logik und seine Datenmodelle werden einmal geschrieben. Ein im Parser behobener Fehler ist auf Android und iOS gleichzeitig behoben, und genau hier zahlt sich der Einrichtungsaufwand von KMP aus. Dasselbe Muster erstreckt sich auf eine gemeinsame Datenbank mit SQLDelight, geteilten Key-Value-Speicher und geteilte Dependency Injection mit Koin, sodass die gesamte Datenschicht in commonMain hinter Schnittstellen liegen kann, von denen die UI abhängt.

Ein häufiger Fehler ist der Versuch, zu früh zu viel zu teilen. Der pragmatische Weg besteht darin, mit einem Repository zu beginnen, die Build- und CI-Pipeline auf beiden Plattformen abzusichern und dann mit wachsendem Vertrauen Funktion für Funktion zu verschieben.

Bereit für deine Android-Interviews?

Übe mit unseren interaktiven Simulatoren, Flashcards und technischen Tests.

Geteilte UI mit Compose Multiplatform bauen

Compose Multiplatform erreichte im Mai 2025 den stabilen Status auf iOS (Version 1.8.0) und lag Mitte 2026 bei 1.11.0, mit nativer Texteingabe und standardmäßig aktivem Concurrent Rendering. Ein @Composable in commonMain rendert auf beiden Plattformen, und geteilte View-Models passen natürlich zu einer MVVM- oder MVI-Architektur.

commonMain/kotlin/ui/QuestionList.ktkotlin
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))
            }
        }
    }
}

Das Teilen der UI ist optional und erfolgt pro Bildschirm. Ein Team kann die gesamte Oberfläche mit Compose Multiplatform teilen, für die gesamte App bei SwiftUI bleiben und nur die Logik teilen oder beides mischen. Bildschirme, die von plattformspezifischer UX abhängen, etwa Widgets oder App Clips, bleiben meist nativ.

Geteilten Code aus Swift auf iOS nutzen

Das gemeinsame Modul kompiliert zu einem Framework, das Swift wie jede andere Bibliothek importiert. Der historische Reibungspunkt waren Kotlin-suspend-Funktionen und Flow, die als umständliche Callback-APIs auftauchten. SKIE von Touchlab löst das, indem es idiomatisches Swift generiert: suspend wird zu nativem async/await und Flow wird zu AsyncSequence.

iosApp/ContentView.swiftswift
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")) ?? []
        }
    }
}

Das iOS-Team schreibt gewöhnliches Swift und SwiftUI. Es nutzt geteilte Modelle und Repositories genauso wie ein natives Swift-Package, wodurch KMP für den Großteil der iOS-Codebasis unsichtbar bleibt.

Zwei Schwachstellen sind in einem Interview weiterhin erwähnenswert. Erstens die Build-Zeiten: Die Kotlin/Native-Kompilierung ist langsamer als ein reiner Swift-Build, auch wenn der Gradle-Build-Cache und die Verteilung eines vorgefertigten XCFramework die täglichen Kosten abmildern. Zweitens verbessert sich das Debugging über die Grenze zwischen Swift und Kotlin, ist aber noch nicht so nahtlos wie das Bleiben in einer einzigen Sprache, was ein weiterer Grund ist, warum Teams die geteilte Oberfläche bewusst begrenzen, statt sie ausufern zu lassen.

Kotlin-Multiplatform-Interviewfragen, mit denen zu rechnen ist

Da KMP-Interviewfragen zunehmend in Android- und Mobile-Rollen auftauchen, lohnt es sich, einige wiederkehrende Fragen zusammen mit einer breiteren Android-Interviewvorbereitung zu üben:

  • Wie unterscheidet sich expect/actual von einer Schnittstelle? expect/actual wird zur Kompilierzeit pro Ziel aufgelöst, ohne Laufzeit-Dispatch, und kann eine Klasse, Funktion, Property oder ein Typealias hinterlegen. Eine Schnittstelle wird dynamisch zur Laufzeit aufgelöst. expect/actual eignet sich für Plattform-APIs, Schnittstellen für Polymorphie innerhalb des geteilten Codes.
  • Wie handhabt Kotlin/Native Speicher und Threading? Seit Kotlin 1.7.20 hat der neue Speichermanager das alte Object-Freezing-Modell abgelöst, sodass geteilter veränderlicher State und Coroutines über Threads hinweg weitgehend so funktionieren wie auf der JVM.
  • Kann eine bestehende App KMP schrittweise einführen? Ja. Das gemeinsame Modul wird als AAR für Android und als XCFramework für iOS ausgeliefert, sodass ein Repository oder eine Funktion nach der anderen migriert werden kann, ohne ein Rewrite.
  • Wann sollte die UI nativ bleiben, statt Compose Multiplatform zu nutzen? Wenn ein Bildschirm auf plattformspezifische UX setzt oder wenn das iOS-Team die Präsentationsschicht besitzt und lieber selbst kontrolliert.
Toolchain im Jahr 2026

Als Zielsetzung dienen Kotlin 2.3 mit dem K2-Compiler, Ktor 3.x für Networking, SQLDelight für eine gemeinsame Datenbank und Koin für Dependency Injection. Für die iOS-Interoperabilität sollte SKIE früh eingebunden werden; es nachträglich einzubauen, nachdem die Swift-Schicht geschrieben ist, ist deutlich schmerzhafter.

Fazit

Kotlin Multiplatform ist 2026 ein pragmatischer Weg, doppelte Logik zwischen Android und iOS zu reduzieren, ohne native UI oder Performance aufzugeben:

  • Die Logik teilen, die eine einzige Quelle der Wahrheit hat (Modelle, Networking, Validierung), und pro Bildschirm zwischen nativer oder Compose-UI wählen.
  • expect/actual für Plattform-APIs nutzen; es wird zur Kompilierzeit aufgelöst, ohne Laufzeit-Overhead.
  • Networking einmal mit Ktor und kotlinx.serialization schreiben, sodass Korrekturen auf beiden Plattformen gleichzeitig ankommen.
  • Schrittweise über ein AAR und ein XCFramework einführen, statt sich auf ein Rewrite festzulegen.
  • SKIE von Anfang an einbinden, damit Swift suspend und Flow als natives async/await und AsyncSequence nutzt.

Fang an zu üben!

Teste dein Wissen mit unseren Interview-Simulatoren und technischen Tests.

Tags

#Kotlin Multiplatform
#KMP
#Android
#iOS
#Cross-platform

Teilen

Verwandte Artikel