Quand on commence à utiliser React Native, on a vite l'impression que « ça marche, sans trop savoir pourquoi ». Dans cet article, nous allons tout expliquer : de Metro, JSI, Codegen et le fonctionnement d'Expo, jusqu'à l'implémentation native pour les domaines que le SDK Expo ne peut pas couvrir.
1. La Philosophie Fondamentale de React Native
Le cœur de React Native est de traduire la logique UI écrite en JS en composants UI natifs. Tout comme ReactDOM convertit <div> en éléments DOM, React Native mappe <View> et <Text> vers UIView sur iOS et android.view.View sur Android.
Cela signifie qu'il génère une vraie UI native — il ne rend pas du HTML dans une WebView. C'est la différence fondamentale avec Cordova ou Ionic.
2. Évolution de l'Architecture : Du Bridge au JSI
React Native dispose actuellement de deux architectures coexistantes.
flowchart LR
subgraph OLD["Ancienne Architecture (Bridge)"]
direction TB
J1["Thread JS<br/>Logique React"] -->|"Sérialisation JSON"| BR["Bridge<br/>File d'attente async ⚠"]
BR -->|"Désérialisation JSON"| N1["Thread Natif<br/>UIKit / Android View"]
SH["Thread Shadow<br/>Layout Yoga"] --> N1
end
subgraph NEW["Nouvelle Architecture (JSI)"]
direction TB
HM["Moteur Hermes<br/>Exécution bytecode"] -->|"Référence C++ directe"| JSI["JSI<br/>Host Objects"]
JSI --> FAB["Fabric<br/>Rendu synchrone"]
JSI --> TM["TurboModules<br/>Initialisation paresseuse"]
FAB --> N2["Couche Native"]
TM --> N2
endProblèmes de l'Ancienne Architecture (Bridge)
L'ancien React Native faisait communiquer le thread JS et le thread natif via un seul goulot d'étranglement asynchrone appelé le Bridge. Chaque échange devait être sérialisé en JSON, envoyé, puis désérialisé — rendant les opérations fréquentes comme les animations et les gestes sujettes à des problèmes de performance.
Innovations de la Nouvelle Architecture (JSI + Hermes)
JSI (JavaScript Interface) est une couche de liaison C++ légère qui permet au moteur JS de référencer directement et de façon synchrone les objets natifs. Cela a rendu possible :
- Fabric — Un nouveau rendu qui construit et met à jour les arbres UI de façon synchrone
- TurboModules — Initialisation paresseuse qui reporte le chargement des modules natifs jusqu'au besoin
- Hermes — Le moteur de Facebook qui pré-compile le JS en bytecode, réduisant drastiquement le temps de démarrage
3. Codegen : La Clé de la Communication Native Type-Safe
Codegen est le mécanisme le plus souvent négligé dans la nouvelle architecture. Tandis que JSI et TurboModules rendent la communication possible, Codegen garantit qu'elle est correcte au moment de la compilation.
flowchart TD
SPEC["Fichier JS Spec<br/>Définitions de types en TypeScript / Flow"]
subgraph BUILD["Temps de Compilation"]
CG["Codegen<br/>S'exécute lors du pod install / Gradle"]
end
subgraph OUT["Artefacts Générés"]
CPP["Classes C++ Abstraites<br/>Interfaces de base TurboModule"]
DESC["Component Descriptor<br/>Pour les composants natifs Fabric"]
JSTYPE["Définitions de Types JS<br/>Équivalent .d.ts"]
end
subgraph NATIVE["Implémentation Native"]
IOS["iOS<br/>Implémenté en Swift / Obj-C"]
AND["Android<br/>Implémenté en Kotlin / Java"]
end
RUNTIME["Via JSI<br/>Type-safe, appels synchrones"]
SPEC --> CG
CG --> CPP & DESC & JSTYPE
CPP --> IOS & AND
DESC --> IOS & AND
IOS --> RUNTIME
AND --> RUNTIMEDans l'ancienne architecture, l'interface entre JS et natif était essentiellement un « accord de gentleman ». Appeler NativeModules.MyModule.doSomething(42) depuis JS ne produirait une erreur qu'au moment d'un crash à l'exécution si le côté natif attendait un String.
Codegen résout ce problème au moment de la compilation. Les développeurs écrivent une Spec (spécification) en TypeScript ou Flow décrivant quels types un module natif accepte. Lors du pod install (iOS) ou de la compilation Gradle (Android), Codegen génère automatiquement des classes C++ abstraites, un échafaudage natif pour iOS et Android et des définitions de types JS.
4. Le Pipeline du Metro Bundler
Metro est le bundler JavaScript dédié de React Native. Il remplit le même rôle que webpack, mais est optimisé pour le développement mobile.
flowchart LR
SRC[".js / .ts / .tsx<br/>Fichiers source"]
RES["Résolution<br/>Résolution des chemins d'import"]
TRA["Transformation<br/>Transforms ES via Babel"]
SER["Sérialisation<br/>Génération du bundle"]
DEV["Livraison à l'appareil<br/>HTTP :8081"]
HMR["HMR<br/>Renvoie only les modules modifiés"]
CACHE["Cache FS<br/>Plus rapide dès le 2e lancement"]
SRC --> RES --> TRA --> SER --> DEV
TRA -.->|"Détection de changement"| HMR
HMR -.->|"Reflète sans perdre le state"| DEV
TRA <-.->|"Met en cache les résultats"| CACHELe pipeline de Metro comporte trois phases :
- Résolution — Résolution de modules à la Node.js avec sélection automatique des fichiers spécifiques à la plateforme (
foo.ios.ts/foo.android.ts) - Transformation — Babel convertit JSX, TypeScript et ES moderne ; les résultats sont mis en cache pour accélérer les démarrages suivants
- Sérialisation — Génère le bundle final et le livre à l'appareil via un serveur HTTP
À noter particulièrement, le HMR (Hot Module Replacement). Il ne met à jour que le graphe des modules modifiés, reflétant les changements de code immédiatement tout en préservant l'état des composants React. Les builds de production combinent --minify et la compilation Hermes en bytecode pour réduire à la fois la taille du fichier et le temps de démarrage.
5. La Structure en Couches d'Expo et les Outils de Développement
flowchart TD
APP["Votre App<br/>App.tsx / screens / hooks"]
SDK["Expo SDK<br/>expo-camera / expo-location / expo-router …"]
EMC["expo-modules-core<br/>DSL type-safe en 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/>Lancement immédiat par scan QR"]
EASB["EAS Build<br/>Générer .ipa / .apk dans le cloud"]
EASU["EAS Update<br/>Livrer uniquement le JS bundle via OTA"]
STORE["App Store / Google Play"]
APP --> SDK
EMC --> SDK
SDK --> RN
RN --> IOS & AND
GO -->|"Héberge en développement"| APP
EASB --> STORE
EASU -->|"Sans review App Store"| APPLe SDK Expo fournit des fonctionnalités natives — caméra, localisation, notifications, système de fichiers — sous forme de packages versionnés. Pas besoin d'écrire son propre code natif — c'est sa plus grande valeur.
expo-modules-core est un système plus récent permettant le développement déclaratif de modules natifs avec des DSLs Swift et Kotlin, intégré à Codegen. Plus type-safe et moins verbeux que l'ancien bridge NativeModules, presque tous les SDKs tiers d'Expo l'utilisent.
EAS (Expo Application Services) est un terme générique pour Build, Update, Submit, Workflows, etc. Cet article se concentre sur EAS Build et EAS Update. EAS Build compile des binaires natifs dans le cloud, permettant des builds iOS sans Mac. EAS Update livre des bundles JS et des assets via OTA, permettant une livraison rapide des changements qui ne touchent pas le code natif.
6. Les Domaines Non Couverts par Expo et le Choix du Workflow
Principaux domaines que le SDK Expo ne couvre pas :
- Bluetooth LE / NFC — Communication avec des périphériques spécifiques à la plateforme
- SDKs de paiement/biométrie personnalisés — Intégration directe de
.framework/.aarfournisseurs - Traitement audio/vidéo en temps réel — WebRTC bas niveau ou opérations avec des codecs personnalisés
- Bibliothèques natives internes — Assets Swift / Kotlin extraits d'applications existantes
- Vues caméra personnalisées — Rendu personnalisé avec OpenGL / Metal
flowchart TD
START["Quelle fonctionnalité native vous faut-il ?"]
START --> Q1{"Couverte par le SDK Expo<br/>ou une bibliothèque publique ?"}
Q1 -->|"Oui"| MANAGED["Managed Workflow<br/>Pas de code natif nécessaire<br/>Juste npx expo start"]
Q1 -->|"Non"| Q2{"Seulement des changements de config ?<br/>(Ajouts Info.plist / AndroidManifest)"}
Q2 -->|"Oui"| PLUGIN["Créer un Config Plugin<br/>Ajouter à app.json<br/>Appliqué automatiquement par prebuild"]
Q2 -->|"Non"| Q3{"Volume de code natif personnalisé ?"}
Q3 -->|"Léger<br/>(wrapper d'un SDK existant)"| BARE["Bare Workflow<br/>npx expo prebuild<br/>→ Gérer ios/ android/ directement"]
Q3 -->|"Complet<br/>(module / UI personnalisé)"| MODULE{"Qu'est-ce qui est construit ?"}
MODULE -->|"Logique<br/>Bluetooth / paiement / crypto"| TM["TurboModule<br/>+ Codegen"]
MODULE -->|"Composant UI<br/>Vue caméra personnalisée, etc."| FAB["Fabric Component<br/>+ Codegen"]
BARE --> TM & FAB
PLUGIN --> BARELes changements de configuration mineurs peuvent être gérés avec des Config Plugins. Mais si du code doit être écrit, générer les répertoires ios/ et android/ avec npx expo prebuild et passer au Bare Workflow est le point de départ pratique.
7. Architecture Complète : JSI + Codegen + Implémentation Native
flowchart TB
subgraph JS["Couche JS (Hermes)"]
APP["Code App / Composants React"]
SPEC["JS Spec<br/>(TypeScript + TurboModuleRegistry)"]
end
subgraph CODEGEN["Codegen (Auto-généré au moment de la compilation)"]
GEN_CPP["Classes C++ Abstraites<br/>TurboModule / ComponentDescriptor"]
GEN_SHADOW["Définitions ShadowNode / Props"]
end
subgraph JSI_LAYER["Couche JSI (C++)"]
JSI["JSI Host Object"]
FABRIC["Fabric Renderer"]
end
subgraph NATIVE_IOS["Implémentation Native iOS"]
SWIFT["Implémentation Swift / Obj-C<br/>(Hérite de la classe C++ abstraite)"]
SDK_IOS["SDK Interne / .framework Externe<br/>ex: CoreBluetooth / Stripe"]
UIVIEW["Sous-classe UIView<br/>(Composant UI Personnalisé)"]
end
subgraph NATIVE_AND["Implémentation Native Android"]
KOTLIN["Implémentation Kotlin / Java<br/>(Hérite de la classe C++ abstraite)"]
SDK_AND["SDK Interne / .aar Externe<br/>ex: BluetoothGatt / Stripe"]
ANDROIDVIEW["Sous-classe View<br/>(Composant UI Personnalisé)"]
end
SPEC -->|"Lire les définitions de types"| CODEGEN
GEN_CPP --> JSI
GEN_SHADOW --> FABRIC
APP -->|"Appels sync / async"| JSI
JSI --> SWIFT & KOTLIN
FABRIC --> UIVIEW & ANDROIDVIEW
SWIFT --> SDK_IOS
KOTLIN --> SDK_ANDLa clé est la dépendance unidirectionnelle : Spec → Codegen → Implémentation Native. Écrire des définitions de types côté JS, et Codegen génère des classes C++ abstraites au moment de la compilation. Les implémentations Swift / Kotlin n'ont qu'à les hériter — la cohérence des types est garantie par le compilateur, éliminant l'« accord de gentleman » comme source de crashes à l'exécution.
8. Flux d'Implémentation TurboModule (Exemple Bluetooth)
Note : Le code ci-dessous est un exemple simplifié basé sur les guides officiels React Native TurboModule / Codegen / Custom Events. Ce ne sont pas des citations directes des exemples officiels, mais une reconstruction utilisant Bluetooth comme exemple.
① Écrire la JS Spec
// 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');
② Ce que Codegen Génère (Automatiquement)
flowchart LR
SPEC["NativeBluetoothModule.ts<br/>(Écrit par le développeur)"]
subgraph AUTO["Code auto-généré"]
CPP["Code C++ glue<br/>Sortie JSI / Codegen"]
IOS_H["Code iOS glue<br/>Sortie Obj-C++ / header"]
AND_J["Code Android glue<br/>Sortie JNI / classe Spec"]
end
subgraph IMPL["Implémenté par le développeur"]
SWIFT_IMPL["NativeBluetoothModule.swift<br/>Implémente la Spec générée, appelle CoreBluetooth"]
KOTLIN_IMPL["NativeBluetoothModule.kt<br/>Implémente la Spec générée, appelle BluetoothGatt"]
end
SPEC --> CPP
SPEC --> IOS_H
SPEC --> AND_J
CPP --> SWIFT_IMPL & KOTLIN_IMPL
IOS_H --> SWIFT_IMPL
AND_J --> KOTLIN_IMPL③ Implémentation iOS (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
])
}
}
④ Implémentation Android (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. Flux d'Implémentation Fabric Component (Exemple Vue Caméra Personnalisée)
Si TurboModule gère la logique, Fabric Component sert à intégrer une UI native dans JSX. Nécessaire pour le rendu personnalisé avec OpenGL / Metal, ou la réutilisation de vues natives existantes.
Note : Ce qui suit est également un exemple minimal pour la compréhension. Les chemins d'import Codegen et les noms de types varient selon la version de React Native, vérifiez toujours la documentation officielle de votre version lors de l'implémentation.
// 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 convertit Float → CGFloat / float
torchEnabled?: boolean;
onFrameCaptured?: DirectEventHandler<FrameCapturedEvent>;
}
export default codegenNativeComponent<NativeProps>('CameraView');
Codegen génère des définitions C++ pour ShadowNode, ComponentDescriptor et Props à partir de cette Spec. Sur iOS, implémenter une CameraViewComponentView héritant de RCTViewComponentView ; sur Android, hériter de ReactViewGroup. Du côté JS, c'est aussi simple que <CameraView zoom={2.0} torchEnabled />.
10. Intégration de SDKs Internes / .framework et .aar Externes
iOS : Référence Locale via CocoaPods
# ios/Podfile
pod 'CompanyPaymentSDK', :path => '../vendor/ios/CompanyPaymentSDK'
# ou pour xcframework
pod 'CompanyPaymentSDK', :podspec => '../vendor/ios/CompanyPaymentSDK.podspec'
Après ajout au Podfile et exécution de pod install, il peut être utilisé comme une classe Swift ordinaire avec import CompanyPaymentSDK. L'appeler directement depuis l'implémentation TurboModule.
Android : Référence Maven Locale / .aar
// android/app/build.gradle
dependencies {
implementation files('../vendor/android/company-payment-sdk.aar')
// ou un dépôt Maven interne
implementation 'com.company:payment-sdk:1.4.0'
}
11. La Boucle de Développement : Du Démarrage à la Vérification
flowchart TD
INIT["npx create-expo-app MyApp<br/>--template blank-typescript"]
subgraph START["npx expo start"]
METRO["Metro Bundler démarre<br/>port 8081"]
QR["Code QR + raccourcis<br/>i = iOS a = Android w = Web"]
end
subgraph DEVICE["Choisir la cible"]
SIM["iOS Simulator<br/>Inclus avec Xcode (Mac uniquement)"]
EMU["Android Emulator<br/>Android Studio AVD"]
GO["Appareil physique + Expo Go<br/>Via LAN ou Tunnel"]
end
subgraph NATIVE_BUILD["Première fois seulement : Build Natif"]
POD["pod install<br/>iOS CocoaPods"]
GRADLE["Gradle build<br/>Android"]
CODEGEN_RUN["Codegen s'exécute<br/>Spec → Échafaudage natif"]
end
BUNDLE["Transférer le JS bundle à l'appareil<br/>via le serveur HTTP Metro"]
RENDER["L'écran s'affiche"]
INIT --> START
METRO --> QR
QR --> SIM & EMU & GO
SIM --> NATIVE_BUILD
EMU --> NATIVE_BUILD
NATIVE_BUILD --> BUNDLE
GO --> BUNDLE
BUNDLE --> RENDERLancez npx expo start pour démarrer Metro, et un code QR avec des raccourcis clavier apparaît dans le terminal.
- Touche
i→ iOS Simulator (fonctionne sur Mac avec Xcode installé) - Touche
a→ Android Emulator (appareil virtuel créé dans Android Studio AVD Manager) - Pour les appareils physiques, scanner le QR dans Expo Go — se connecte presque instantanément sur LAN
En déclenchant explicitement un build natif avec npx expo run:ios ou npx expo run:android, pod install (iOS) ou Gradle (Android) s'exécute la première fois et Codegen génère l'échafaudage natif. Les exécutions suivantes ne transfèrent que les différences du JS bundle et sont beaucoup plus rapides.
12. La Boucle de Changement dans le Bare Workflow
flowchart TD
subgraph CHANGE["Branchement par type de changement"]
JS_ONLY["Changements JS uniquement<br/>Composants / logique / styles"]
NATIVE_CHANGE["Changements de code natif<br/>Swift / Kotlin / Spec / Podfile"]
end
subgraph JS_LOOP["Boucle de Changement JS (Rapide)"]
METRO_HMR["Metro HMR<br/>Transférer le bundle différentiel<br/>~Centaines de ms"]
SIM_JS["Visible immédiatement dans l'émulateur<br/>État de l'app préservé"]
end
subgraph NATIVE_LOOP["Boucle de Changement Natif (Lent)"]
CODEGEN_RUN["Re-exécuter Codegen<br/>pod install (iOS)<br/>Gradle sync (Android)"]
REBUILD["Rebuild natif<br/>npx expo run:ios<br/>npx expo run:android"]
SIM_NATIVE["Vérifier sur émulateur / appareil"]
end
JS_ONLY --> METRO_HMR --> SIM_JS
NATIVE_CHANGE --> CODEGEN_RUN --> REBUILD --> SIM_NATIVE
SIM_JS -->|"Pas de problème"| DONE["Vérification OK"]
SIM_NATIVE -->|"Pas de problème"| DONE
SIM_JS -->|"Bug trouvé"| DEBUG_JS["Hermes Debugger<br/>React DevTools"]
SIM_NATIVE -->|"Crash natif"| DEBUG_NATIVE["Xcode / Android Studio<br/>Débogueur natif"]
DEBUG_JS --> JS_ONLY
DEBUG_NATIVE --> NATIVE_CHANGELes changements uniquement JS sont gérés par Metro HMR en quelques centaines de millisecondes, en préservant l'état.
Les changements de code natif nécessitent un rebuild. Le premier build iOS prend 2–5 minutes, les builds différentiels environ 30 secondes à une minute. Android avec le cache Gradle est similaire ; le premier build prend plus longtemps.
Cette asymétrie est la clé de l'efficacité du développement en Bare Workflow : fixer tôt la limite native (Spec) et pousser la plupart de l'implémentation côté JS pour réduire la fréquence des lourds builds natifs.
13. Debugging : Investigation à Travers la Frontière JS/Natif
flowchart TD
SYMPTOM["Observer les symptômes"]
SYMPTOM --> Q1{"Type de crash"}
Q1 -->|"Erreur JS<br/>Écran rouge LogBox"| JS_ERR["Investiguer avec Hermes Debugger<br/>Breakpoints, stack trace<br/>Numéros de ligne TS via Source Map"]
Q1 -->|"Crash natif<br/>L'app freeze / crash"| NAT_ERR["Vérifier les logs de crash"]
Q1 -->|"Fonctionne mais<br/>mauvais résultat"| BORDER["Suspecter la frontière JS↔Natif"]
NAT_ERR --> IOS_CRASH["iOS: Xcode<br/>Debug Navigator<br/>+ attacher lldb"]
NAT_ERR --> AND_CRASH["Android: Android Studio<br/>Stack trace Logcat<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["Côté JS: console.log<br/>Vérifier les valeurs de retour TurboModule"]
IOS_CRASH --> INSTRUMENTS["Instruments<br/>Time Profiler / Allocations<br/>Identifier fuites mémoire / CPU"]
AND_CRASH --> PROFILER["Android Studio<br/>Memory / CPU Profiler"]
JS_ERR --> FIXED["Corriger → Visible via Metro HMR"]
INSTRUMENTS --> FIXED2["Corriger → Vérifier avec npx expo run:ios"]
PROFILER --> FIXED3["Corriger → Vérifier avec npx expo run:android"]
LOG_IOS & LOG_AND & JS_LOG --> BORDER2["Identifier la cause racine<br/>→ Corriger Spec / implémentation"]Erreurs JS : Hermes Debugger
Cmd+D → « Open Debugger » ouvre Chrome DevTools. Metro fournit des Source Maps, permettant de placer des breakpoints aux numéros de ligne TypeScript originaux — pas dans le code bundlé. Les exceptions dans les appels TurboModule montrent la stack complète remontant jusqu'au JS.
Crashes Natifs : Attacher le Débogueur Xcode / Android Studio
# iOS: Attacher lldb au simulateur en cours
npx expo run:ios --configuration Debug
# → Xcode s'ouvre automatiquement et attache le débogueur
# Android
npx expo run:android
# → "Run > Attach Debugger to Android Process"
Pour les crashes qui ne se reproduisent que sur des appareils physiques (surtout Bluetooth, caméra, capteurs), le débogueur peut être attaché de la même façon à un appareil connecté via USB.
Investigation de Frontière : Encadrer avec des Logs
Quand les données sont corrompues à la frontière JS↔Natif, les encadrer avec des logs des deux côtés est l'approche la plus rapide.
// Côté Swift
os_log("BluetoothModule.connect called: %{public}@", deviceId)
// → Filtrer avec npx react-native log-ios
// Côté Kotlin
Log.d("BluetoothModule", "connect called: $deviceId")
// → Filtrer avec npx react-native log-android
// Côté JS
const result = await BluetoothModule.connect(deviceId);
console.log('connect result:', result); // Sortie dans Metro
React DevTools
npx react-devtools
# → Attend la connexion de l'appareil sur le port 8097
Visualiser l'arbre de composants et inspecter (et modifier) les props et l'état en temps réel.
14. Simulateurs vs. Appareils Physiques
| Ce qu'on teste | iOS Simulator | Android Emulator | Appareil Physique |
|---|---|---|---|
| UI / Layout | Suffisant | Suffisant | Vérification finale |
| Logique JS / API | Suffisant | Suffisant | Pas nécessaire |
| Bluetooth / NFC | Impossible | Impossible | Requis |
| Caméra / Microphone | Partiel | Impossible | Requis |
| Notifications push | Partiel | Partiel | Recommandé |
| Performance | Référence seulement | Référence seulement | Requis |
| Authentification biométrique | Sim Face ID disponible | Sim empreinte disponible | Recommandé |
Pour le développement avec des modules natifs, une approche en deux étapes fonctionne bien : consolider le comportement JS sur les émulateurs, vérifier les fonctionnalités natives sur les appareils physiques. Surtout pour Bluetooth et NFC — les émulateurs ne pouvant pas les tester — le nombre d'appareils et la couverture des versions OS impacte directement la qualité.
Résumé
La Stack Technologique Complète
| Couche | Technologie | Rôle |
|---|---|---|
| Bundler | Metro | Transpilation source, HMR, branchement plateforme |
| Moteur JS | Hermes | Exécution bytecode pour démarrage rapide |
| Bridge JS↔Natif | JSI | Liaison synchrone via C++ |
| Génération de Code | Codegen | Auto-génération d'échafaudages natifs type-safe depuis la Spec |
| Rendu UI | Fabric | Rendu UI synchrone sur JSI |
| APIs Natives | TurboModules | Modules à initialisation paresseuse héritant des classes Codegen |
| Infrastructure Dev | Expo / EAS | SDK, builds cloud, mises à jour OTA |
Flux Complet pour l'Implémentation Native
① Écrire la JS Spec (définitions de types en TypeScript)
↓
② pod install / gradle build → Codegen auto-génère l'échafaudage C++
↓
③ Implémenter en Swift / Kotlin (appeler SDK interne / bibliothèques externes)
↓
④ npx expo run:ios / run:android → Build natif & vérification émulateur
↓
⑤ Écrire le code d'appel JS → Itération rapide via Metro HMR
↓
⑥ Erreurs JS → Hermes Debugger
Crashes natifs → Débogueur Xcode / Android Studio
Discordances de frontière → Encadrer avec log-ios / log-android + console.log
↓
⑦ Vérifier Bluetooth / caméra / capteurs sur appareils physiques
↓
⑧ EAS Build génère les binaires prêts pour les stores
La transition de React Native de Bridge vers JSI + Codegen l'a transformé de « fonctionne mais fragile » à « type-safe et haute performance ». Sortir d'Expo entraîne des coûts — builds lourds et écrire la même logique en deux langues — mais Codegen fait de la Spec l'unique source de vérité, éliminant le plus grand risque : les crashes à l'exécution dus aux incompatibilités de types, dès la phase de conception.
URLs de Référence
Note : Cet article résume et reconstruit des informations provenant des sources suivantes. Les exemples et explications ont été simplifiés pour plus de clarté et ne sont pas des citations directes. Toujours consulter la documentation officielle de votre version lors de l'implémentation.
URLs Référencées
React Native Officiel
- React Native Nouvelle Architecture (Vue d'ensemble) : https://reactnative.dev/architecture/landing-page
- Vue d'ensemble JSI & TurboModules : https://reactnative.dev/docs/turbo-native-modules-introduction
- Qu'est-ce que Codegen : https://reactnative.dev/docs/the-new-architecture/what-is-codegen
- Comment utiliser Codegen : https://reactnative.dev/docs/the-new-architecture/using-codegen
- Envoi d'événements depuis TurboModules : https://reactnative.dev/docs/the-new-architecture/native-modules-custom-events
- Annexe définitions de types Codegen : https://reactnative.dev/docs/appendix
- Moteur Hermes : https://reactnative.dev/docs/hermes
- Metro (React Native officiel) : https://reactnative.dev/docs/metro
Expo Officiel
- Pourquoi Metro (Expo) : https://docs.expo.dev/guides/why-metro/
- Utiliser Hermes (Expo) : https://docs.expo.dev/guides/using-hermes/
- Vue d'ensemble EAS : https://docs.expo.dev/eas/
- Vue d'ensemble EAS Build : https://docs.expo.dev/build/introduction/
- Vue d'ensemble EAS Update : https://docs.expo.dev/eas-update/introduction/
