Typsichere native Schnittstellen aus einer einzigen Spec generieren
Codegen verwandelt eine TypeScript-Spec-Datei in die einzige Quelle der Wahrheit für ein TurboModule und erzeugt daraus zur Buildzeit passenden Interface-Code für iOS und Android. Dieser Artikel zeigt, was Codegen dabei tatsächlich tut, wie eine neue TurboModule-Spec in der Praxis entsteht und welche Fehlerklasse gegenüber manuell geschriebenen Bridging-Deklarationen komplett verschwindet.
Inhaltsverzeichnis
- 1. Warum manuelles Bridging fehleranfällig war
- 2. Wie Codegen arbeitet: von der TypeScript-Spec zum generierten Code
- 3. Eine TurboModule-Spec anlegen: NativeMyModule.ts
- 4. Der generierte Code im Detail: Schema, Header und Interfaces
- 5. Implementierung auf iOS: Objective-C++ gegen das generierte Protokoll
- 6. Implementierung auf Android: Kotlin gegen das generierte Java-Interface
- 7. Registrierung, Podspec und Autolinking
- 8. Typsicherheit: welche Fehler Codegen zur Buildzeit abfängt
- 9. Workflow-Tipps: Monorepos, lokale Libraries und Debugging von Codegen-Fehlern
- 10. Zusammenfassung
- 11. FAQ
1. Warum manuelles Bridging fehleranfällig war
In der alten Architektur wurde jedes NativeModule manuell mit passenden Methodensignaturen auf iOS und Android nachgebaut, wobei JavaScript zur Laufzeit lediglich per Reflection oder String-basiertem Lookup herausfand, welche Methoden überhaupt existierten. Eine falsch geschriebene Methode, ein vergessener Parameter oder ein Typmismatch zwischen JavaScript und nativer Implementierung fiel oft erst als Laufzeitfehler auf, manchmal erst bei einem seltenen Codepfad in Produktion.
Da beide Plattformen unabhängig voneinander gepflegt wurden, driftete die iOS- und die Android-Implementierung eines Moduls im Laufe der Zeit häufig auseinander, etwa wenn ein neuer Parameter nur auf einer Plattform ergänzt wurde. Codegen setzt genau hier an: eine einzige, typisierte Spec-Datei wird zur einzigen Quelle der Wahrheit, aus der beide Plattformen konsistent generiert werden.
Zusätzlich erschwerte die alte, lose gekoppelte Bridging-Deklaration jede größere Refaktorierung, weil ein Team beim Umbenennen einer Methode auf beiden Plattformen gleichzeitig manuell nachziehen musste, ohne dass der Compiler an irgendeiner Stelle auf die verbliebene Inkonsistenz hinwies. Gerade in größeren Teams mit getrennten iOS- und Android-Entwicklern führte das regelmäßig zu Reibungsverlusten, die mit einer einzigen, verbindlichen Spec-Datei strukturell verschwinden.
2. Wie Codegen arbeitet: von der TypeScript-Spec zum generierten Code
Codegen liest zur Buildzeit spezielle TypeScript-Dateien, die per Namenskonvention als NativeXyz.ts erkannt werden und ein Interface exportieren, das TurboModule erweitert. Aus diesem Interface erzeugt ein Parser zunächst eine plattformunabhängige Schema-Repräsentation als JSON, die alle Methoden, Parametertypen und Rückgabewerte in einer maschinenlesbaren Form beschreibt.
Aus diesem Schema generieren separate Codegen-Backends dann die tatsächlichen Artefakte je Plattform: für iOS Objective-C++ Protokolle und Header, für Android Java-Interfaces und die passenden JNI-Bindings. Der generierte Code landet in einem Build-Verzeichnis und wird bei jedem Build neu erzeugt, weshalb er niemals von Hand editiert werden sollte.
3. Eine TurboModule-Spec anlegen: NativeMyModule.ts
Eine Spec-Datei folgt einer strengen, eingeschränkten Teilmenge von TypeScript, damit Codegen sie zuverlässig parsen kann. Erlaubt sind bestimmte primitive Typen, Arrays, Objekte mit fest definierter Form sowie einige spezielle Typen wie Double oder UnsafeObject für Fälle, in denen keine feste Struktur vorliegt. Generische TypeScript-Features wie Union-Types mit mehr als zwei Optionen oder komplexe Mapped Types werden bewusst nicht unterstützt.
Das folgende Beispiel zeigt eine minimale, gültige Spec-Datei mit einer synchronen und einer asynchronen Methode, exakt so, wie Codegen sie als Vertrag zwischen JavaScript und nativer Seite interpretiert.
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';
export interface Spec extends TurboModule {
getDeviceId(): string;
getBatteryLevel(): Promise<number>;
multiply(a: number, b: number): number;
}
export default TurboModuleRegistry.getEnforcing<Spec>('MyModule');
4. Der generierte Code im Detail: Schema, Header und Interfaces
Für das Beispielmodul erzeugt Codegen unter anderem eine JSON-Schema-Datei mit der abstrakten Methodenbeschreibung, ein Objective-C++ Protokoll NativeMyModuleSpec mit exakt passenden Methodensignaturen für iOS sowie ein Java-Interface NativeMyModuleSpec für Android, das die native Klasse implementieren muss. Beide generierten Interfaces spiegeln die TypeScript-Spec bis auf Typkonvertierung exakt wider.
Wichtig ist, dass diese generierten Dateien reine Verträge sind, keine Implementierung. Die eigentliche native Klasse muss das generierte Interface implementieren, und der Compiler meldet einen Fehler, sobald eine Methode fehlt oder eine falsche Signatur hat, was in der alten, manuell gepflegten Bridging-Welt schlicht nicht existierte.
5. Implementierung auf iOS: Objective-C++ gegen das generierte Protokoll
Die iOS-Implementierung erbt von NSObject, konformiert zum generierten Protokoll NativeMyModuleSpec und implementiert jede Methode mit exakt passender Signatur. Für asynchrone Methoden wie getBatteryLevel übernimmt der generierte Code bereits die Umwandlung in ein JavaScript Promise, die native Implementierung muss nur noch resolve oder reject aufrufen.
Registriert wird das Modul über die generierte Factory-Methode getTurboModule, die der TurboModuleManager beim ersten Zugriff aus JavaScript aufruft. Anders als bei alten NativeModules entfällt hier das manuelle Makro RCT_EXPORT_METHOD für jede einzelne Methode, da die gesamte Methodenliste bereits aus der Spec bekannt ist.
6. Implementierung auf Android: Kotlin gegen das generierte Java-Interface
Auf Android implementiert die Kotlin-Klasse das generierte Interface NativeMyModuleSpec, das von der abstrakten Basisklasse ReactContextBaseJavaModule abgeleitet ist. Jede Methode muss exakt der generierten Signatur entsprechen, inklusive der für Promises nötigen Promise-Parameter bei asynchronen Methoden.
Die Registrierung erfolgt über ein TurboReactPackage, das in seiner getModule-Methode eine Instanz der Klasse zurückgibt sowie über getReactModuleInfoProvider Metadaten für die Turbo-Module-Erkennung liefert. Fehlt einer dieser beiden Bestandteile, findet der TurboModuleManager das Modul zur Laufzeit nicht, meldet aber einen klaren Fehler statt eines stillen Fehlschlags.
7. Registrierung, Podspec und Autolinking
Damit Codegen ein Modul überhaupt findet, muss die Podspec auf iOS beziehungsweise die build.gradle auf Android auf die Codegen-Konfiguration verweisen, üblicherweise über einen Eintrag in der package.json unter codegenConfig, der Name und Pfad der Spec-Dateien deklariert. React Native Autolinking scannt diese Konfiguration automatisch bei jedem Build und bindet neue Module ein, ohne dass native Projektdateien von Hand angepasst werden müssen.
Für lokale Module innerhalb eines Monorepos, die nicht als separates npm-Paket veröffentlicht werden, reicht ein passender codegenConfig-Eintrag im App-eigenen package.json, solange die Spec-Datei über den konfigurierten jsSrcsDir auffindbar ist.
8. Typsicherheit: welche Fehler Codegen zur Buildzeit abfängt
Codegen fängt vor allem strukturelle Fehler ab: fehlende Methoden, falsche Parameteranzahl, inkompatible Typen zwischen der Spec und der nativen Implementierung sowie vergessene Promise-Parameter bei asynchronen Methoden. Diese Fehlerklasse führte in der alten Architektur regelmäßig zu Crashes, die erst beim tatsächlichen Aufruf der betroffenen Methode auftraten, oft weit entfernt vom eigentlichen Implementierungsort.
Nicht abgefangen werden dagegen semantische Fehler, etwa eine Methode, die laut Spec eine gültige Geräte-ID zurückgibt, tatsächlich aber einen leeren String liefert. Codegen prüft ausschließlich die Struktur des Vertrags, nicht dessen inhaltliche Korrektheit, weshalb Unit-Tests für die eigentliche Business-Logik weiterhin notwendig bleiben.
9. Workflow-Tipps: Monorepos, lokale Libraries und Debugging von Codegen-Fehlern
In einem Monorepo mit mehreren Paketen lohnt es sich, Spec-Dateien konsequent in einem eigenen specs-Verzeichnis pro Paket zu bündeln und den codegenConfig-Eintrag darauf zu verweisen, statt Spec-Dateien verstreut neben beliebigem anderen Code abzulegen. Das erleichtert es, bei einem fehlgeschlagenen Codegen-Lauf schnell die betroffene Datei zu finden.
Codegen-Fehler äußern sich meist als kryptische Parserfehler mit Zeilennummer in der generierten Zwischendarstellung, nicht direkt in der Spec-Datei. In der Praxis hilft es, verdächtige TypeScript-Konstrukte wie komplexe Union-Types oder optionale, verschachtelte Objekte schrittweise zu vereinfachen, bis der Codegen-Lauf wieder durchläuft, und danach die Komplexität kontrolliert zurückzubauen.
| Aspekt | Manuelles Bridging (alt) | Codegen für TurboModules | Konkreter Nutzen |
|---|---|---|---|
| Quelle der Wahrheit | Getrennte iOS- und Android-Implementierung | Eine TypeScript-Spec-Datei | Keine Drift zwischen Plattformen |
| Fehlererkennung | Laufzeitfehler bei Typmismatch | Compile-Zeit-Fehler bei falscher Signatur | Fehler fallen vor dem Release auf |
| Async-Handling | Manuelles Promise/Callback-Mapping | Automatisch generierter Promise-Wrapper | Weniger Boilerplate pro Methode |
| Registrierung | RCT_EXPORT_METHOD je Methode | Interface-Implementierung, kein Makro | Weniger manuelle Pflege bei neuen Methoden |
| Monorepo-Eignung | Schwer synchron zu halten | codegenConfig pro Paket deklarierbar | Konsistente Module über mehrere Pakete |
Mironsoft
React-Native-App-Entwicklung und Magento-Anbindung
Eine mobile App zum Magento-Shop, die wirklich rund läuft?
Wir entwickeln React-Native-Apps, die sauber an die Magento REST- oder GraphQL-API angebunden sind, von der ersten Codezeile bis zur Veröffentlichung im App Store und bei Google Play.
App-Konzeption
Architektur und Feature-Umfang einer Magento-angebundenen App gemeinsam planen.
Magento-API-Integration
Produktkatalog, Warenkorb und Checkout sauber an die Shop-API anbinden.
Store-Veröffentlichung
App Store- und Google-Play-Freigabeprozess ohne Stolperfallen begleiten.
10. Zusammenfassung
Codegen für TurboModules: Das Wichtigste auf einen Blick
Spec-Datei
Eine TypeScript-Datei mit eingeschränkter Syntax ist die einzige Quelle der Wahrheit für ein TurboModule.
Codegen-Backends
Erzeugen aus dem Schema separate, plattformspezifische Interfaces für iOS und Android.
Buildzeit-Sicherheit
Strukturelle Fehler wie falsche Signaturen fallen beim Kompilieren auf, nicht erst zur Laufzeit.
Grenzen
Codegen prüft nur die Struktur des Vertrags, semantische Korrektheit bleibt Aufgabe eigener Tests.