Wenn man sich in die Mobile-Entwicklung einarbeitet, wird die Zahl der Frameworks und Ansätze schnell verwirrend. Man hört von nativen Apps, React Native, Flutter, Expo, Capacitor, Cordova, Ionic, Tauri Mobile, und es ist nicht immer klar, was sie voneinander unterscheidet. Machen sie alle dasselbe auf verschiedene Weise, oder sind einige von ihnen im Kern unterschiedliche Architekturen?
Die Antwort ist beides. Es gibt drei Hauptwege, eine Mobile-App zu bauen, und die meisten Frameworks, die man hört, fallen in einen davon. Die Unterschiede liegen darin, was die UI rendert, was die Logik ausführt und wie die beiden miteinander kommunizieren.
Die drei Ansätze
Hier ist die Landschaft:
| Ansatz | UI wird gerendert mit | Logik läuft in | Beispiele |
|---|---|---|---|
| Native | Plattform-UI-Komponenten | Plattform-Sprache | Swift/Kotlin-Apps |
| Web-Wrapper | WebView (Browser) | JavaScript | Capacitor, Cordova, Ionic |
| Cross-Platform | Variiert (nativ, Canvas oder WebView) | JS, Dart oder Rust | React Native, Flutter, Tauri |
Das Erste, was auffällt, ist, dass "Cross-Platform" nicht ein Ansatz ist. Es ist eine Kategorie, und innerhalb davon ist die UI-Rendering-Strategie für jedes Framework unterschiedlich. Daher kommt die meiste Verwirrung. Leute hören "Cross-Platform" und gehen davon aus, es bedeute eine Sache. Tut es nicht.
Gehen wir jeden durch.
Native Apps
Das ist die Grundlinie. Man schreibt eine iOS-App in Swift oder Objective-C, mit UIKit oder SwiftUI. Man schreibt eine Android-App in Kotlin oder Java, mit Android Views oder Jetpack Compose. Der Code kompiliert zum nativen Binär der Plattform, die UI sind die eigenen UI-Komponenten der Plattform, und die Logik läuft direkt auf der Plattform-Runtime.
iOS: Swift/SwiftUI → kompiliert → UIKit/SwiftUI rendert auf den Bildschirm
Android: Kotlin/Compose → kompiliert → Android Views/Compose rendert auf den BildschirmDer Vorteil ist voller Zugriff auf alles, was die Plattform bietet. Jede API, jede UI-Komponente, jedes plattformspezifische Feature, jede Performance-Optimierung. Es gibt keine Bridge, keine Übersetzungsschicht, keine Abstraktion. Der Code, den man schreibt, ist der Code, der läuft.
Der Preis ist, dass man die App zweimal schreibt. Eine iOS-App und eine Android-App sind zwei komplett separate Codebasen, in zwei verschiedenen Sprachen, mit zwei verschiedenen UI-Frameworks, zwei verschiedenen Build-Systemen und zwei verschiedenen Deployment-Targets. Wenn man ein Feature auf beiden Plattformen will, baut man es zweimal.
Daher kommen die anderen zwei Ansätze. Der Web-Wrapper und die Cross-Platform-Kategorie existieren beide, weil alles zweimal zu schreiben teuer ist.
Web-Wrapper
Der Web-Wrapper-Ansatz nimmt eine Web-App und packt sie in eine native Hülle. Capacitor (von Ionic) und Cordova sind die Haupt-Frameworks hier.
HTML/CSS/JS → WebView → native Hülle → OSDie UI ist ein WebView. Das ist eine Browser-Engine, dieselbe, die das System nutzt, wenn man einen Link innerhalb einer App öffnet. Auf iOS ist es WKWebView, auf Android ist es der Android WebView. Das JavaScript läuft in diesem WebView, genauso wie es in einem Browser-Tab läuft. Die native Hülle darum liefert eine Bridge, sodass das JavaScript Plattform-APIs aufrufen, auf die Kamera zugreifen, das Dateisystem lesen, native Benachrichtigungen anzeigen und so weiter kann.
Wenn man die Tauri-Einführung gelesen hat, sollte diese Struktur vertraut aussehen. Tauri auf dem Desktop nutzt dasselbe Muster: Web-UI in einem nativen Fenster, mit einer Bridge zum OS. Der Unterschied ist das Backend. Capacitor und Cordova bridgen zur nativen Sprache der Plattform (Swift oder Kotlin), die pro Plattform geschrieben wird. Tauri bridgt zu Rust, das nativ für jedes Mobile-Target kompiliert wird.
Der Reiz des Web-Wrappers ist offensichtlich. Wenn man bereits eine Web-App hat, kann man sie als Mobile-App ausliefern, ohne die UI neu zu schreiben. Dasselbe HTML, CSS und JavaScript, das in einem Browser läuft, läuft im WebView. Man schreibt ein Frontend, und es funktioniert überall.
Der Kompromiss ist Performance und natives Gefühl. Ein WebView ist ein Browser. Es rendert HTML auf einen Bildschirm, auf dieselbe Weise wie ein Browser. Native UI-Komponenten sind schneller, flüssiger und fühlen sich nach der Plattform an, auf eine Weise, die ein WebView nicht immer erreicht. Scroll-Physik, Gesten-Handling, Übergänge und Input-Verhalten sind in einem WebView subtil anders als in einer nativen UI. Für manche Apps fällt das nicht ins Gewicht. Für andere ist es das ganze Spiel.
Cross-Platform-Frameworks
Hier wird es interessant, denn "Cross-Platform" bedeutet für verschiedene Frameworks unterschiedliche Dinge. Jedes wählt eine andere UI-Rendering-Strategie und einen anderen Bridge-Mechanismus.
React Native
React Native rendert nicht in einen WebView. Die React-Komponenten beschreiben eine native UI, und React Native erzeugt tatsächliche native iOS- und Android-Views. Ein View wird auf iOS zu einem UIView und auf Android zu einem android.view.View. Es gibt keinen Browser, kein HTML, kein DOM.
React-Komponenten → JSI-Bridge → native UI-Komponenten → BildschirmDie Logik läuft in JavaScript, auf einer JavaScript-Engine, die in die App eingebettet ist. Die Bridge zwischen JavaScript und nativ heißt JSI (JavaScript Interface). Sie erlaubt JavaScript, direkt in native C++-Objekte aufzurufen, was schneller ist als die ältere Bridge, die alles zu JSON serialisierte.
Wenn man den vollständigen Deep Dive zu React Native und Expo will, habe ich darüber hier geschrieben. Die kurze Version für diesen Post: Die UI ist nativ, die Logik ist JavaScript, und die Bridge ist JSI.
Flutter
Flutter geht einen anderen Weg für die UI. Es nutzt keine nativen UI-Komponenten und keinen WebView. Flutter hat seine eigene Rendering-Engine, Impeller genannt (früher Skia), die direkt auf einen Canvas zeichnet. Jeder Pixel auf dem Bildschirm wird von Flutters eigener Engine gemalt.
Flutter-Widgets → Dart-Runtime → Impeller rendert auf Canvas → BildschirmDie Logik läuft in Dart, das zu nativem ARM-Code für die Zielplattform kompiliert. Die Bridge zwischen Dart und Plattform-APIs heißt Platform Channels. Wenn Flutter eine Plattform-API aufrufen muss, etwa die Kamera oder das Dateisystem, sendet es eine Nachricht durch einen Platform Channel an nativen Code, der die Anfrage behandelt.
Der Reiz ist Konsistenz. Weil Flutter die eigene UI zeichnet, sieht und verhält sich eine Flutter-App auf iOS und Android identisch. Es gibt kein "das sieht aus wie eine Android-App auf iOS"-Problem, weil die UI keine Komponenten beider Plattformen nutzt. Der Kompromiss ist, dass eine Flutter-App nicht automatisch das Aussehen und Gefühl der Plattform übernimmt. Will man, dass es sich nativ anfühlt, muss man das selbst bauen.
Tauri Mobile
Tauri Mobile ist das neueste der drei und kombiniert den UI-Ansatz des Web-Wrappers mit einem Rust-Backend statt eines Backend in einer nativen Sprache.
HTML/CSS/JS → WebView → Tauri IPC → Rust → OSDie UI ist ein WebView, genauso wie bei Capacitor und Cordova. Der Unterschied ist das Backend. Anstatt zu Swift- oder Kotlin-Code zu bridgen, der pro Plattform geschrieben wird, bridgt Tauri zu Rust, das nativ für jedes Mobile-Target kompiliert wird. Eine Rust-Codebasis handhabt die native Seite für sowohl iOS als auch Android.
Hier wird das One-Codebase-Muster nützlich. Dieselbe Interface-und-Implementierungs-Strategie, die es einer Tauri-Desktop-App ermöglicht, Code mit einem Web-Frontend zu teilen, gilt auch für Mobile. Die Service-Interfaces bleiben gleich, und man fügt eine Mobile-Implementierung neben der Web- und Desktop-Implementierung hinzu.
Das gemeinsame Muster
Hier ist der Teil, der für mich die ganze Landschaft zusammenbrachte. Alle drei Cross-Platform-Frameworks folgen derselben Architektur:
UI
|
Bridge
|
EngineDie UI ist die Präsentationsschicht. Die Bridge ist der Kommunikationsmechanismus. Die Engine ist die Runtime, die die eigentliche Arbeit erledigt. Die Technologie ändert sich, aber die Form bleibt gleich.
| Framework | UI | Bridge | Engine |
|---|---|---|---|
| React Native | Native Komponenten | JSI | JavaScript-Engine |
| Flutter | Impeller-Canvas | Platform Channels | Dart-Runtime |
| Tauri Mobile | WebView | IPC | Rust |
Sobald man dieses Muster sieht, hören die Unterschiede zwischen den Frameworks auf, verwirrend zu sein. Sie sind alle Variationen derselben Form. React Native wählt native UI und JSI. Flutter wählt den eigenen Canvas und Platform Channels. Tauri wählt einen WebView und IPC zu Rust. Die Wahl dreht sich darum, welche Kompromisse man für die UI-Schicht akzeptiert und welchen Bridge-Mechanismus man nutzen will, um die Plattform zu erreichen.
Wie man wählt
Die Frage ist nicht, welches Framework das Beste ist. Die Frage ist, welche Kompromisse zur eigenen App passen.
Native (Swift/Kotlin), wenn man vollen Plattform-Zugriff, maximale Performance oder eine UI braucht, die sich auf jeder Plattform komplett nativ anfühlt. Der Preis ist, zwei Codebasen zu pflegen.
React Native, wenn man native UI-Komponenten will, die von JavaScript angetrieben werden, und man bereits ein React-Team hat oder Logik mit einer React-Web-App teilen will. Die UI ist nativ, die Logik ist JavaScript, und man schreibt eine Codebase, die beide Plattformen ansteuert.
Flutter, wenn man pixelgenaue Konsistenz über Plattformen hinweg will und bereit ist, das native Aussehen und Gefühl selbst zu bauen. Die UI ist Flutters eigenes Rendering, die Logik ist Dart, und die App sieht auf iOS und Android standardmäßig identisch aus.
Tauri Mobile, wenn man bereits ein Web-Frontend und ein Rust-Backend hat, oder wenn man eine Tauri-Desktop-App auf Mobile erweitern will, ohne den Stack zu wechseln. Die UI ist ein WebView, das Backend ist Rust, und dieselbe Codebasis kann Desktop und Mobile ansteuern.
Web-Wrapper (Capacitor/Cordova), wenn man bereits eine Web-App hat und sie mit minimalen Änderungen auf Mobile ausliefern will, und man die WebView-Kompromisse bei Performance und nativem Gefühl akzeptieren kann.
Das größere Bild
Die meisten Cross-Platform-Frameworks und die meisten modernen App-Architekturen im Allgemeinen folgen dem UI-, Bridge-, Engine-Muster. Die UI ist, was der Nutzer sieht. Die Bridge ist, wie die UI mit der Engine spricht. Die Engine ist, was die Arbeit erledigt. Ob man eine Mobile-App, eine Desktop-App oder eine Web-App baut, diese Form taucht auf.
Die Technologie ändert sich. Die Architektur nicht. Sobald man die Form sehen kann, kann man jedes neue Framework evaluieren, indem man drei Fragen stellt: Was rendert die UI, was führt die Logik aus, und wie kommunizieren sie. Alles andere ist Detail.
Mehr lesen
Architektur für zuverlässige mobile Abrechnung: Was ich auf die harte Tour gelernt habe
Ein Praxisblick auf die Reparatur von mobiler Abonnement-Abrechnung, wenn Webhooks, Sandbox-Käufe und Benutzeridentität versagen.
React Native und Expo einfach erklärt
Verstehen Sie, wie React Native und Expo wirklich funktionieren, von nativen Builds bis hin zu OTA-Updates und Entwicklungs-Workflows.
Lernen Sie Tauri kennen [2.0]
Wie Tauri Desktop-Apps aus Web-Technologien baut, wo es die Vertrauensgrenze im Vergleich zu Electron platziert, und der Denkwechsel von einer Website im Fenster zu einer nativen Anwendung.
