Native Modules in iOS
- Create custom Native Module header (
RCTCalendarModule.h) and implementation (RCTCalendarModule.m) files:
// RCTCalendarModule.h
#import <React/RCTBridgeModule.h>
@interface RCTCalendarModule : NSObject <RCTBridgeModule>
@endIn iOS a native module is an Objective-C/Swift class that implements the RCTBridgeModule protocol.
💡 Since ObjC does not have language-level support for namespaces like Java or C++, convention is to prepend the class name with a substring.
// RCTCalendarModule.m
#import "RCTCalendarModule.h"
@implementation RCTCalendarModule
RCT_EXPORT_MODULE(CalendarModule); // <- "macro" with the name of the module
@endThe name of the Native Module is an argument to RCT_EXPORT_MODULE macro. This argument is not a string literal. (if you do not specify a name, the JavaScript module name will match the Objective-C class name, with any RCT or RK prefixes removed).
- Explicitly export a Native Method to JS using
RCT_EXPORT_METHODmacro.
💡 Methods written in the
RCT_EXPORT_METHODmacro are asynchronous and the return type is therefore always void. In order to pass a result from aRCT_EXPORT_METHODmethod to JavaScript you can use callbacks or emit events.
RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)name location:(NSString *)location)
{
// ...
}You can use
RCTLogAPI from<React/RCTLog.h>to consolellog the method, check if it's been invoked.
You can export
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHODs as well. The methods can only returnnilor JSON values, like NSNumber, NSString, NSArray, NSDictionary. This is not recommended though, as it has strong performance penalties and disables Chrome Debugger.
- (optional) Add Callbacks.
Native modules support callbacks to pass data from Obj-C to JS for async methods and to asynchronously execute JS from native part.
More on the topic here.
- Import the native module
import { NativeModules } from "react-native";
const { CalendarModule } = NativeModules;Sending events to JS
Signaling events to JS without being invoked directly
- Update your header class to import
RCTEventEmitterand subclassRCTEventEmitter:
// CalendarModule.h
#import <React/RCTBridgeModule.h>#import <React/RCTEventEmitter.h>
@interface CalendarModule : RCTEventEmitter <RCTBridgeModule>@end- Subscribe to these events via a new
NativeEventEmitterinstance on JS side. - Optimize the workload (e.g. by unsubscribing from upstream notifications or pausing background tasks), you can override
startObservingandstopObservingin yourRCTEventEmittersubclass).
@implementation CalendarManager{ 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}]; }}Additional topics:
- Threading
- Dependency Injection
- Exporting in swift
- a bit different, additional configuration is needed, but in general - the class and functions need to be exported properly to the Objective-C runtime (
@objc)
- a bit different, additional configuration is needed, but in general - the class and functions need to be exported properly to the Objective-C runtime (
Promises
Native modules can also fulfill a promise, which can simplify your JavaScript, especially when using ES2016's async/await syntax. When the last parameter of a native module method is a RCTPromiseResolveBlock and RCTPromiseRejectBlock, its corresponding JS method will return a JS Promise object.