Cuando se empieza a usar React Native, es fácil tener la sensación de que "funciona sin saber muy bien por qué". En este artículo explicaremos todo lo que ocurre bajo el capó — desde Metro, JSI, Codegen y cómo funciona Expo, hasta la implementación nativa para áreas que el SDK de Expo no puede cubrir.
1. La Filosofía Central de React Native
El corazón de React Native es traducir la lógica UI escrita en JS a componentes UI nativos. Al igual que ReactDOM convierte <div> en elementos DOM, React Native mapea <View> y <Text> a UIView en iOS y android.view.View en Android.
Esto significa que genera UI nativa real — no renderiza HTML en una WebView. Esa es la diferencia fundamental con Cordova o Ionic.
2. Evolución de la Arquitectura: De Bridge a JSI
React Native actualmente tiene dos arquitecturas coexistentes.
flowchart LR
subgraph OLD["Arquitectura Antigua (Bridge)"]
direction TB
J1["Hilo JS<br/>Lógica React"] -->|"Serializar JSON"| BR["Bridge<br/>Cola asíncrona ⚠"]
BR -->|"Deserializar JSON"| N1["Hilo Nativo<br/>UIKit / Android View"]
SH["Hilo Shadow<br/>Layout Yoga"] --> N1
end
subgraph NEW["Nueva Arquitectura (JSI)"]
direction TB
HM["Motor Hermes<br/>Ejecución bytecode"] -->|"Referencia C++ directa"| JSI["JSI<br/>Host Objects"]
JSI --> FAB["Fabric<br/>Renderizador síncrono"]
JSI --> TM["TurboModules<br/>Inicialización perezosa"]
FAB --> N2["Capa Nativa"]
TM --> N2
endProblemas de la Arquitectura Antigua (Bridge)
El React Native legacy hacía comunicar los hilos JS y nativo a través de un único cuello de botella asíncrono llamado Bridge. Cada intercambio debía serializarse como JSON, enviarse y deserializarse — haciendo que operaciones frecuentes como animaciones y gestos fueran propensas a problemas de rendimiento.
Innovaciones de la Nueva Arquitectura (JSI + Hermes)
JSI (JavaScript Interface) es una capa de enlace delgada en C++ que permite al motor JS referenciar directa y síncronamente objetos nativos. Esto habilitó:
- Fabric — Un nuevo renderizador que construye y actualiza árboles UI síncronamente
- TurboModules — Inicialización perezosa que aplaza la carga de módulos nativos hasta que se necesiten
- Hermes — El motor de Facebook que precompila JS en bytecode, reduciendo drásticamente el tiempo de inicio
3. Codegen: La Clave para la Comunicación Nativa Segura en Tipos
Codegen es el mecanismo más ignorado en la nueva arquitectura. Mientras JSI y TurboModules hacen la comunicación posible, Codegen garantiza que es correcta en tiempo de compilación.
flowchart TD
SPEC["Archivo JS Spec<br/>Definiciones de tipos en TypeScript / Flow"]
subgraph BUILD["Tiempo de Compilación"]
CG["Codegen<br/>Se ejecuta durante pod install / Gradle"]
end
subgraph OUT["Artefactos Generados"]
CPP["Clases C++ Abstractas<br/>Interfaces base TurboModule"]
DESC["Component Descriptor<br/>Para componentes nativos Fabric"]
JSTYPE["Definiciones de Tipos JS<br/>Equivalente a .d.ts"]
end
subgraph NATIVE["Implementación Nativa"]
IOS["iOS<br/>Implementado en Swift / Obj-C"]
AND["Android<br/>Implementado en Kotlin / Java"]
end
RUNTIME["Vía JSI<br/>Seguro en tipos, llamadas síncronas"]
SPEC --> CG
CG --> CPP & DESC & JSTYPE
CPP --> IOS & AND
DESC --> IOS & AND
IOS --> RUNTIME
AND --> RUNTIMEEn la arquitectura antigua, la interfaz entre JS y nativo era esencialmente un "acuerdo de caballeros". Llamar a NativeModules.MyModule.doSomething(42) desde JS no fallaría hasta un crash en tiempo de ejecución si el lado nativo esperaba un String.
Codegen resuelve esto en tiempo de compilación. Los desarrolladores escriben una Spec (especificación) en TypeScript o Flow describiendo qué tipos acepta un módulo nativo. Durante pod install (iOS) o la compilación Gradle (Android), Codegen genera automáticamente clases C++ abstractas, andamiaje nativo para iOS y Android y definiciones de tipos JS.
4. El Pipeline del Metro Bundler
Metro es el bundler JavaScript dedicado de React Native. Cumple el mismo rol que webpack, pero está optimizado para el desarrollo móvil.
flowchart LR
SRC[".js / .ts / .tsx<br/>Archivos fuente"]
RES["Resolución<br/>Resolución de rutas de import"]
TRA["Transformación<br/>Transforms ES via Babel"]
SER["Serialización<br/>Generación del bundle"]
DEV["Entrega al dispositivo<br/>HTTP :8081"]
HMR["HMR<br/>Reenviar solo módulos cambiados"]
CACHE["Caché FS<br/>Más rápido desde el 2do arranque"]
SRC --> RES --> TRA --> SER --> DEV
TRA -.->|"Detección de cambio de archivo"| HMR
HMR -.->|"Reflejar sin perder state"| DEV
TRA <-.->|"Cachear resultados transformados"| CACHEEl pipeline de Metro tiene tres fases:
- Resolución — Resolución de módulos al estilo Node.js con selección automática de archivos específicos por plataforma (
foo.ios.ts/foo.android.ts) - Transformación — Babel convierte JSX, TypeScript y ES moderno; los resultados se cachean para inicios más rápidos
- Serialización — Genera el bundle final y lo entrega al dispositivo mediante un servidor HTTP
Notablemente, el HMR (Hot Module Replacement) solo actualiza el grafo de módulos cambiados, reflejando cambios de código instantáneamente mientras preserva el estado de los componentes React. Las compilaciones de producción combinan --minify y la compilación Hermes en bytecode para reducir tanto el tamaño del archivo como el tiempo de inicio.
5. La Estructura por Capas de Expo y las Herramientas de Desarrollo
flowchart TD
APP["Tu App<br/>App.tsx / screens / hooks"]
SDK["Expo SDK<br/>expo-camera / expo-location / expo-router …"]
EMC["expo-modules-core<br/>DSL con seguridad de tipos 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/>Lanzar instantáneamente via QR"]
EASB["EAS Build<br/>Generar .ipa / .apk en la nube"]
EASU["EAS Update<br/>Entregar solo el JS bundle via OTA"]
STORE["App Store / Google Play"]
APP --> SDK
EMC --> SDK
SDK --> RN
RN --> IOS & AND
GO -->|"Alojar en desarrollo"| APP
EASB --> STORE
EASU -->|"Sin revisión App Store"| APPEl SDK de Expo proporciona funcionalidades nativas — cámara, ubicación, notificaciones, sistema de archivos — como paquetes versionados. Sin necesidad de escribir código nativo propio — ese es su mayor valor.
expo-modules-core es un sistema más nuevo que permite el desarrollo declarativo de módulos nativos con DSLs de Swift y Kotlin, integrado con Codegen. Es más seguro en tipos y menos verboso que el antiguo bridge NativeModules, y casi todos los SDKs de terceros de Expo lo usan.
EAS (Expo Application Services) es un término general para Build, Update, Submit, Workflows y más. Este artículo se centra en EAS Build y EAS Update. EAS Build compila binarios nativos en la nube, habilitando builds de iOS sin Mac. EAS Update entrega bundles JS y assets via OTA, permitiendo entrega rápida de cambios que no tocan código nativo.
6. Áreas que Expo no Cubre y la Elección del Workflow
Principales áreas que el SDK de Expo no cubre:
- Bluetooth LE / NFC — Comunicación con periféricos específicos de plataforma
- SDKs personalizados de pago/biométrica — Integración directa de
.framework/.aarde proveedores - Procesamiento de audio/video en tiempo real — WebRTC de bajo nivel u operaciones con codecs personalizados
- Bibliotecas nativas internas — Assets Swift / Kotlin extraídos de apps existentes
- Vistas de cámara personalizadas — Renderizado personalizado con OpenGL / Metal
flowchart TD
START["¿Qué funcionalidad nativa necesitas?"]
START --> Q1{"¿Cubierta por SDK Expo<br/>o biblioteca pública?"}
Q1 -->|"Sí"| MANAGED["Managed Workflow<br/>Sin código nativo necesario<br/>Solo npx expo start"]
Q1 -->|"No"| Q2{"¿Solo cambios de configuración?<br/>(Adiciones Info.plist / AndroidManifest)"}
Q2 -->|"Sí"| PLUGIN["Crear Config Plugin<br/>Agregar a app.json<br/>Aplicado automaticamente por prebuild"]
Q2 -->|"No"| Q3{"¿Cuánto código nativo personalizado?"}
Q3 -->|"Ligero<br/>(envolver SDK existente)"| BARE["Bare Workflow<br/>npx expo prebuild<br/>→ Gestionar ios/ android/ directamente"]
Q3 -->|"Completo<br/>(módulo / UI personalizado)"| MODULE{"¿Qué se construye?"}
MODULE -->|"Lógica<br/>Bluetooth / pago / cripto"| TM["TurboModule<br/>+ Codegen"]
MODULE -->|"Componente UI<br/>Vista cámara personalizada, etc."| FAB["Fabric Component<br/>+ Codegen"]
BARE --> TM & FAB
PLUGIN --> BARELos cambios de configuración menores pueden manejarse con Config Plugins. Pero si se necesita escribir código, generar los directorios ios/ y android/ con npx expo prebuild y pasar al Bare Workflow es el punto de partida práctico.
7. Arquitectura Completa: JSI + Codegen + Implementación Nativa
flowchart TB
subgraph JS["Capa JS (Hermes)"]
APP["Código App / Componentes React"]
SPEC["JS Spec<br/>(TypeScript + TurboModuleRegistry)"]
end
subgraph CODEGEN["Codegen (Auto-generado en tiempo de compilación)"]
GEN_CPP["Clases C++ Abstractas<br/>TurboModule / ComponentDescriptor"]
GEN_SHADOW["Definiciones ShadowNode / Props"]
end
subgraph JSI_LAYER["Capa JSI (C++)"]
JSI["JSI Host Object"]
FABRIC["Fabric Renderer"]
end
subgraph NATIVE_IOS["Implementación Nativa iOS"]
SWIFT["Implementación Swift / Obj-C<br/>(Hereda clase C++ abstracta)"]
SDK_IOS["SDK Interno / .framework Externo<br/>ej: CoreBluetooth / Stripe"]
UIVIEW["Subclase UIView<br/>(Componente UI Personalizado)"]
end
subgraph NATIVE_AND["Implementación Nativa Android"]
KOTLIN["Implementación Kotlin / Java<br/>(Hereda clase C++ abstracta)"]
SDK_AND["SDK Interno / .aar Externo<br/>ej: BluetoothGatt / Stripe"]
ANDROIDVIEW["Subclase View<br/>(Componente UI Personalizado)"]
end
SPEC -->|"Leer definiciones de tipos"| CODEGEN
GEN_CPP --> JSI
GEN_SHADOW --> FABRIC
APP -->|"Llamadas síncronas / asíncronas"| JSI
JSI --> SWIFT & KOTLIN
FABRIC --> UIVIEW & ANDROIDVIEW
SWIFT --> SDK_IOS
KOTLIN --> SDK_ANDLa clave es la dependencia unidireccional: Spec → Codegen → Implementación Nativa. Escribe definiciones de tipos en el lado JS, y Codegen genera clases C++ abstractas en tiempo de compilación. Las implementaciones Swift / Kotlin solo necesitan heredarlas — la consistencia de tipos la garantiza el compilador, eliminando el "acuerdo de caballeros" como fuente de crashes en tiempo de ejecución.
8. Flujo de Implementación TurboModule (Ejemplo Bluetooth)
Nota: El código siguiente es un ejemplo simplificado basado en las guías oficiales de React Native para TurboModule / Codegen / Custom Events. No son citas directas de los ejemplos oficiales, sino una reconstrucción usando Bluetooth como ejemplo.
① Escribir 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');
② Lo que Codegen Genera (Automáticamente)
flowchart LR
SPEC["NativeBluetoothModule.ts<br/>(Escrito por el desarrollador)"]
subgraph AUTO["Código auto-generado"]
CPP["Código C++ glue<br/>Salida JSI / Codegen"]
IOS_H["Código iOS glue<br/>Salida Obj-C++ / header"]
AND_J["Código Android glue<br/>Salida JNI / clase Spec"]
end
subgraph IMPL["Implementado por el desarrollador"]
SWIFT_IMPL["NativeBluetoothModule.swift<br/>Implementa Spec generada, llama CoreBluetooth"]
KOTLIN_IMPL["NativeBluetoothModule.kt<br/>Implementa Spec generada, llama BluetoothGatt"]
end
SPEC --> CPP
SPEC --> IOS_H
SPEC --> AND_J
CPP --> SWIFT_IMPL & KOTLIN_IMPL
IOS_H --> SWIFT_IMPL
AND_J --> KOTLIN_IMPL③ Implementación 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
])
}
}
④ Implementación 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. Flujo de Implementación Fabric Component (Ejemplo Vista Cámara Personalizada)
Si TurboModule maneja la lógica, Fabric Component sirve para incrustar UI nativa en JSX. Necesario para renderizado personalizado con OpenGL / Metal, o reutilizar vistas nativas existentes.
Nota: Lo siguiente también es un ejemplo mínimo para la comprensión. Las rutas de import de Codegen y nombres de tipos varían según la versión de React Native, siempre consulta la documentación oficial de tu versión al implementar.
// 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 mapea Float → CGFloat / float
torchEnabled?: boolean;
onFrameCaptured?: DirectEventHandler<FrameCapturedEvent>;
}
export default codegenNativeComponent<NativeProps>('CameraView');
Codegen genera definiciones C++ para ShadowNode, ComponentDescriptor y Props desde esta Spec. En iOS, implementar un CameraViewComponentView que herede de RCTViewComponentView; en Android, heredar de ReactViewGroup. Desde el lado JS, es tan simple como <CameraView zoom={2.0} torchEnabled />.
10. Integración de SDKs Internos / .framework y .aar Externos
iOS: Referencia Local via CocoaPods
# ios/Podfile
pod 'CompanyPaymentSDK', :path => '../vendor/ios/CompanyPaymentSDK'
# o para xcframework
pod 'CompanyPaymentSDK', :podspec => '../vendor/ios/CompanyPaymentSDK.podspec'
Tras añadirlo al Podfile y ejecutar pod install, se puede usar como una clase Swift normal con import CompanyPaymentSDK. Llamarlo directamente desde la implementación TurboModule.
Android: Maven Local / Referencia .aar
// android/app/build.gradle
dependencies {
implementation files('../vendor/android/company-payment-sdk.aar')
// o un repositorio Maven interno
implementation 'com.company:payment-sdk:1.4.0'
}
11. El Bucle de Desarrollo: Del Inicio a la Verificación
flowchart TD
INIT["npx create-expo-app MyApp<br/>--template blank-typescript"]
subgraph START["npx expo start"]
METRO["Metro Bundler arranca<br/>puerto 8081"]
QR["Código QR + atajos<br/>i = iOS a = Android w = Web"]
end
subgraph DEVICE["Elegir objetivo"]
SIM["iOS Simulator<br/>Incluido con Xcode (solo Mac)"]
EMU["Android Emulator<br/>Android Studio AVD"]
GO["Dispositivo físico + Expo Go<br/>Via LAN o Tunnel"]
end
subgraph NATIVE_BUILD["Solo la primera vez: Build Nativo"]
POD["pod install<br/>iOS CocoaPods"]
GRADLE["Gradle build<br/>Android"]
CODEGEN_RUN["Codegen se ejecuta<br/>Spec → Andamiaje nativo"]
end
BUNDLE["Transferir JS bundle al dispositivo<br/>via servidor HTTP de Metro"]
RENDER["La pantalla se muestra"]
INIT --> START
METRO --> QR
QR --> SIM & EMU & GO
SIM --> NATIVE_BUILD
EMU --> NATIVE_BUILD
NATIVE_BUILD --> BUNDLE
GO --> BUNDLE
BUNDLE --> RENDEREjecuta npx expo start para arrancar Metro, y aparece un código QR con atajos de teclado en el terminal.
- Tecla
i→ iOS Simulator (funciona en Mac con Xcode instalado) - Tecla
a→ Android Emulator (dispositivo virtual creado en Android Studio AVD Manager) - Para dispositivos físicos, escanear el QR en Expo Go — se conecta casi instantáneamente en LAN
Al desencadenar explícitamente un build nativo con npx expo run:ios o npx expo run:android, pod install (iOS) o Gradle (Android) se ejecutan la primera vez y Codegen genera el andamiaje nativo. Las ejecuciones posteriores solo transfieren diferencias del JS bundle y son mucho más rápidas.
12. El Bucle de Cambios en Bare Workflow
flowchart TD
subgraph CHANGE["Ramificación por tipo de cambio"]
JS_ONLY["Solo cambios JS<br/>Componentes / lógica / estilos"]
NATIVE_CHANGE["Cambios de código nativo<br/>Swift / Kotlin / Spec / Podfile"]
end
subgraph JS_LOOP["Bucle de Cambio JS (Rápido)"]
METRO_HMR["Metro HMR<br/>Transferir bundle diferencial<br/>~Cientos de ms"]
SIM_JS["Visible de inmediato en emulador<br/>Estado de la app preservado"]
end
subgraph NATIVE_LOOP["Bucle de Cambio Nativo (Lento)"]
CODEGEN_RUN["Re-ejecutar Codegen<br/>pod install (iOS)<br/>Gradle sync (Android)"]
REBUILD["Rebuild nativo<br/>npx expo run:ios<br/>npx expo run:android"]
SIM_NATIVE["Verificar en emulador / dispositivo"]
end
JS_ONLY --> METRO_HMR --> SIM_JS
NATIVE_CHANGE --> CODEGEN_RUN --> REBUILD --> SIM_NATIVE
SIM_JS -->|"Sin problemas"| DONE["Verificación OK"]
SIM_NATIVE -->|"Sin problemas"| DONE
SIM_JS -->|"Bug encontrado"| DEBUG_JS["Hermes Debugger<br/>React DevTools"]
SIM_NATIVE -->|"Crash nativo"| DEBUG_NATIVE["Xcode / Android Studio<br/>Depurador nativo"]
DEBUG_JS --> JS_ONLY
DEBUG_NATIVE --> NATIVE_CHANGELos cambios solo de JS son manejados por Metro HMR en cientos de milisegundos, preservando el estado.
Los cambios de código nativo requieren un rebuild. El primer build de iOS toma 2–5 minutos, los builds diferenciales unos 30 segundos a un minuto. Android con caché de Gradle es similar; el primer build toma más tiempo.
Esta asimetría es clave para la eficiencia de desarrollo en Bare Workflow: fijar temprano el límite nativo (Spec) y empujar la mayor parte de la implementación al lado JS para reducir la frecuencia de builds nativos pesados.
13. Depuración: Investigación a Través del Límite JS/Nativo
flowchart TD
SYMPTOM["Observar síntomas"]
SYMPTOM --> Q1{"Tipo de crash"}
Q1 -->|"Error JS<br/>Pantalla roja LogBox"| JS_ERR["Investigar con Hermes Debugger<br/>Breakpoints, stack trace<br/>Números de línea TS via Source Map"]
Q1 -->|"Crash nativo<br/>App congela / crashea"| NAT_ERR["Revisar logs de crash"]
Q1 -->|"Funciona pero<br/>resultado incorrecto"| BORDER["Sospechar del límite JS↔Nativo"]
NAT_ERR --> IOS_CRASH["iOS: Xcode<br/>Debug Navigator<br/>+ adjuntar 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["Lado JS: console.log<br/>Verificar valores de retorno TurboModule"]
IOS_CRASH --> INSTRUMENTS["Instruments<br/>Time Profiler / Allocations<br/>Identificar fugas de memoria / CPU"]
AND_CRASH --> PROFILER["Android Studio<br/>Memory / CPU Profiler"]
JS_ERR --> FIXED["Corregir → Visible de inmediato via Metro HMR"]
INSTRUMENTS --> FIXED2["Corregir → Verificar con npx expo run:ios"]
PROFILER --> FIXED3["Corregir → Verificar con npx expo run:android"]
LOG_IOS & LOG_AND & JS_LOG --> BORDER2["Identificar causa raíz<br/>→ Corregir Spec / implementación"]Errores JS: Hermes Debugger
Cmd+D → "Open Debugger" abre Chrome DevTools. Metro proporciona Source Maps, permitiendo colocar breakpoints en los números de línea TypeScript originales — no en el código bundleado. Las excepciones en llamadas TurboModule muestran el stack completo de vuelta al lado JS.
Crashes Nativos: Adjuntar el Depurador de Xcode / Android Studio
# iOS: Adjuntar lldb al simulador en ejecución
npx expo run:ios --configuration Debug
# → Xcode se abre automáticamente y adjunta el depurador
# Android
npx expo run:android
# → "Run > Attach Debugger to Android Process"
Para crashes que solo se reproducen en dispositivos físicos (especialmente Bluetooth, cámara, sensores), el depurador puede adjuntarse de la misma forma a un dispositivo conectado por USB.
Investigación de Límite: Encerrar con Logs
Cuando los datos se corrompen en el límite JS↔Nativo, encerrarlos con logs de ambos lados es el enfoque más rápido.
// Lado Swift
os_log("BluetoothModule.connect called: %{public}@", deviceId)
// → Filtrar con npx react-native log-ios
// Lado Kotlin
Log.d("BluetoothModule", "connect called: $deviceId")
// → Filtrar con npx react-native log-android
// Lado JS
const result = await BluetoothModule.connect(deviceId);
console.log('connect result:', result); // Salida en Metro
React DevTools
npx react-devtools
# → Espera conexión del dispositivo en el puerto 8097
Visualizar el árbol de componentes e inspeccionar (y modificar) props y state en tiempo real.
14. Simuladores vs. Dispositivos Físicos
| Qué se prueba | iOS Simulator | Android Emulator | Dispositivo Físico |
|---|---|---|---|
| UI / Layout | Suficiente | Suficiente | Verificación final |
| Lógica JS / API | Suficiente | Suficiente | No necesario |
| Bluetooth / NFC | Imposible | Imposible | Requerido |
| Cámara / Micrófono | Parcial | Imposible | Requerido |
| Notificaciones push | Parcial | Parcial | Recomendado |
| Rendimiento | Solo referencia | Solo referencia | Requerido |
| Autenticación biométrica | Sim Face ID disponible | Sim huella disponible | Recomendado |
Para el desarrollo con módulos nativos, un enfoque de dos etapas funciona bien: consolidar el comportamiento JS en emuladores, verificar las funcionalidades nativas en dispositivos físicos. Especialmente para Bluetooth y NFC — ya que los emuladores no pueden probarlos — el número de dispositivos y la cobertura de versiones OS impacta directamente la calidad.
Resumen
El Stack Tecnológico Completo
| Capa | Tecnología | Rol |
|---|---|---|
| Bundler | Metro | Transpilación de fuentes, HMR, ramificación de plataforma |
| Motor JS | Hermes | Ejecución bytecode para inicio rápido |
| Bridge JS↔Nativo | JSI | Enlace síncrono via C++ |
| Generación de Código | Codegen | Auto-generación de andamiaje nativo con seguridad de tipos desde la Spec |
| Renderizador UI | Fabric | Renderizado UI síncrono sobre JSI |
| APIs Nativas | TurboModules | Módulos con inicialización perezosa que heredan clases generadas por Codegen |
| Infraestructura Dev | Expo / EAS | SDK, builds en la nube, actualizaciones OTA |
Flujo Completo para Implementación Nativa
① Escribir la JS Spec (definiciones de tipos en TypeScript)
↓
② pod install / gradle build → Codegen auto-genera andamiaje C++
↓
③ Implementar en Swift / Kotlin (llamar SDK interno / bibliotecas externas)
↓
④ npx expo run:ios / run:android → Build nativo & verificación en emulador
↓
⑤ Escribir código de llamada JS → Iteración rápida via Metro HMR
↓
⑥ Errores JS → Hermes Debugger
Crashes nativos → Depurador Xcode / Android Studio
Discrepancias en límite → Encerrar con log-ios / log-android + console.log
↓
⑦ Verificar Bluetooth / cámara / sensores en dispositivos físicos
↓
⑧ EAS Build genera binarios listos para la tienda
La transición de React Native de Bridge a JSI + Codegen lo transformó de "funciona pero frágil" a "con seguridad de tipos y alto rendimiento". Salir de Expo trae costos — builds pesados y escribir la misma lógica en dos lenguajes — pero Codegen hace de la Spec la única fuente de verdad, eliminando el mayor riesgo: los crashes en tiempo de ejecución por incompatibilidades de tipos, desde la fase de diseño.
URLs de Referencia
Nota: Este artículo resume y reconstruye información de las siguientes fuentes. Los ejemplos y explicaciones han sido simplificados para mayor claridad y no son citas directas. Siempre consultar la documentación oficial de la versión que se usa al implementar.
URLs Referenciadas
React Native Oficial
- React Native Nueva Arquitectura (Resumen): https://reactnative.dev/architecture/landing-page
- Resumen JSI & TurboModules: https://reactnative.dev/docs/turbo-native-modules-introduction
- Qué es Codegen: https://reactnative.dev/docs/the-new-architecture/what-is-codegen
- Cómo usar Codegen: https://reactnative.dev/docs/the-new-architecture/using-codegen
- Enviar eventos desde TurboModules: https://reactnative.dev/docs/the-new-architecture/native-modules-custom-events
- Apéndice de definiciones de tipos Codegen: https://reactnative.dev/docs/appendix
- Motor Hermes: https://reactnative.dev/docs/hermes
- Metro (React Native oficial): https://reactnative.dev/docs/metro
Expo Oficial
- Por qué Metro (Expo): https://docs.expo.dev/guides/why-metro/
- Usar Hermes (Expo): https://docs.expo.dev/guides/using-hermes/
- Resumen EAS: https://docs.expo.dev/eas/
- Resumen EAS Build: https://docs.expo.dev/build/introduction/
- Resumen EAS Update: https://docs.expo.dev/eas-update/introduction/
