跳到主要内容
新架构实战课 实操 + 基建 + 原理全维度包揽,抢先掌握 React Native 新架构精髓 立即查看 >

在 iOS 上启用 TurboModule

注意

这个文档仍然是实验性的,随着我们的迭代,细节会有变化。欢迎在工作小组内的讨论中分享你的反馈。

此外,它还包含几个手动步骤。请注意新架构尚未稳定下来,最终的开发者体验会继续迭代改善。我们正在努力开发工具、模板和库,以帮助你在新架构上快速入门,而不需要经历整个设置过程。

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 the RCTCoreModulesClassProvider() method to handle the default, core modules.
  • getTurboModule:jsInvoker: - This should return nullptr. 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’s extraModulesForBridge 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.

AppDelegate.mm
// ...

#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;
}