Saltar al contenido principal
Versión: 0.79

Módulos nativos de iOS

Traducción Beta No Oficial

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

Traducción Beta No Oficial

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

información

Native Module y Native Components son nuestras tecnologías estables utilizadas por la arquitectura heredada. Serán desaprobadas en el futuro cuando la Nueva Arquitectura sea estable. La Nueva Arquitectura utiliza Turbo Native Module y Fabric Native Components para lograr resultados similares.

Bienvenido a los módulos nativos para iOS. Te recomendamos comenzar leyendo la Introducción a los módulos nativos para entender qué son los módulos nativos.

Crear un módulo nativo de calendario

En esta guía crearás un módulo nativo, CalendarModule, que te permitirá acceder a las APIs de calendario de Apple desde JavaScript. Al final, podrás llamar CalendarModule.createCalendarEvent('Dinner Party', 'My House'); desde JavaScript, invocando un método nativo que crea un evento de calendario.

Configuración inicial

Para comenzar, abre el proyecto iOS de tu aplicación React Native en Xcode. Puedes encontrar tu proyecto iOS aquí dentro de una app React Native:

Image of opening up an iOS project within a React Native app inside of Xcode.
Image of where you can find your iOS project

Recomendamos usar Xcode para escribir tu código nativo. Xcode está diseñado para desarrollo iOS y te ayudará a resolver rápidamente errores menores como sintaxis de código.

Crear archivos de módulo nativo

El primer paso es crear nuestros archivos de cabecera e implementación principales. Crea un nuevo archivo llamado RCTCalendarModule.h

Image of creating a class called  RCTCalendarModule.h.
Image of creating a custom native module file within the same folder as AppDelegate

y añade lo siguiente:

objectivec
//  RCTCalendarModule.h
#import <React/RCTBridgeModule.h>
@interface RCTCalendarModule : NSObject <RCTBridgeModule>
@end

Puedes usar cualquier nombre que se ajuste al módulo nativo que estás construyendo. Nombra la clase RCTCalendarModule ya que estás creando un módulo de calendario. Como Objective-C no tiene soporte a nivel de lenguaje para namespaces como Java o C++, la convención es anteponer al nombre de clase un prefijo. Este podría ser una abreviatura de tu aplicación o infraestructura. RCT, en este ejemplo, se refiere a React.

Como verás a continuación, la clase CalendarModule implementa el protocolo RCTBridgeModule. Un módulo nativo es una clase Objective-C que implementa el protocolo RCTBridgeModule.

Ahora, comencemos a implementar el módulo nativo. Crea el archivo de implementación correspondiente usando Cocoa Touch Class en Xcode, RCTCalendarModule.m, en la misma carpeta e incluye este contenido:

objectivec
// RCTCalendarModule.m
#import "RCTCalendarModule.h"

@implementation RCTCalendarModule

// To export a module named RCTCalendarModule
RCT_EXPORT_MODULE();

@end

Nombre del módulo

Por ahora, tu módulo nativo RCTCalendarModule.m solo contiene la macro RCT_EXPORT_MODULE, que exporta y registra la clase del módulo con React Native. La macro RCT_EXPORT_MODULE también acepta un argumento opcional que especifica el nombre accesible en tu código JavaScript.

Este argumento no es un string literal. En el ejemplo siguiente se pasa RCT_EXPORT_MODULE(CalendarModuleFoo), no RCT_EXPORT_MODULE("CalendarModuleFoo").

objectivec
// To export a module named CalendarModuleFoo
RCT_EXPORT_MODULE(CalendarModuleFoo);

El módulo nativo podrá entonces accederse en JS así:

tsx
const {CalendarModuleFoo} = ReactNative.NativeModules;

Si no especificas un nombre, el nombre del módulo en JavaScript coincidirá con el nombre de la clase Objective-C, eliminando los prefijos "RCT" o "RK".

Sigamos el ejemplo inferior y llamemos RCT_EXPORT_MODULE sin argumentos. Como resultado, el módulo se expondrá en React Native como CalendarModule, pues ese es el nombre de la clase Objective-C sin el prefijo RCT.

objectivec
// Without passing in a name this will export the native module name as the Objective-C class name with “RCT” removed
RCT_EXPORT_MODULE();

El módulo nativo podrá entonces accederse en JS así:

tsx
const {CalendarModule} = ReactNative.NativeModules;

Exportar un método nativo a JavaScript

React Native no expondrá ningún método de un módulo nativo a JavaScript a menos que se le indique explícitamente. Esto se hace usando la macro RCT_EXPORT_METHOD. Los métodos escritos con RCT_EXPORT_METHOD son asíncronos y su tipo de retorno es siempre void. Para pasar resultados desde un método RCT_EXPORT_METHOD a JavaScript puedes usar callbacks o emitir eventos (veremos esto después). Configuremos un método nativo para nuestro módulo CalendarModule usando RCT_EXPORT_METHOD. Llamémoslo createCalendarEvent() y por ahora aceptará argumentos nombre y ubicación como strings. Las opciones de tipos de argumentos se cubrirán pronto.

objectivec
RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)name location:(NSString *)location)
{
}

Nota: La macro RCT_EXPORT_METHOD no será necesaria con TurboModules a menos que tu método dependa de la conversión de argumentos RCT (ver tipos de argumentos abajo). Eventualmente, React Native eliminará RCT_EXPORT_MACRO,, por lo que desaconsejamos usar RCTConvert. En su lugar, puedes hacer la conversión de argumentos dentro del cuerpo del método.

Antes de desarrollar la funcionalidad del método createCalendarEvent(), añade un registro en la consola dentro del método para confirmar que se ha invocado desde JavaScript en tu aplicación React Native. Utiliza las APIs RCTLog de React. Importemos ese encabezado en la parte superior de tu archivo y luego añadamos la llamada de registro.

objectivec
#import <React/RCTLog.h>
RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)name location:(NSString *)location)
{
RCTLogInfo(@"Pretending to create an event %@ at %@", name, location);
}

Métodos Síncronos

Puedes usar RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD para crear un método nativo síncrono.

objectivec
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getName)
{
return [[UIDevice currentDevice] name];
}

El tipo de retorno de este método debe ser de tipo objeto (id) y debe ser serializable a JSON. Esto significa que el hook solo puede retornar nil o valores JSON (por ejemplo, NSNumber, NSString, NSArray, NSDictionary).

Actualmente, no recomendamos usar métodos síncronos, ya que llamar métodos de forma síncrona puede tener fuertes penalizaciones de rendimiento e introducir errores relacionados con hilos en tus módulos nativos. Adicionalmente, ten en cuenta que si decides usar RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD, tu aplicación ya no podrá utilizar el depurador de Google Chrome. Esto se debe a que los métodos síncronos requieren que la máquina virtual de JavaScript (JS VM) comparta memoria con la aplicación. Para el depurador de Google Chrome, React Native se ejecuta dentro de la JS VM en Google Chrome y se comunica de forma asíncrona con los dispositivos móviles mediante WebSockets.

Prueba lo que has construido

En este punto has configurado la estructura básica de tu módulo nativo en iOS. Pruébalo accediendo al módulo nativo e invocando su método exportado en JavaScript.

Encuentra un lugar en tu aplicación donde quieras añadir una llamada al método createCalendarEvent() del módulo nativo. A continuación se muestra un ejemplo de un componente, NewModuleButton, que puedes añadir en tu app. Puedes invocar el módulo nativo dentro de la función onPress() de NewModuleButton.

tsx
import React from 'react';
import {Button} from 'react-native';

const NewModuleButton = () => {
const onPress = () => {
console.log('We will invoke the native module here!');
};

return (
<Button
title="Click to invoke your native module!"
color="#841584"
onPress={onPress}
/>
);
};

export default NewModuleButton;

Para acceder a tu módulo nativo desde JavaScript, primero debes importar NativeModules desde React Native:

tsx
import {NativeModules} from 'react-native';

Luego puedes acceder al módulo nativo CalendarModule desde NativeModules.

tsx
const {CalendarModule} = NativeModules;

Ahora que tienes disponible el módulo nativo CalendarModule, puedes invocar tu método nativo createCalendarEvent(). A continuación se muestra cómo agregarlo al método onPress() en NewModuleButton:

tsx
const onPress = () => {
CalendarModule.createCalendarEvent('testName', 'testLocation');
};

El paso final es recompilar la aplicación React Native para tener disponible el último código nativo (¡con tu nuevo módulo nativo!). En tu línea de comandos, ubicada en la carpeta de la aplicación React Native, ejecuta lo siguiente:

shell
npm run ios

Construyendo durante la iteración

Mientras trabajas en estas guías e iteras sobre tu módulo nativo, necesitarás recompilar nativamente tu aplicación para acceder a tus cambios más recientes desde JavaScript. Esto se debe a que el código que estás escribiendo reside en la parte nativa de tu aplicación. Mientras el paquete metro de React Native puede detectar cambios en JavaScript y recompilar el bundle JS sobre la marcha, no lo hará para el código nativo. Por lo tanto, si quieres probar tus últimos cambios nativos, debes recompilar usando el comando anterior.

Recapitulación ✨

Ahora deberías poder invocar el método createCalendarEvent() de tu módulo nativo desde JavaScript. Como estás usando RCTLog en la función, puedes confirmar que tu método nativo se está invocando activando el modo de depuración en tu app y revisando la consola JS en Chrome o el depurador Flipper en la app móvil. Deberías ver tu mensaje RCTLogInfo(@"Pretending to create an event %@ at %@", name, location); cada vez que invoques el método del módulo nativo.

Image of logs.
Image of iOS logs in Flipper

En este punto has creado un módulo nativo de iOS e invocado un método desde JavaScript en tu aplicación React Native. Puedes continuar leyendo para aprender más sobre temas como los tipos de argumentos que aceptan los métodos de tu módulo nativo y cómo configurar callbacks y promesas dentro de él.

Más allá de un módulo nativo de calendario

Mejor exportación de módulos nativos

Importar tu módulo nativo extrayéndolo de NativeModules como arriba es un poco engorroso.

Para evitar que los usuarios de tu módulo nativo tengan que hacer esto cada vez que accedan a él, puedes crear un envoltorio JavaScript. Crea un nuevo archivo JavaScript llamado NativeCalendarModule.js con el siguiente contenido:

tsx
/**
* This exposes the native CalendarModule module as a JS module. This has a
* function 'createCalendarEvent' which takes the following parameters:

* 1. String name: A string representing the name of the event
* 2. String location: A string representing the location of the event
*/
import {NativeModules} from 'react-native';
const {CalendarModule} = NativeModules;
export default CalendarModule;

Este archivo JavaScript también se convierte en un buen lugar para añadir funcionalidades del lado de JavaScript. Por ejemplo, si usas un sistema de tipos como TypeScript, puedes añadir anotaciones de tipos para tu módulo nativo aquí. Aunque React Native aún no soporta seguridad de tipos de nativo a JS, con estas anotaciones todo tu código JS será tipo seguro. Estas anotaciones también facilitarán la transición a módulos nativos tipo seguro en el futuro. Aquí un ejemplo de cómo añadir seguridad de tipos al Módulo de Calendario:

tsx
/**
* This exposes the native CalendarModule module as a JS module. This has a
* function 'createCalendarEvent' which takes the following parameters:
*
* 1. String name: A string representing the name of the event
* 2. String location: A string representing the location of the event
*/
import {NativeModules} from 'react-native';
const {CalendarModule} = NativeModules;
interface CalendarInterface {
createCalendarEvent(name: string, location: string): void;
}
export default CalendarModule as CalendarInterface;

En tus otros archivos JavaScript puedes acceder al módulo nativo e invocar su método así:

tsx
import NativeCalendarModule from './NativeCalendarModule';
NativeCalendarModule.createCalendarEvent('foo', 'bar');

Nota: Esto asume que el lugar desde donde importas CalendarModule está en la misma jerarquía que NativeCalendarModule.js. Actualiza la importación relativa según sea necesario.

Tipos de Argumentos

Cuando se invoca un método de módulo nativo en JavaScript, React Native convierte los argumentos de objetos JS a sus análogos en Objective-C/Swift. Por ejemplo, si tu método de módulo nativo en Objective-C acepta un NSNumber, en JS debes llamar al método con un número. React Native manejará la conversión. Aquí una lista de los tipos de argumentos soportados y sus equivalentes en JavaScript:

Objective-CJavaScript
NSStringstring, ?string
BOOLboolean
doublenumber
NSNumber?number
NSArrayArray, ?Array
NSDictionaryObject, ?Object
RCTResponseSenderBlockFunction (success)
RCTResponseSenderBlock, RCTResponseErrorBlockFunction (failure)
RCTPromiseResolveBlock, RCTPromiseRejectBlockPromise

Los siguientes tipos están soportados actualmente pero no lo estarán en TurboModules. Evita usarlos:

  • Function (failure) -> RCTResponseErrorBlock
  • Number -> NSInteger
  • Number -> CGFloat
  • Number -> float

Para iOS, también puedes escribir métodos de módulo nativo con cualquier tipo de argumento soportado por la clase RCTConvert (ver RCTConvert para detalles). Las funciones auxiliares de RCTConvert aceptan valores JSON como entrada y los mapean a tipos o clases nativas de Objective-C.

Exportar constantes

Un módulo nativo puede exportar constantes sobrescribiendo el método nativo constantsToExport(). Aquí se sobrescribe constantsToExport() y retorna un diccionario con una propiedad de nombre de evento predeterminado, accesible en JavaScript así:

objectivec
- (NSDictionary *)constantsToExport
{
return @{ @"DEFAULT_EVENT_NAME": @"New Event" };
}

La constante puede entonces accederse invocando getConstants() en el módulo nativo en JS de esta forma:

tsx
const {DEFAULT_EVENT_NAME} = CalendarModule.getConstants();
console.log(DEFAULT_EVENT_NAME);

Técnicamente es posible acceder a constantes exportadas en constantsToExport() directamente desde el objeto NativeModule. Esto dejará de soportarse en TurboModules, por lo que recomendamos migrar al enfoque anterior para evitar retrabajos futuros.

Nota: Las constantes se exportan solo en tiempo de inicialización. Si modificas valores de constantsToExport() en tiempo de ejecución, no afectará el entorno JavaScript.

En iOS, si sobrescribes constantsToExport(), debes implementar + requiresMainQueueSetup para indicar si tu módulo necesita inicializarse en el hilo principal antes de ejecutar código JavaScript. De lo contrario verás una advertencia indicando que en el futuro tu módulo podría inicializarse en un hilo secundario a menos que excluyas explícitamente con + requiresMainQueueSetup:. Si tu módulo no requiere acceso a UIKit, responde a + requiresMainQueueSetup con NO.

Callbacks

Los módulos nativos también soportan un tipo especial de argumento: callbacks. Se usan para pasar datos de Objective-C a JavaScript en métodos asíncronos y también para ejecutar JS de forma asíncrona desde el lado nativo.

En iOS, los callbacks se implementan usando el tipo RCTResponseSenderBlock. Aquí se añade el parámetro callback myCallBack a createCalendarEventMethod():

objectivec
RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)title
location:(NSString *)location
myCallback:(RCTResponseSenderBlock)callback)

Puedes entonces invocar el callback en tu función nativa, pasando cualquier resultado a JavaScript en un array. Nota que RCTResponseSenderBlock acepta solo un argumento: un array de parámetros para pasar al callback de JavaScript. Aquí pasarás el ID de un evento creado previamente.

Es importante destacar que el callback no se invoca inmediatamente después de que la función nativa finaliza—recuerda que la comunicación es asíncrona.

objectivec
RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)title location:(NSString *)location callback: (RCTResponseSenderBlock)callback)
{
NSInteger eventId = ...
callback(@[@(eventId)]);

RCTLogInfo(@"Pretending to create an event %@ at %@", title, location);
}

Este método podría entonces accederse en JavaScript de la siguiente manera:

tsx
const onSubmit = () => {
CalendarModule.createCalendarEvent(
'Party',
'04-12-2020',
eventId => {
console.log(`Created a new event with id ${eventId}`);
},
);
};

Un módulo nativo debe invocar su callback solo una vez. Sin embargo, puede almacenar el callback e invocarlo más tarde. Este patrón se usa a menudo para encapsular APIs de iOS que requieren delegados— consulta RCTAlertManager como ejemplo. Si el callback nunca se invoca, se produce una fuga de memoria.

Existen dos enfoques para manejar errores con callbacks. El primero es seguir la convención de Node y tratar el primer argumento pasado al array de callback como un objeto de error.

objectivec
RCT_EXPORT_METHOD(createCalendarEventCallback:(NSString *)title location:(NSString *)location callback: (RCTResponseSenderBlock)callback)
{
NSNumber *eventId = [NSNumber numberWithInt:123];
callback(@[[NSNull null], eventId]);
}

En JavaScript, puedes entonces verificar el primer argumento para detectar si se pasó un error:

tsx
const onPress = () => {
CalendarModule.createCalendarEventCallback(
'testName',
'testLocation',
(error, eventId) => {
if (error) {
console.error(`Error found! ${error}`);
}
console.log(`event id ${eventId} returned`);
},
);
};

Otra opción es usar dos callbacks separados: onFailure y onSuccess.

objectivec
RCT_EXPORT_METHOD(createCalendarEventCallback:(NSString *)title
location:(NSString *)location
errorCallback: (RCTResponseSenderBlock)errorCallback
successCallback: (RCTResponseSenderBlock)successCallback)
{
@try {
NSNumber *eventId = [NSNumber numberWithInt:123];
successCallback(@[eventId]);
}

@catch ( NSException *e ) {
errorCallback(@[e]);
}
}

Entonces en JavaScript puedes añadir callbacks separados para respuestas de error y éxito:

tsx
const onPress = () => {
CalendarModule.createCalendarEventCallback(
'testName',
'testLocation',
error => {
console.error(`Error found! ${error}`);
},
eventId => {
console.log(`event id ${eventId} returned`);
},
);
};

Si deseas pasar objetos tipo error a JavaScript, usa RCTMakeError de RCTUtils.h.. Actualmente esto solo pasa un diccionario con forma de Error a JavaScript, pero React Native planea generar automáticamente objetos Error reales de JavaScript en el futuro. También puedes proporcionar un argumento RCTResponseErrorBlock, usado para callbacks de error que aceptan un NSError \* object. Ten en cuenta que este tipo de argumento no será compatible con TurboModules.

Promesas

Los módulos nativos también pueden resolver promesas, lo que simplifica tu código JavaScript, especialmente al usar sintaxis async/await de ES2016. Cuando el último parámetro de un método de módulo nativo es un RCTPromiseResolveBlock y RCTPromiseRejectBlock, su método JS correspondiente devolverá un objeto Promise de JavaScript.

Refactorizar el código anterior para usar promesas en lugar de callbacks luce así:

objectivec
RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)title
location:(NSString *)location
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSInteger eventId = createCalendarEvent();
if (eventId) {
resolve(@(eventId));
} else {
reject(@"event_failure", @"no event id returned", nil);
}
}

La contraparte en JavaScript de este método devuelve una Promise. Esto significa que puedes usar la palabra clave await dentro de una función asíncrona para llamarlo y esperar su resultado:

tsx
const onSubmit = async () => {
try {
const eventId = await CalendarModule.createCalendarEvent(
'Party',
'my house',
);
console.log(`Created a new event with id ${eventId}`);
} catch (e) {
console.error(e);
}
};

Envío de eventos a JavaScript

Los módulos nativos pueden señalar eventos a JavaScript sin ser invocados directamente. Por ejemplo, podrías querer notificar a JavaScript un recordatorio de que un evento del calendario nativo de iOS ocurrirá pronto. La forma preferida es crear una subclase de RCTEventEmitter, implementar supportedEvents y llamar a self sendEventWithName:

Actualiza tu clase en el archivo de cabecera para importar RCTEventEmitter y heredar de RCTEventEmitter:

objectivec
//  CalendarModule.h

#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>

@interface CalendarModule : RCTEventEmitter <RCTBridgeModule>
@end

El código JavaScript puede suscribirse a estos eventos creando una nueva instancia de NativeEventEmitter alrededor de tu módulo.

Recibirás una advertencia si gastas recursos innecesariamente emitiendo eventos sin listeners activos. Para evitarlo y optimizar la carga de trabajo de tu módulo (ej. cancelando suscripciones a notificaciones o pausando tareas en segundo plano), puedes sobrescribir startObserving y stopObserving en tu subclase de RCTEventEmitter.

objectivec
@implementation CalendarModule
{
bool hasListeners;
}

// Will be called when this module's first listener is added.
-(void)startObserving {
hasListeners = YES;
// Set up any upstream listeners or background tasks as necessary
}

// Will be called when this module's last listener is removed, or on dealloc.
-(void)stopObserving {
hasListeners = NO;
// Remove upstream listeners, stop unnecessary background tasks
}

- (void)calendarEventReminderReceived:(NSNotification *)notification
{
NSString *eventName = notification.userInfo[@"name"];
if (hasListeners) {// Only send events if anyone is listening
[self sendEventWithName:@"EventReminder" body:@{@"name": eventName}];
}
}

Manejo de hilos (Threading)

A menos que el módulo nativo proporcione su propia cola de métodos, no debe hacer suposiciones sobre en qué hilo se está ejecutando. Actualmente, si un módulo nativo no proporciona una cola de métodos, React Native creará una cola GCD separada e invocará sus métodos allí. Ten en cuenta que esto es un detalle de implementación y podría cambiar. Si deseas proporcionar explícitamente una cola de métodos para un módulo nativo, sobrescribe el método (dispatch_queue_t) methodQueue. Por ejemplo, si necesita usar una API de iOS exclusiva del hilo principal, debe especificarlo mediante:

objectivec
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}

De manera similar, si una operación puede tardar mucho en completarse, el módulo nativo puede especificar su propia cola para ejecutar operaciones. Actualmente, React Native proporciona una cola de métodos separada para tu módulo nativo, pero esto es un detalle de implementación que no deberías dar por sentado. Si no proporcionas tu propia cola de métodos, en el futuro las operaciones de larga duración de tu módulo nativo podrían bloquear llamadas asíncronas en otros módulos nativos no relacionados. Por ejemplo, el módulo RCTAsyncLocalStorage crea su propia cola para evitar que la cola de React se bloquee esperando accesos a disco potencialmente lentos.

objectivec
- (dispatch_queue_t)methodQueue
{
return dispatch_queue_create("com.facebook.React.AsyncLocalStorageQueue", DISPATCH_QUEUE_SERIAL);
}

La methodQueue especificada será compartida por todos los métodos de tu módulo. Si solo uno de tus métodos es de larga duración (o necesita ejecutarse en una cola diferente por algún motivo), puedes usar dispatch_async dentro del método para ejecutar ese código específico en otra cola, sin afectar a los demás:

objectivec
RCT_EXPORT_METHOD(doSomethingExpensive:(NSString *)param callback:(RCTResponseSenderBlock)callback)
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Call long-running code on background thread
...
// You can invoke callback from any thread/queue
callback(@[...]);
});
}

Compartir colas de despacho entre módulos

El método methodQueue se llamará una vez durante la inicialización del módulo y será retenido por React Native, así que no necesitas mantener una referencia a la cola a menos que quieras usarla dentro de tu módulo. Sin embargo, si deseas compartir la misma cola entre varios módulos, deberás asegurarte de retener y devolver la misma instancia de cola para cada uno.

Inyección de dependencias

React Native crea e inicializa automáticamente todos los módulos nativos registrados. Pero puedes crear e inicializar tus propias instancias de módulos para, por ejemplo, inyectar dependencias.

Puedes lograrlo creando una clase que implemente el protocolo RCTBridgeDelegate, inicializando un RCTBridge con este delegado como argumento y luego inicializando un RCTRootView con el puente creado.

objectivec
id<RCTBridgeDelegate> moduleInitialiser = [[classThatImplementsRCTBridgeDelegate alloc] init];

RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:moduleInitialiser launchOptions:nil];

RCTRootView *rootView = [[RCTRootView alloc]
initWithBridge:bridge
moduleName:kModuleName
initialProperties:nil];

Exportando Swift

Swift no soporta macros, por lo que exponer módulos nativos y sus métodos a JavaScript en React Native requiere más configuración. Sin embargo, el funcionamiento es similar. Imagina que tienes el mismo CalendarModule pero como clase Swift:

swift
// CalendarModule.swift

@objc(CalendarModule)
class CalendarModule: NSObject {

@objc(addEvent:location:date:)
func addEvent(_ name: String, location: String, date: NSNumber) -> Void {
// Date is ready to use!
}

@objc
func constantsToExport() -> [String: Any]! {
return ["someKey": "someValue"]
}

}

Es crucial usar los modificadores @objc para garantizar que la clase y funciones se exporten correctamente al runtime de Objective-C.

Luego crea un archivo de implementación privado que registrará la información requerida con React Native:

objectivec
// CalendarModuleBridge.m
#import <React/RCTBridgeModule.h>

@interface RCT_EXTERN_MODULE(CalendarModule, NSObject)

RCT_EXTERN_METHOD(addEvent:(NSString *)name location:(NSString *)location date:(nonnull NSNumber *)date)

@end

Para quienes son nuevos en Swift y Objective-C, cuando mezclas ambos lenguajes en un proyecto iOS, necesitarás un archivo puente adicional (bridging header) para exponer los archivos Objective-C a Swift. Xcode te ofrecerá crear este encabezado si añades tu archivo Swift mediante la opción de menú File>New File. Deberás importar RCTBridgeModule.h en este archivo de encabezado.

objectivec
// CalendarModule-Bridging-Header.h
#import <React/RCTBridgeModule.h>

También puedes usar RCT_EXTERN_REMAP_MODULE y _RCT_EXTERN_REMAP_METHOD para modificar el nombre JavaScript del módulo o métodos exportados. Para más detalles, consulta RCTBridgeModule.

Importante para módulos de terceros: Las bibliotecas estáticas con Swift solo son compatibles desde Xcode 9. Para que el proyecto se compile al usar Swift en bibliotecas estáticas incluidas en el módulo, tu aplicación principal debe contener código Swift y un bridging header. Si tu aplicación no tiene código Swift, una solución es añadir un archivo .swift vacío y un bridging header vacío.

Nombres de Métodos Reservados

invalidate()

Los módulos nativos pueden implementar el protocolo RCTInvalidating en iOS definiendo el método invalidate(). Este método puede invocarse cuando el puente nativo se invalida (ej: durante recargas en modo desarrollo). Utiliza este mecanismo para realizar limpiezas necesarias en tu módulo nativo.