Enabling TurboModule on iOS
This documentation is still experimental and details are subject to changes as we iterate. Feel free to share your feedback on the discussion inside the working group for this page.
Moreover, it contains several manual steps. Please note that this won't be representative of the final developer experience once the New Architecture is stable. We're working on tools, templates and libraries to help you get started fast on the New Architecture, without having to go through the whole setup.
Make sure your application meets all the prerequisites.
1. Provide a TurboModuleManager Delegate
Add the following imports at the top of your bridge delegate (e.g. AppDelegate.mm
):
#import <ReactCommon/RCTTurboModuleManager.h>
#import <React/CoreModulesPlugins.h>
You will also need to declare that your AppDelegate conforms to the RCTTurboModuleManagerDelegate
protocol, as well as create an instance variable for our Turbo Module manager:
@interface AppDelegate () <RCTCxxBridgeDelegate, RCTTurboModuleManagerDelegate> {
// ...
RCTTurboModuleManager *_turboModuleManager;
}
@end
To conform to the RCTTurboModuleManagerDelegate
protocol, you will implement these three methods:
getModuleClassFromName:
- This method should return the Class for a native module. You may use theRCTCoreModulesClassProvider()
method to handle the default, core modules.getTurboModule:jsInvoker:
- This should returnnullptr
. This method may be used later to support C++ TurboModules.getModuleInstanceFromClass:moduleClass:
- This method allows you to perform any side-effects when your TurboModules are initialized. This is the TurboModule analogue to your bridge delegate’sextraModulesForBridge
method. At this time, you’ll need to initialize the default RCTNetworking and RCTImageLoader modules as indicated below.
TurboModuleManagerDelegate Example
Take note of getModuleInstanceFromClass:
in the following example, as it includes some necessary instantiation of several core modules that you will need to include in your application. Eventually, this may not be required.
// ...
#import <React/RCTDataRequestHandler.h>
#import <React/RCTHTTPRequestHandler.h>
#import <React/RCTFileRequestHandler.h>
#import <React/RCTNetworking.h>
#import <React/RCTImageLoader.h>
#import <React/RCTGIFImageDecoder.h>
#import <React/RCTLocalAssetImageLoader.h>
#import <React/CoreModulesPlugins.h>
#import <ReactCommon/RCTTurboModuleManager.h>
// ...
#pragma mark RCTTurboModuleManagerDelegate
- (Class)getModuleClassFromName:(const char *)name
{
return RCTCoreModulesClassProvider(name);
}
- (std::shared_ptr<facebook::react::TurboModule>)
getTurboModule:(const std::string &)name
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker {
return nullptr;
}
- (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
{
// Set up the default RCTImageLoader and RCTNetworking modules.
if (moduleClass == RCTImageLoader.class) {
return [[moduleClass alloc] initWithRedirectDelegate:nil
loadersProvider:^NSArray<id<RCTImageURLLoader>> *(RCTModuleRegistry * moduleRegistry) {
return @ [[RCTLocalAssetImageLoader new]];
}
decodersProvider:^NSArray<id<RCTImageDataDecoder>> *(RCTModuleRegistry * moduleRegistry) {
return @ [[RCTGIFImageDecoder new]];
}];
} else if (moduleClass == RCTNetworking.class) {
return [[moduleClass alloc]
initWithHandlersProvider:^NSArray<id<RCTURLRequestHandler>> *(
RCTModuleRegistry *moduleRegistry) {
return @[
[RCTHTTPRequestHandler new],
[RCTDataRequestHandler new],
[RCTFileRequestHandler new],
];
}];
}
// No custom initializer here.
return [moduleClass new];
}
2. Install TurboModuleManager JavaScript Bindings
Next, you will create a RCTTurboModuleManager
in your bridge delegate’s jsExecutorFactoryForBridge:
method, and install the JavaScript bindings:
#pragma mark - RCTCxxBridgeDelegate
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
{
// Add these lines to create a TurboModuleManager
if (RCTTurboModuleEnabled()) {
_turboModuleManager =
[[RCTTurboModuleManager alloc] initWithBridge:bridge
delegate:self
jsInvoker:bridge.jsCallInvoker];
// Necessary to allow NativeModules to lookup TurboModules
[bridge setRCTTurboModuleRegistry:_turboModuleManager];
if (!RCTTurboModuleEagerInitEnabled()) {
/**
* Instantiating DevMenu has the side-effect of registering
* shortcuts for CMD + d, CMD + i, and CMD + n via RCTDevMenu.
* Therefore, when TurboModules are enabled, we must manually create this
* NativeModule.
*/
[_turboModuleManager moduleForName:"DevMenu"];
}
}
// Add this line...
__weak __typeof(self) weakSelf = self;
// If you want to use the `JSCExecutorFactory`, remember to add the `#import <React/JSCExecutorFactory.h>`
// import statement on top.
return std::make_unique<facebook::react::HermesExecutorFactory>(
facebook::react::RCTJSIExecutorRuntimeInstaller([weakSelf, bridge](facebook::jsi::Runtime &runtime) {
if (!bridge) {
return;
}
// And add these lines to install the bindings...
__typeof(self) strongSelf = weakSelf;
if (strongSelf) {
facebook::react::RuntimeExecutor syncRuntimeExecutor =
[&](std::function<void(facebook::jsi::Runtime & runtime_)> &&callback) { callback(runtime); };
[strongSelf->_turboModuleManager installJSBindingWithRuntimeExecutor:syncRuntimeExecutor];
}
}));
}
3. Enable TurboModule System
Finally, enable TurboModules in your app by executing the following statement before React Native is initialized in your app delegate (e.g. within didFinishLaunchingWithOptions:
):
RCTEnableTurboModule(YES);
Example
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
RCTEnableTurboModule(YES);
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self
launchOptions:launchOptions];
// ...
return YES;
}