We designed React Native such that it is possible for you to write real native code and have access to the full power of the platform. This is a more advanced feature and we don't expect it to be part of the usual development process, however it is essential that it exists. If React Native doesn't support a native feature that you need, you should be able to build it yourself.
This is a more advanced guide that shows how to build a native module. It assumes the reader knows Objective-C or Swift and core libraries (Foundation, UIKit).
A native module is just an Objective-C class that implements the
RCTBridgeModule protocol. If you are wondering, RCT is an abbreviation of ReaCT.
In addition to implementing the
RCTBridgeModule protocol, your class must also include the
React Native will not expose any methods of
The CalendarManager module is instantiated on the Objective-C side using a [CalendarManager new] call. The return type of bridge methods is always
RCT_EXPORT_METHOD supports all standard JSON object types, such as:
NSArray) of any types from this list
NSDictionary) with string keys and values of any type from this list
But it also works with any type that is supported by the
RCTConvert class (see
RCTConvert for details). The
RCTConvert helper functions all accept a JSON value as input and map it to a native Objective-C type or class.
or like this:
But by using the automatic type conversion feature, we can skip the manual conversion step completely, and just write:
And both values would get converted correctly to the native
NSDate. A bad value, like an
Array, would generate a helpful "RedBox" error message.
CalendarManager.addEvent method gets more and more complex, the number of arguments will grow. Some of them might be optional. In this case it's worth considering changing the API a little bit to accept a dictionary of event attributes, like this:
NOTE: About array and map
NSArraycontaining a mix of
NSString. For arrays,
RCTConvertprovides some typed collections you can use in your method declaration, such as
UIColorArray. For maps, it is the developer's responsibility to check the value types individually by manually calling
This section is more experimental than others because we don't have a solid set of best practices around callbacks yet.
null when there is no error) and the rest are the results of the function.
A native module should invoke its callback exactly once. It's okay to store the callback and invoke it later. This pattern is often used to wrap iOS APIs that require delegates - see
RCTAlertManager for an example. If the callback is never invoked, some memory is leaked. If both
onFail callbacks are passed, you should only invoke one of them.
Error objects in the future.
Native modules can also fulfill a promise, which can simplify your code, especially when using ES2016's
async/await syntax. When the last parameters of a bridged native method are an
RCTPromiseRejectBlock, its corresponding JS method will return a JS Promise object.
Refactoring the above code to use a promise instead of callbacks looks like this:
await keyword within an async function to call it and wait for its result:
The native module should not have any assumptions about what thread it is being called on. React Native invokes native modules methods on a separate serial GCD queue, but this is an implementation detail and might change. The
- (dispatch_queue_t)methodQueue method allows the native module to specify which queue its methods should be run on. For example, if it needs to use a main-thread-only iOS API, it should specify this via:
Similarly, if an operation may take a long time to complete, the native module should not block and can specify it's own queue to run operations on. For example, the
RCTAsyncLocalStorage module creates it's own queue so the React queue isn't blocked waiting on potentially slow disk access:
methodQueue will be shared by all of the methods in your module. If just one of your methods is long-running (or needs to be run on a different queue than the others for some reason), you can use
dispatch_async inside the method to perform that particular method's code on another queue, without affecting the others:
NOTE: Sharing dispatch queues between modules
methodQueuemethod will be called once when the module is initialized, and then retained by the bridge, so there is no need to retain the queue yourself, unless you wish to make use of it within your module. However, if you wish to share the same queue between multiple modules then you will need to ensure that you retain and return the same queue instance for each of them; merely returning a queue of the same name for each won't work.
The bridge initializes any registered RCTBridgeModules automatically, however you may wish to instantiate your own module instances (so you may inject dependencies, for example).
You can do this by creating a class that implements the RCTBridgeDelegate Protocol, initializing an RCTBridge with the delegate as an argument and initialising a RCTRootView with the initialized bridge.
Note that the constants are exported only at initialization time, so if you change
Enums that are defined via
NS_ENUM cannot be used as method arguments without first extending RCTConvert.
In order to export the following
You must create a class extension of RCTConvert like so:
You can then define methods and export your enum constants like this:
Your enum will then be automatically unwrapped using the selector provided (
integerValue in the above example) before being passed to your exported method.
suppportEvents and call
NativeEventEmitter instance around your module.
You will receive a warning if you expend resources unnecessarily by emitting an event while there are no listeners. To avoid this, and to optimize your module's workload (e.g. by unsubscribing from upstream notifications or pausing background tasks), you can override
stopObserving in your
Swift doesn't have support for macros so exposing it to React Native requires a bit more setup but works relatively the same.
Let's say we have the same
CalendarManager but as a Swift class:
NOTE: It is important to use the @objc modifiers to ensure the class and functions are exported properly to the Objective-C runtime.
Then create a private implementation file that will register the required information with the React Native bridge:
For those of you new to Swift and Objective-C, whenever you mix the two languages in an iOS project, you will also need an additional bridging file, known as a bridging header, to expose the Objective-C files to Swift. Xcode will offer to create this header file for you if you add your Swift file to your app through the Xcode
File>New File menu option. You will need to import
RCTBridgeModule.h in this header file.
You can also use
You can edit the content above on GitHub and send us a pull request!