Multiplattform-Entwicklung für Android und Web? Kotlin macht‘s möglich!

Seite 2: KMPs Modulstruktur und expect/actual-Funktionalität

Inhaltsverzeichnis

Das Herzstück jeder KMP-Anwendung ist das Common-Modul. Es beherbergt den Code, der plattformübergreifend verwendet wird – beispielsweise Datenmodelle, Netzwerklogik, ViewModels und Use Cases (Abbildung 2).

Beispielhaftes Architekturdiagramm einer KMP-Anwendung für Android und Web (Abb. 2)

Innerhalb des Common-Moduls befinden sich Unterordner. commonMain enthält die Definition plattformunabhängigem Codes. Um plattformspezifische Anpassungen vorzunehmen, ohne die gemeinsame Logik zu beeinträchtigen, sind zwei weitere Unterordner vorgesehen: jsMain für das Web und androidMain für Android.

Diese Struktur ermöglicht den Einsatz der expect/actual-Funktionalität (Abbildung 3). Das bedeutet, dass Entwicklerinnen und Entwickler in commonMain generelle Erwartungen (expects) bezüglich des Verhaltens setzen, und in den spezifischen Ordnern (jsMain und androidMain) wird dann die tatsächliche (actual) Umsetzung dieser Erwartungen vorgenommen. Man kann sich das vorstellen wie ein Interface, das sich implementieren lässt.

Expect-/actual-Mechanismus zur Nutzung des lokalen Speichers (Abb. 3)

Ein praxisnahes Beispiel ist das Speichern von Daten. Während Key-Value-Paare im Web oft in localStorage des Browsers zu finden sind, kommen dafür in Android die SharedPreferences zum Einsatz. Kein Grund, die gesamte Logik zum Ablegen dieser Daten separat zu implementieren. commonMain enthält die Definition einer expect-Klasse oder -Funktion und der Compiler erwartet die entsprechende actual-Implementierung in androidMain und jsMain.

Beim Ausführen der Anwendung wird dann zur Laufzeit ermittelt, welches Verhalten für die jeweilige Plattform gelten soll.

Zusätzlich zum Common-Modul gibt es die plattformspezifischen Module androidApp und webApp. Darin implementiert man alles, was tatsächlich nur für die jeweilige Plattform nötig ist, wie das Frontend und die Navigationslogik.

Code zu teilen, verringert den Wartungsaufwand und reduziert Entwicklungskosten. Diese Hoffnung erfüllt sich spätestens dann nicht, wenn das Multiplattform-Framework spezielle plattformspezifische APIs nicht unterstützt. KMP bringt mit seinem flexiblen Ansatz frischen Wind in die Betrachtung. Entwicklerinnen und Entwickler können projektspezifisch entscheiden, welche Elemente des Codes sie teilen möchten. Das ist besonders praktisch, wenn sich die Zielplattformen so gravierend unterscheiden wie Android und Web.

Das wachsende Ökosystem und die begeisterte Community um KMP ermöglichen es, dass sich trotz der großen Unterschiede viele Komponenten und Funktionen teilen lassen. Denn die meisten plattformspezifischen Probleme lassen sich mit geeigneten Bibliotheken lösen.

Beim plattformübergreifenden Implementieren der Netzwerklogik kann JetBrains' quelloffene Kotlin-Bibliothek Ktor helfen. Eine plattformunabhängige lokale Datenbank zu implementieren, stellt dank SQLDelight keine Hürde dar. Dependency Injection für Android und Web? Kodein (Kotlin Dependency Injection) macht’s möglich! Und selbst die Android-Entwicklern vertrauten ViewModels lassen sich dank kmp-viewmodel plattformübergreifend nutzen.

Für all die, die jetzt die Nase rümpfen, bei dem Gedanken Model-View-ViewModel (MVVM) im Web zu nutzen, gibt es natürlich auch andere Möglichkeiten, wie Model-View-Intent (MVI).

Sich zu sehr auf verschiedene, mitunter kleine Bibliotheken zu verlassen, birgt natürlich auch Risiken, wie Wartungsunsicherheit, Kompatibilitätsprobleme und potenzielle Sicherheitslücken. Hier bedarf es sinnvollen Abwägens, wann eine Bibliothek und wann lieber das expect/actual-Konzept zum Einsatz kommen sollte. Außerdem ist es ratsam, die Zukunftsfähigkeit der genutzten Bibliotheken im Blick zu behalten. Sonst kommt es im Projektverlauf möglicherweise zu bösen Überraschungen, wenn man eine weitere Plattform ergänzen möchte.