Wer mit React Native beginnt, hat schnell das Gefühl, dass „es irgendwie funktioniert", ohne wirklich zu verstehen warum. In diesem Artikel erläutern wir alles, was im Hintergrund passiert – von Metro, JSI, Codegen und den Expo-Mechanismen bis hin zur nativen Implementierung für Bereiche, die das Expo SDK nicht abdecken kann.
1. Die Grundphilosophie von React Native
Der Kern von React Native liegt darin, in JS geschriebene UI-Logik in native UI-Komponenten zu übersetzen. Genauso wie ReactDOM <div> in DOM-Elemente umwandelt, ordnet React Native <View> und <Text> auf iOS-UIView und Android-android.view.View zu.
Das bedeutet: Es wird echtes natives UI generiert – nicht HTML in einer WebView gerendert. Das ist der grundlegende Unterschied zu Cordova oder Ionic.
2. Architekturentwicklung: Von Bridge zu JSI
React Native hat derzeit zwei koexistierende Architekturen.
flowchart LR
subgraph OLD["Alte Architektur (Bridge)"]
direction TB
J1["JS-Thread<br/>React-Logik"] -->|"JSON serialisieren"| BR["Bridge<br/>Asynchrone Warteschlange ⚠"]
BR -->|"JSON deserialisieren"| N1["Nativer Thread<br/>UIKit / Android View"]
SH["Shadow-Thread<br/>Yoga-Layout"] --> N1
end
subgraph NEW["Neue Architektur (JSI)"]
direction TB
HM["Hermes-Engine<br/>Bytecode-Ausführung"] -->|"Direkter C++-Verweis"| JSI["JSI<br/>Host Objects"]
JSI --> FAB["Fabric<br/>Synchroner Renderer"]
JSI --> TM["TurboModules<br/>Verzögerte Initialisierung"]
FAB --> N2["Native Schicht"]
TM --> N2
endProbleme der alten Architektur (Bridge)
Das alte React Native kommunizierte zwischen JS-Thread und nativem Thread über eine einzige asynchrone Bridge als Flaschenhals. Alle Austausche mussten als JSON-String serialisiert, gesendet und deserialisiert werden. Dieser Round-Trip führte bei häufigen Operationen wie Animationen und Gesten zu Leistungseinbußen.
Innovationen der neuen Architektur (JSI + Hermes)
JSI (JavaScript Interface) ist eine dünne Bindungsschicht in C++, die es der JS-Engine ermöglicht, native Objekte direkt und synchron zu referenzieren. Dadurch wurden folgende Verbesserungen möglich:
- Fabric — Ein neuer Renderer, der UI-Bäume synchron aufbaut und aktualisiert
- TurboModules — Verzögerte Initialisierung, die native Module erst bei Bedarf lädt
- Hermes — Facebooks Engine, die JS in Bytecode vorkompiliert und die Startzeit drastisch reduziert
3. Codegen: Der Schlüssel zur typsicheren nativen Kommunikation
Codegen ist der am häufigsten übersehene Mechanismus in der neuen Architektur. Während JSI und TurboModules Kommunikation ermöglichen, garantiert Codegen zur Build-Zeit, dass sie korrekt abläuft.
flowchart TD
SPEC["JS-Spec-Datei<br/>Typdefinitionen in TypeScript / Flow"]
subgraph BUILD["Build-Zeit"]
CG["Codegen<br/>Läuft bei pod install / Gradle"]
end
subgraph OUT["Generierte Artefakte"]
CPP["Abstrakte C++-Klassen<br/>TurboModule-Basisschnittstellen"]
DESC["Component Descriptor<br/>Für Fabric-native Komponenten"]
JSTYPE["JS-Typdefinitionen<br/>.d.ts-Äquivalent"]
end
subgraph NATIVE["Native Implementierung"]
IOS["iOS<br/>Implementiert in Swift / Obj-C"]
AND["Android<br/>Implementiert in Kotlin / Java"]
end
RUNTIME["Über JSI<br/>Typsicher, synchrone Aufrufe"]
SPEC --> CG
CG --> CPP & DESC & JSTYPE
CPP --> IOS & AND
DESC --> IOS & AND
IOS --> RUNTIME
AND --> RUNTIMEIn der alten Architektur war die Schnittstelle zwischen JS und Nativ im Wesentlichen ein „Gentleman's Agreement". Der Aufruf NativeModules.MyModule.doSomething(42) aus JS würde erst beim Laufzeit-Crash bemerkt werden, wenn die native Seite einen String erwartete.
Codegen löst dieses Problem zur Build-Zeit. Entwickler schreiben eine Spec (Spezifikation) in TypeScript oder Flow, die beschreibt, welche Typen ein natives Modul akzeptiert. Beim pod install (iOS) oder dem Gradle-Build (Android) generiert Codegen automatisch abstrakte C++-Klassen, natives Gerüst für iOS und Android und JS-Typdefinitionen.
4. Die Metro-Bundler-Pipeline
Metro ist Bundles React Natives dedizierter JavaScript-Bundler. Er erfüllt dieselbe Rolle wie webpack, ist aber für die mobile Entwicklung optimiert.
flowchart LR
SRC[".js / .ts / .tsx<br/>Quelldateien"]
RES["Auflösung<br/>Import-Pfad-Auflösung"]
TRA["Transformation<br/>ES-Transforms via Babel"]
SER["Serialisierung<br/>Bundle-Generierung"]
DEV["Auslieferung ans Gerät<br/>HTTP :8081"]
HMR["HMR<br/>Nur geänderte Module neu senden"]
CACHE["FS-Cache<br/>Ab dem 2. Lauf schneller"]
SRC --> RES --> TRA --> SER --> DEV
TRA -.->|"Dateiänderung erkannt"| HMR
HMR -.->|"Ohne State-Verlust reflektieren"| DEV
TRA <-.->|"Transformierte Ergebnisse cachen"| CACHEMetros Pipeline besteht aus drei Phasen:
- Auflösung — Node.js-ähnliche Modulauflösung mit automatischer plattformspezifischer Dateiauswahl (
foo.ios.ts/foo.android.ts) - Transformation — Babel konvertiert JSX, TypeScript und modernes ES; Ergebnisse werden im Cache gespeichert, sodass nachfolgende Starts schneller sind
- Serialisierung — Generiert das finale Bundle und liefert es über einen HTTP-Server an das Gerät
Besonders hervorzuheben ist HMR (Hot Module Replacement). Nur der Graph der geänderten Module wird aktualisiert, sodass Codeänderungen sofort sichtbar werden, während der React-Komponentenzustand erhalten bleibt. Produktions-Builds kombinieren --minify mit der Hermes-Bytecode-Kompilierung und reduzieren so sowohl Dateigröße als auch Startzeit.
5. Expos Schichtenstruktur und Entwicklungswerkzeuge
flowchart TD
APP["Ihre App<br/>App.tsx / screens / hooks"]
SDK["Expo SDK<br/>expo-camera / expo-location / expo-router …"]
EMC["expo-modules-core<br/>Typsicheres DSL in Swift / Kotlin"]
RN["React Native Core<br/>View / Text / Animated / StyleSheet"]
IOS["iOS<br/>UIKit / AVFoundation / CoreLocation"]
AND["Android<br/>View / Camera2 / LocationManager"]
GO["Expo Go<br/>Sofort per QR-Scan starten"]
EASB["EAS Build<br/>.ipa / .apk in der Cloud generieren"]
EASU["EAS Update<br/>Nur JS-Bundle per OTA ausliefern"]
STORE["App Store / Google Play"]
APP --> SDK
EMC --> SDK
SDK --> RN
RN --> IOS & AND
GO -->|"Während der Entwicklung hosten"| APP
EASB --> STORE
EASU -->|"Ohne App-Store-Review"| APPDas Expo SDK stellt native Funktionen – Kamera, Standort, Benachrichtigungen, Dateisystem – als versionierte Pakete bereit. Kein eigener nativer Code nötig – das ist der größte Vorteil.
expo-modules-core ermöglicht die deklarative Entwicklung nativer Module mit Swift- und Kotlin-DSLs, integriert mit Codegen. Es ist typsicherer und weniger ausführlich als die alte NativeModules-Bridge, und fast alle Drittanbieter-SDKs von Expo nutzen es.
EAS (Expo Application Services) ist ein Sammelbegriff für Build, Update, Submit, Workflows und mehr. Dieser Artikel konzentriert sich auf EAS Build und EAS Update. EAS Build kompiliert native Binaries in der Cloud und ermöglicht iOS-Builds ohne Mac. EAS Update liefert JS-Bundles und Assets per OTA und ermöglicht schnelle Auslieferung von Änderungen, die keinen nativen Code berühren.
6. Bereiche, die Expo nicht abdeckt, und die Workflow-Wahl
Hauptbereiche, die das Expo SDK nicht abdeckt:
- Bluetooth LE / NFC — Plattformspezifische Peripherie-Kommunikation
- Eigene Payment-/Biometrie-SDKs — Direktintegration von Hersteller-
.framework/.aar - Echtzeit-Audio/Video-Verarbeitung — Low-Level-WebRTC oder benutzerdefinierte Codec-Operationen
- Interne native Bibliotheken — Swift / Kotlin-Assets aus bestehenden Apps
- Benutzerdefinierte Kamera-Views — Benutzerdefiniertes Rendering mit OpenGL / Metal
flowchart TD
START["Welche native Funktion brauchen Sie?"]
START --> Q1{"Durch Expo SDK /<br/>öffentliche Bibliothek abgedeckt?"}
Q1 -->|"Ja"| MANAGED["Managed Workflow<br/>Kein nativer Code nötig<br/>Nur npx expo start"]
Q1 -->|"Nein"| Q2{"Nur Konfigurationsänderungen nötig?<br/>(Info.plist / AndroidManifest-Ergänzungen)"}
Q2 -->|"Ja"| PLUGIN["Config Plugin erstellen<br/>In app.json eintragen<br/>Automatisch per prebuild angewendet"]
Q2 -->|"Nein"| Q3{"Umfang des nativen Codes?"}
Q3 -->|"Leichtgewichtig<br/>(vorhandenes SDK wrappen)"| BARE["Bare Workflow<br/>npx expo prebuild<br/>→ ios/ android/ direkt verwalten"]
Q3 -->|"Umfangreich<br/>(eigenes Modul / UI)"| MODULE{"Was wird gebaut?"}
MODULE -->|"Logik<br/>Bluetooth / Payment / Krypto"| TM["TurboModule<br/>+ Codegen"]
MODULE -->|"UI-Komponente<br/>Benutzerdefinierte Kamera-View"| FAB["Fabric Component<br/>+ Codegen"]
BARE --> TM & FAB
PLUGIN --> BAREGeringfügige Konfigurationsänderungen können mit Config Plugins abgedeckt werden. Falls Code geschrieben werden muss, ist der Wechsel zum Bare Workflow durch Generierung der ios/- und android/-Verzeichnisse mit npx expo prebuild der praktische Ausgangspunkt.
7. Gesamtarchitektur: JSI + Codegen + Native Implementierung
flowchart TB
subgraph JS["JS-Schicht (Hermes)"]
APP["App-Code / React-Komponenten"]
SPEC["JS Spec<br/>(TypeScript + TurboModuleRegistry)"]
end
subgraph CODEGEN["Codegen (Automatisch zur Build-Zeit generiert)"]
GEN_CPP["Abstrakte C++-Klassen<br/>TurboModule / ComponentDescriptor"]
GEN_SHADOW["ShadowNode / Props-Definitionen"]
end
subgraph JSI_LAYER["JSI-Schicht (C++)"]
JSI["JSI Host Object"]
FABRIC["Fabric Renderer"]
end
subgraph NATIVE_IOS["iOS Native Implementierung"]
SWIFT["Swift / Obj-C Implementierung<br/>(Erbt abstrakte C++-Klasse)"]
SDK_IOS["Internes SDK / Externes .framework<br/>z.B. CoreBluetooth / Stripe"]
UIVIEW["UIView-Unterklasse<br/>(Benutzerdefinierte UI-Komponente)"]
end
subgraph NATIVE_AND["Android Native Implementierung"]
KOTLIN["Kotlin / Java Implementierung<br/>(Erbt abstrakte C++-Klasse)"]
SDK_AND["Internes SDK / Externes .aar<br/>z.B. BluetoothGatt / Stripe"]
ANDROIDVIEW["View-Unterklasse<br/>(Benutzerdefinierte UI-Komponente)"]
end
SPEC -->|"Typdefinitionen lesen"| CODEGEN
GEN_CPP --> JSI
GEN_SHADOW --> FABRIC
APP -->|"Synchrone / asynchrone Aufrufe"| JSI
JSI --> SWIFT & KOTLIN
FABRIC --> UIVIEW & ANDROIDVIEW
SWIFT --> SDK_IOS
KOTLIN --> SDK_ANDDer Schlüssel liegt in der einseitigen Abhängigkeit: Spec → Codegen → Native Implementierung. Schreiben Sie Typdefinitionen auf der JS-Seite, generiert Codegen zur Build-Zeit abstrakte C++-Klassen. Swift / Kotlin-Implementierungen müssen diese nur erben – die Typkonsistenz wird vom Compiler garantiert und das „Gentleman's Agreement" als Ursache von Laufzeit-Crashes verschwindet.
8. TurboModule-Implementierungsfluss (Bluetooth-Beispiel)
Hinweis: Der folgende Code ist ein vereinfachtes Beispiel basierend auf den offiziellen React Native TurboModule / Codegen / Custom Events-Anleitungen. Es handelt sich nicht um direkte Zitate der offiziellen Beispiele, sondern um eine Neukonstruktion mit Bluetooth als Thema.
① JS Spec schreiben
// src/specs/NativeBluetoothModule.ts
import type { TurboModule, CodegenTypes } from 'react-native';
import { TurboModuleRegistry } from 'react-native';
export type ScanResult = {
id: string;
name: string;
rssi: number;
};
export interface Spec extends TurboModule {
startScan(serviceUUIDs: string[]): void;
stopScan(): void;
connect(deviceId: string): Promise<boolean>;
readonly onDeviceFound: CodegenTypes.EventEmitter<ScanResult>;
}
export default TurboModuleRegistry.getEnforcing<Spec>('NativeBluetoothModule');
② Was Codegen generiert (Automatisch)
flowchart LR
SPEC["NativeBluetoothModule.ts<br/>(Vom Entwickler geschrieben)"]
subgraph AUTO["Automatisch generierter Code"]
CPP["C++ Glue Code<br/>JSI / Codegen-Ausgabe"]
IOS_H["iOS Glue Code<br/>Obj-C++ / Header-Ausgabe"]
AND_J["Android Glue Code<br/>JNI / Spec-Klassen-Ausgabe"]
end
subgraph IMPL["Vom Entwickler implementiert"]
SWIFT_IMPL["NativeBluetoothModule.swift<br/>Implementiert generierte Spec, ruft CoreBluetooth auf"]
KOTLIN_IMPL["NativeBluetoothModule.kt<br/>Implementiert generierte Spec, ruft BluetoothGatt auf"]
end
SPEC --> CPP
SPEC --> IOS_H
SPEC --> AND_J
CPP --> SWIFT_IMPL & KOTLIN_IMPL
IOS_H --> SWIFT_IMPL
AND_J --> KOTLIN_IMPL③ iOS-Implementierung (Swift)
// ios/NativeBluetoothModule.swift
import CoreBluetooth
@objc(NativeBluetoothModule)
class NativeBluetoothModule: NativeBluetoothModuleSpec, CBCentralManagerDelegate {
private var centralManager: CBCentralManager!
override func startScan(_ serviceUUIDs: [String]) {
let uuids = serviceUUIDs.map { CBUUID(string: $0) }
centralManager.scanForPeripherals(withServices: uuids)
}
func centralManager(_ central: CBCentralManager,
didDiscover peripheral: CBPeripheral,
advertisementData: [String: Any], rssi: NSNumber) {
emitOnDeviceFound([
"id": peripheral.identifier.uuidString,
"name": peripheral.name ?? "",
"rssi": rssi
])
}
}
④ Android-Implementierung (Kotlin)
// android/src/main/java/NativeBluetoothModule.kt
class NativeBluetoothModule(reactContext: ReactApplicationContext)
: NativeBluetoothModuleSpec(reactContext), BluetoothGatt.Callback {
private val adapter = BluetoothAdapter.getDefaultAdapter()
override fun startScan(serviceUUIDs: ReadableArray) {
val filters = serviceUUIDs.toArrayList().map { uuid ->
ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString(uuid as String)).build()
}
adapter.bluetoothLeScanner.startScan(filters, ScanSettings.Builder().build(), scanCallback)
}
private val scanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
val params = Arguments.createMap().apply {
putString("id", result.device.address)
putString("name", result.device.name ?: "")
putInt("rssi", result.rssi)
}
emitOnDeviceFound(params)
}
}
}
9. Fabric Component Implementierungsfluss (Benutzerdefiniertes Kamera-View-Beispiel)
Wenn TurboModule die Logik übernimmt, dient Fabric Component dazu, natives UI in JSX einzubetten. Das wird benötigt für benutzerdefiniertes Rendering mit OpenGL / Metal oder die Wiederverwendung bestehender nativer Views.
Hinweis: Das Folgende ist auch ein minimales Verständnisbeispiel. Codegen-Import-Pfade und Typnamen variieren je nach React Native-Version, daher die offizielle Dokumentation für die verwendete Version prüfen.
// src/specs/NativeCameraViewSpec.ts
import type { ViewProps } from 'react-native';
import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
import type { DirectEventHandler, Float } from 'react-native/Libraries/Types/CodegenTypes';
type FrameCapturedEvent = Readonly<{
uri: string;
}>;
interface NativeProps extends ViewProps {
zoom?: Float; // Codegen konvertiert Float → CGFloat / float
torchEnabled?: boolean;
onFrameCaptured?: DirectEventHandler<FrameCapturedEvent>;
}
export default codegenNativeComponent<NativeProps>('CameraView');
Codegen generiert aus dieser Spec C++-Definitionen für ShadowNode, ComponentDescriptor und Props. Auf iOS implementieren Sie eine CameraViewComponentView die RCTViewComponentView erbt, auf Android erbt sie von ReactViewGroup. Von der JS-Seite aus ist es so einfach wie <CameraView zoom={2.0} torchEnabled />.
10. Integration interner SDKs / externer .framework und .aar
iOS: Lokale Referenz über CocoaPods
# ios/Podfile
pod 'CompanyPaymentSDK', :path => '../vendor/ios/CompanyPaymentSDK'
# oder für xcframework
pod 'CompanyPaymentSDK', :podspec => '../vendor/ios/CompanyPaymentSDK.podspec'
Nach dem Hinzufügen zur Podfile und dem Ausführen von pod install kann es als normale Swift-Klasse mit import CompanyPaymentSDK verwendet werden. Direkt aus der TurboModule-Implementierung aufrufen.
Android: Lokales Maven / .aar-Referenz
// android/app/build.gradle
dependencies {
implementation files('../vendor/android/company-payment-sdk.aar')
// oder ein internes Maven-Repository
implementation 'com.company:payment-sdk:1.4.0'
}
11. Die Entwicklungsschleife: Von Start bis Verifikation
flowchart TD
INIT["npx create-expo-app MyApp<br/>--template blank-typescript"]
subgraph START["npx expo start"]
METRO["Metro Bundler startet<br/>Port 8081"]
QR["QR-Code + Shortcuts<br/>i = iOS a = Android w = Web"]
end
subgraph DEVICE["Zielgerät wählen"]
SIM["iOS Simulator<br/>In Xcode enthalten (nur Mac)"]
EMU["Android Emulator<br/>Android Studio AVD"]
GO["Physisches Gerät + Expo Go<br/>Über LAN oder Tunnel"]
end
subgraph NATIVE_BUILD["Nur beim ersten Mal: Nativer Build"]
POD["pod install<br/>iOS CocoaPods"]
GRADLE["Gradle Build<br/>Android"]
CODEGEN_RUN["Codegen läuft<br/>Spec → Natives Gerüst"]
end
BUNDLE["JS Bundle ans Gerät übertragen<br/>über Metro HTTP-Server"]
RENDER["Bildschirm wird angezeigt"]
INIT --> START
METRO --> QR
QR --> SIM & EMU & GO
SIM --> NATIVE_BUILD
EMU --> NATIVE_BUILD
NATIVE_BUILD --> BUNDLE
GO --> BUNDLE
BUNDLE --> RENDERMit npx expo start wird Metro gestartet, und im Terminal erscheint ein QR-Code mit Tastenkombinationen.
i-Taste → iOS Simulator (läuft auf Mac mit installiertem Xcode)a-Taste → Android Emulator (virtuelles Gerät im Android Studio AVD Manager erstellt)- Für physische Geräte den QR-Code in Expo Go scannen – verbindet sich im LAN fast sofort
Beim expliziten Auslösen eines nativen Builds mit npx expo run:ios oder npx expo run:android läuft beim ersten Mal pod install (iOS) oder Gradle (Android), und Codegen generiert das native Gerüst. Spätere Läufe übertragen nur JS-Bundle-Differenzen und sind deutlich schneller.
12. Die Änderungsschleife im Bare Workflow
flowchart TD
subgraph CHANGE["Verzweigung nach Änderungstyp"]
JS_ONLY["Nur JS-Änderungen<br/>Komponenten / Logik / Styles"]
NATIVE_CHANGE["Native Code-Änderungen<br/>Swift / Kotlin / Spec / Podfile"]
end
subgraph JS_LOOP["JS-Änderungsschleife (Schnell)"]
METRO_HMR["Metro HMR<br/>Differenz-Bundle übertragen<br/>~Hunderte ms"]
SIM_JS["Sofort im Emulator sichtbar<br/>App-Zustand erhalten"]
end
subgraph NATIVE_LOOP["Native Änderungsschleife (Langsam)"]
CODEGEN_RUN["Codegen erneut ausführen<br/>pod install (iOS)<br/>Gradle sync (Android)"]
REBUILD["Nativer Rebuild<br/>npx expo run:ios<br/>npx expo run:android"]
SIM_NATIVE["Auf Emulator / Gerät überprüfen"]
end
JS_ONLY --> METRO_HMR --> SIM_JS
NATIVE_CHANGE --> CODEGEN_RUN --> REBUILD --> SIM_NATIVE
SIM_JS -->|"Keine Probleme"| DONE["Verifikation OK"]
SIM_NATIVE -->|"Keine Probleme"| DONE
SIM_JS -->|"Fehler gefunden"| DEBUG_JS["Hermes Debugger<br/>React DevTools"]
SIM_NATIVE -->|"Nativer Crash"| DEBUG_NATIVE["Xcode / Android Studio<br/>Nativer Debugger"]
DEBUG_JS --> JS_ONLY
DEBUG_NATIVE --> NATIVE_CHANGENur JS-Änderungen werden von Metro HMR innerhalb von Hunderten von Millisekunden als Differenz übertragen, wobei der Zustand erhalten bleibt.
Native Code-Änderungen erfordern einen Rebuild. Der erste iOS-Build dauert 2–5 Minuten, Differenzbuilds etwa 30 Sekunden bis eine Minute. Android mit Gradle-Cache ist ähnlich; der erste Build dauert länger.
Diese Asymmetrie ist der Schlüssel zur Entwicklungseffizienz im Bare Workflow: Die native Schnittstelle (Spec) frühzeitig fixieren und die meiste Implementierung auf der JS-Seite halten, um die Häufigkeit schwerer nativer Builds zu reduzieren.
13. Debugging: Untersuchung über die JS/Native-Grenze hinweg
flowchart TD
SYMPTOM["Symptome beobachten"]
SYMPTOM --> Q1{"Art des Crashes"}
Q1 -->|"JS-Fehler<br/>LogBox-Rotbildschirm"| JS_ERR["Mit Hermes Debugger untersuchen<br/>Breakpoints, Stack Trace<br/>TS-Zeilennummern via Source Map"]
Q1 -->|"Nativer Crash<br/>App friert ein / stürzt ab"| NAT_ERR["Crash-Logs prüfen"]
Q1 -->|"Läuft, aber<br/>falsches Ergebnis"| BORDER["JS↔Native-Grenze verdächtig"]
NAT_ERR --> IOS_CRASH["iOS: Xcode<br/>Debug Navigator<br/>+ lldb anhängen"]
NAT_ERR --> AND_CRASH["Android: Android Studio<br/>Logcat Stack Trace<br/>+ Native Debugger"]
BORDER --> LOG_IOS["iOS: NSLog / os_log<br/>npx react-native log-ios"]
BORDER --> LOG_AND["Android: Log.d<br/>npx react-native log-android"]
BORDER --> JS_LOG["JS-Seite: console.log<br/>TurboModule-Rückgabewerte prüfen"]
IOS_CRASH --> INSTRUMENTS["Instruments<br/>Time Profiler / Allocations<br/>Speicherlecks / CPU identifizieren"]
AND_CRASH --> PROFILER["Android Studio<br/>Memory / CPU Profiler"]
JS_ERR --> FIXED["Beheben → Sofort via Metro HMR sichtbar"]
INSTRUMENTS --> FIXED2["Beheben → Mit npx expo run:ios prüfen"]
PROFILER --> FIXED3["Beheben → Mit npx expo run:android prüfen"]
LOG_IOS & LOG_AND & JS_LOG --> BORDER2["Ursache identifizieren<br/>→ Spec / Implementierung beheben"]JS-Fehler: Hermes Debugger
Cmd+D → „Open Debugger" öffnet Chrome DevTools. Metro stellt Source Maps bereit, sodass Breakpoints an originalen TypeScript-Zeilennummern gesetzt werden können – nicht im gebündelten Code. Ausnahmen bei TurboModule-Aufrufen zeigen den vollständigen Aufrufstapel bis zur JS-Seite.
Native Crashes: Xcode / Android Studio Debugger anhängen
# iOS: lldb an laufenden Simulator anhängen
npx expo run:ios --configuration Debug
# → Xcode öffnet sich automatisch und hängt den Debugger an
# Android
npx expo run:android
# → "Run > Attach Debugger to Android Process"
Bei Crashes, die nur auf physischen Geräten auftreten (besonders Bluetooth, Kamera, Sensoren) kann der Debugger auf die gleiche Weise an ein per USB verbundenes Gerät angehängt werden.
Grenzuntersuchung: Mit Logs einrahmen
Wenn Daten an der JS↔Native-Grenze beschädigt werden, ist das Einrahmen von beiden Seiten mit Logs der schnellste Ansatz.
// Swift-Seite
os_log("BluetoothModule.connect called: %{public}@", deviceId)
// → Mit npx react-native log-ios filtern
// Kotlin-Seite
Log.d("BluetoothModule", "connect called: $deviceId")
// → Mit npx react-native log-android filtern
// JS-Seite
const result = await BluetoothModule.connect(deviceId);
console.log('connect result:', result); // Ausgabe in Metro
React DevTools
npx react-devtools
# → Wartet auf Geräteverbindung auf Port 8097
Den Komponentenbaum visuell inspizieren und Props sowie State im Inspektor direkt bearbeiten und Änderungen in Echtzeit sehen.
14. Simulatoren vs. physische Geräte
| Was getestet wird | iOS Simulator | Android Emulator | Physisches Gerät |
|---|---|---|---|
| UI / Layout | Ausreichend | Ausreichend | Abschlussverifikation |
| JS-Logik / API | Ausreichend | Ausreichend | Nicht nötig |
| Bluetooth / NFC | Nicht möglich | Nicht möglich | Erforderlich |
| Kamera / Mikrofon | Teilweise | Nicht möglich | Erforderlich |
| Push-Benachrichtigungen | Teilweise | Teilweise | Empfohlen |
| Performance | Nur als Referenz | Nur als Referenz | Erforderlich |
| Biometrie | Face ID-Sim verfügbar | Fingerabdruck-Sim verfügbar | Empfohlen |
Für die Entwicklung mit nativen Modulen funktioniert ein zweistufiger Ansatz gut: JS-Verhalten auf Emulatoren festigen, native Funktionen auf physischen Geräten verifizieren. Besonders für Bluetooth und NFC – da Emulatoren sie gar nicht testen können – wirkt sich die Anzahl der Geräte und die OS-Versionsabdeckung direkt auf die Qualität aus.
Zusammenfassung
Der vollständige Technologie-Stack
| Schicht | Technologie | Rolle |
|---|---|---|
| Bundler | Metro | Quell-Transpilierung, HMR, Plattformverzweigung |
| JS-Engine | Hermes | Bytecode-Ausführung für schnellen Start |
| JS↔Native-Bridge | JSI | Synchrone Bindung über C++ |
| Code-Generierung | Codegen | Automatische Generierung typsicherer nativer Gerüste aus Spec |
| UI-Renderer | Fabric | Synchrones UI-Rendering auf JSI |
| Native APIs | TurboModules | Verzögert initialisierte Module, die Codegen-generierte Klassen erben |
| Entwicklungsinfrastruktur | Expo / EAS | SDK, Cloud-Builds, OTA-Updates |
Vollständiger Ablauf für native Implementierung
① JS Spec schreiben (Typdefinitionen in TypeScript)
↓
② pod install / gradle build → Codegen generiert automatisch C++-Gerüst
↓
③ In Swift / Kotlin implementieren (internes SDK / externe Bibliotheken aufrufen)
↓
④ npx expo run:ios / run:android → Nativer Build & Emulator-Verifikation
↓
⑤ JS-Aufrufcode schreiben → Schnelle Iteration via Metro HMR
↓
⑥ JS-Fehler → Hermes Debugger
Native Crashes → Xcode / Android Studio Debugger
Grenz-Diskrepanzen → Mit log-ios / log-android + console.log einrahmen
↓
⑦ Bluetooth / Kamera / Sensoren auf physischen Geräten verifizieren
↓
⑧ EAS Build generiert Store-fertige Binaries
React Natives Wechsel von Bridge zu JSI + Codegen hat es von „funktioniert, aber fragil" zu „typsicher und hochperformant" transformiert. Das Verlassen von Expo bringt Kosten mit sich – schwere Builds und dieselbe Logik in zwei Sprachen – aber Codegen macht die Spec zur einzigen Quelle der Wahrheit und eliminiert das größte Risiko: Laufzeit-Crashes durch Typ-Diskrepanzen, bereits in der Designphase.
Referenz-URLs
Hinweis: Dieser Artikel fasst Informationen aus den folgenden Quellen zusammen und rekonstruiert sie. Beispiele und Erklärungen wurden zur besseren Verständlichkeit vereinfacht und sind keine direkten Zitate. Für die Implementierung immer die offizielle Dokumentation für die verwendete Version konsultieren.
Referenzierte URLs
React Native Offiziell
- React Native Neue Architektur (Übersicht): https://reactnative.dev/architecture/landing-page
- JSI & TurboModules Übersicht: https://reactnative.dev/docs/turbo-native-modules-introduction
- Was ist Codegen: https://reactnative.dev/docs/the-new-architecture/what-is-codegen
- Codegen verwenden: https://reactnative.dev/docs/the-new-architecture/using-codegen
- Events aus TurboModules senden: https://reactnative.dev/docs/the-new-architecture/native-modules-custom-events
- Codegen-Typdefinitionen Anhang: https://reactnative.dev/docs/appendix
- Hermes-Engine: https://reactnative.dev/docs/hermes
- Metro (React Native offiziell): https://reactnative.dev/docs/metro
Expo Offiziell
- Warum Metro (Expo): https://docs.expo.dev/guides/why-metro/
- Hermes verwenden (Expo): https://docs.expo.dev/guides/using-hermes/
- EAS Übersicht: https://docs.expo.dev/eas/
- EAS Build Übersicht: https://docs.expo.dev/build/introduction/
- EAS Update Übersicht: https://docs.expo.dev/eas-update/introduction/
