Skip to content

Commit 5d5d370

Browse files
cipolleschifacebook-github-bot
authored andcommitted
Connect the classes for C++ Module registration to TMManagerDelegate (facebook#49611)
Summary: Pull Request resolved: facebook#49611 This Change connects the pod created previously to the `RCTTuboModuleManager` so that it can ask the delegate about the CxxTurboModuleProviders. We had to introduce a `RCTFullTurboModuleManagerDelegate` that implements both the old `TurboModuleManagerDelegate` and the new Swift compatible one. ## Problem As of today, it is not possible to create a pure C++ TM and to register it through a Swift AppDelegate ## Solution We can create a pod that can be imported in a Swift AppDelegate and that offer some pure Objective-C classes. These classes contains a provider that can be instantiated in Swift. The TurboModule manager delegate will ask the AppDelegate about the presence of some provider that can instantiate a pure C++ turbomodule with a given name. The provider has an empty interface, but the implementation contains a function that can actually instantiate the TM. The function is implemented in an Objective-C++ class that imports the pure C++ turbomodule and creates it. The TMManager extends the provider through a category to attaach the signature of the function that is implemented by the provider. The last diff in this stack contains an exaple on how to implement this. ## Changelog: [iOS][Added] - Added the React-SwiftCompatibleNativeModules pod Reviewed By: javache Differential Revision: D70012142 fbshipit-source-id: db96c4cd3cdd1062b12f11131b7c6c51ecd74bc7
1 parent 2062def commit 5d5d370

File tree

6 files changed

+98
-29
lines changed

6 files changed

+98
-29
lines changed

packages/react-native/Libraries/AppDelegate/RCTDefaultReactNativeFactoryDelegate.mm

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,13 @@ - (void)hostDidStart:(RCTHost *)host
9090
{
9191
}
9292

93+
- (nullable id<RCTModuleProvider>)getModuleProvider:(const char *)name
94+
{
95+
// NSString *providerName = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
96+
// return self.dependencyProvider ? self.dependencyProvider.moduleProviders[providerName] : nullptr;
97+
return nil;
98+
}
99+
93100
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
94101
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
95102
{

packages/react-native/Libraries/AppDelegate/RCTReactNativeFactory.mm

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,14 @@ - (Class)getModuleClassFromName:(const char *)name
170170
#endif
171171
}
172172

173+
- (nullable id<RCTModuleProvider>)getModuleProvider:(const char *)name
174+
{
175+
if ([_delegate respondsToSelector:@selector(getModuleProvider:)]) {
176+
return [_delegate getModuleProvider:name];
177+
}
178+
return nil;
179+
}
180+
173181
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
174182
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
175183
{

packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModule.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,9 +176,25 @@ class JSI_EXPORT ObjCTurboModule : public TurboModule {
176176
}
177177
@end
178178

179-
@protocol RCTTurboModule <NSObject>
179+
/**
180+
* Factory object that can create a Turbomodule. It could be either a C++ TM or any TurboModule.
181+
* This needs to be an Objective-C class so we can instantiate it at runtime.
182+
*/
183+
@protocol RCTModuleProvider <NSObject>
184+
185+
/**
186+
* Create an instance of a TurboModule with the JS Invoker.
187+
*/
180188
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
181189
(const facebook::react::ObjCTurboModule::InitParams &)params;
190+
@end
191+
192+
/**
193+
* Protocol that objects can inherit to conform to be treated as turbomodules.
194+
* It inherits from RCTTurboModuleProvider, meaning that a TurboModule can create itself
195+
*/
196+
@protocol RCTTurboModule <RCTModuleProvider>
197+
182198
@optional
183199
- (void)setEventEmitterCallback:(EventEmitterCallbackWrapper *)eventEmitterCallbackWrapper;
184200
@end

packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@
3434

3535
@optional
3636

37+
/**
38+
* This method is used to retrieve a factory object that can create a `facebook::react::TurboModule`,
39+
* The class implementing `RCTTurboModuleProvider` must be an Objective-C class so that we can
40+
* initialize it dynamically with Codegen.
41+
*/
42+
- (id<RCTModuleProvider>)getModuleProvider:(const char *)name;
43+
3744
/**
3845
* Create an instance of a TurboModule without relying on any ObjC++ module instance.
3946
*/

packages/react-native/ReactCommon/react/nativemodule/core/platform/ios/ReactCommon/RCTTurboModuleManager.mm

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -340,8 +340,7 @@ - (instancetype)initWithBridgeProxy:(RCTBridgeProxy *)bridgeProxy
340340
/**
341341
* Step 2: Look for platform-specific modules.
342342
*/
343-
id<RCTBridgeModule> module =
344-
!RCTTurboModuleInteropEnabled() || [self _isTurboModule:moduleName] ? [self _provideObjCModule:moduleName] : nil;
343+
id<RCTModuleProvider> module = [self _moduleProviderForName:moduleName];
345344

346345
TurboModulePerfLogger::moduleJSRequireEndingStart(moduleName);
347346

@@ -351,31 +350,28 @@ - (instancetype)initWithBridgeProxy:(RCTBridgeProxy *)bridgeProxy
351350
return nullptr;
352351
}
353352

354-
Class moduleClass = [module class];
355-
353+
std::shared_ptr<NativeMethodCallInvoker> nativeMethodCallInvoker = nullptr;
356354
dispatch_queue_t methodQueue = (dispatch_queue_t)objc_getAssociatedObject(module, &kAssociatedMethodQueueKey);
357-
if (methodQueue == nil) {
358-
RCTLogError(@"TurboModule \"%@\" was not associated with a method queue.", moduleClass);
359-
}
360-
361-
/**
362-
* Step 2c: Create and native CallInvoker from the TurboModule's method queue.
363-
*/
364-
std::shared_ptr<NativeMethodCallInvoker> nativeMethodCallInvoker =
365-
std::make_shared<ModuleNativeMethodCallInvoker>(methodQueue);
355+
if (methodQueue) {
356+
/**
357+
* Step 2c: Create and native CallInvoker from the TurboModule's method queue.
358+
*/
359+
nativeMethodCallInvoker = std::make_shared<ModuleNativeMethodCallInvoker>(methodQueue);
366360

367-
/**
368-
* Have RCTCxxBridge decorate native CallInvoker, so that it's aware of TurboModule async method calls.
369-
* This helps the bridge fire onBatchComplete as readily as it should.
370-
*/
371-
if ([_bridge respondsToSelector:@selector(decorateNativeMethodCallInvoker:)]) {
372-
nativeMethodCallInvoker = [_bridge decorateNativeMethodCallInvoker:nativeMethodCallInvoker];
361+
/**
362+
* Have RCTCxxBridge decorate native CallInvoker, so that it's aware of TurboModule async method calls.
363+
* This helps the bridge fire onBatchComplete as readily as it should.
364+
*/
365+
if ([_bridge respondsToSelector:@selector(decorateNativeMethodCallInvoker:)]) {
366+
nativeMethodCallInvoker = [_bridge decorateNativeMethodCallInvoker:nativeMethodCallInvoker];
367+
}
373368
}
374369

375370
/**
376371
* Step 2d: If the moduleClass is a legacy CxxModule, return a TurboCxxModule instance that
377372
* wraps CxxModule.
378373
*/
374+
Class moduleClass = [module class];
379375
if ([moduleClass isSubclassOfClass:RCTCxxModule.class]) {
380376
// Use TurboCxxModule compat class to wrap the CxxModule instance.
381377
// This is only for migration convenience, despite less performant.
@@ -393,7 +389,7 @@ - (instancetype)initWithBridgeProxy:(RCTBridgeProxy *)bridgeProxy
393389
if ([module respondsToSelector:@selector(getTurboModule:)]) {
394390
ObjCTurboModule::InitParams params = {
395391
.moduleName = moduleName,
396-
.instance = module,
392+
.instance = (id<RCTBridgeModule>)module,
397393
.jsInvoker = _jsInvoker,
398394
.nativeMethodCallInvoker = nativeMethodCallInvoker,
399395
.isSyncModule = methodQueue == RCTJSThread,
@@ -430,7 +426,8 @@ - (instancetype)initWithBridgeProxy:(RCTBridgeProxy *)bridgeProxy
430426
TurboModulePerfLogger::moduleJSRequireBeginningEnd(moduleName);
431427

432428
// Create platform-specific native module object
433-
id<RCTBridgeModule> module = [self _isLegacyModule:moduleName] ? [self _provideObjCModule:moduleName] : nil;
429+
id<RCTBridgeModule> module =
430+
[self _isLegacyModule:moduleName] ? [self _provideObjCModule:moduleName moduleProvider:nil] : nil;
434431

435432
TurboModulePerfLogger::moduleJSRequireEndingStart(moduleName);
436433

@@ -475,6 +472,8 @@ - (instancetype)initWithBridgeProxy:(RCTBridgeProxy *)bridgeProxy
475472
return turboModule;
476473
}
477474

475+
#pragma mark - Private Methods
476+
478477
- (BOOL)_isTurboModule:(const char *)moduleName
479478
{
480479
Class moduleClass = [self _getModuleClassFromName:moduleName];
@@ -492,6 +491,27 @@ - (BOOL)_isLegacyModuleClass:(Class)moduleClass
492491
return moduleClass != nil && (!isTurboModuleClass(moduleClass) || [moduleClass isSubclassOfClass:RCTCxxModule.class]);
493492
}
494493

494+
- (id<RCTModuleProvider>)_moduleProviderForName:(const char *)moduleName
495+
{
496+
id<RCTModuleProvider> moduleProvider = [_delegate getModuleProvider:moduleName];
497+
BOOL isTurboModule = [self _isTurboModule:moduleName];
498+
if (RCTTurboModuleEnabled() && !isTurboModule && !moduleProvider) {
499+
return nil;
500+
}
501+
502+
if (moduleProvider) {
503+
if ([moduleProvider conformsToProtocol:@protocol(RCTTurboModule)]) {
504+
// moduleProvider is also a TM, we need to initialize objectiveC properties, like the dispatch queue
505+
return (id<RCTModuleProvider>)[self _provideObjCModule:moduleName moduleProvider:moduleProvider];
506+
}
507+
// module is Cxx module
508+
return moduleProvider;
509+
}
510+
511+
// No module provider, the Module is registered without Codegen
512+
return (id<RCTModuleProvider>)[self _provideObjCModule:moduleName moduleProvider:nil];
513+
}
514+
495515
- (ModuleHolder *)_getOrCreateModuleHolder:(const char *)moduleName
496516
{
497517
std::lock_guard<std::mutex> guard(_moduleHoldersMutex);
@@ -501,7 +521,6 @@ - (ModuleHolder *)_getOrCreateModuleHolder:(const char *)moduleName
501521

502522
return &_moduleHolders[moduleName];
503523
}
504-
505524
/**
506525
* Given a name for a NativeModule, return an ObjC object which is the instance
507526
* of that NativeModule ObjC class. If no NativeModule exist with the provided name,
@@ -510,7 +529,7 @@ - (ModuleHolder *)_getOrCreateModuleHolder:(const char *)moduleName
510529
* Note: All NativeModule instances are cached, which means they're all long-lived
511530
* (for now).
512531
*/
513-
- (id<RCTBridgeModule>)_provideObjCModule:(const char *)moduleName
532+
- (id<RCTBridgeModule>)_provideObjCModule:(const char *)moduleName moduleProvider:(id<RCTModuleProvider>)moduleProvider
514533
{
515534
if (strncmp("RCT", moduleName, 3) == 0) {
516535
moduleName = [[[NSString stringWithUTF8String:moduleName] substringFromIndex:3] UTF8String];
@@ -523,7 +542,10 @@ - (ModuleHolder *)_getOrCreateModuleHolder:(const char *)moduleName
523542
}
524543

525544
TurboModulePerfLogger::moduleCreateStart(moduleName, moduleHolder->getModuleId());
526-
id<RCTBridgeModule> module = [self _provideObjCModule:moduleName moduleHolder:moduleHolder shouldPerfLog:YES];
545+
id<RCTBridgeModule> module = [self _provideObjCModule:moduleName
546+
moduleHolder:moduleHolder
547+
shouldPerfLog:YES
548+
moduleProvider:moduleProvider];
527549

528550
if (module) {
529551
TurboModulePerfLogger::moduleCreateEnd(moduleName, moduleHolder->getModuleId());
@@ -537,6 +559,7 @@ - (ModuleHolder *)_getOrCreateModuleHolder:(const char *)moduleName
537559
- (id<RCTBridgeModule>)_provideObjCModule:(const char *)moduleName
538560
moduleHolder:(ModuleHolder *)moduleHolder
539561
shouldPerfLog:(BOOL)shouldPerfLog
562+
moduleProvider:(id<RCTModuleProvider>)moduleProvider
540563
{
541564
bool shouldCreateModule = false;
542565

@@ -560,7 +583,7 @@ - (ModuleHolder *)_getOrCreateModuleHolder:(const char *)moduleName
560583
/**
561584
* Step 2a: Resolve platform-specific class.
562585
*/
563-
Class moduleClass = [self _getModuleClassFromName:moduleName];
586+
Class moduleClass = moduleProvider ? [moduleProvider class] : [self _getModuleClassFromName:moduleName];
564587

565588
__block id<RCTBridgeModule> module = nil;
566589

@@ -611,7 +634,6 @@ - (ModuleHolder *)_getOrCreateModuleHolder:(const char *)moduleName
611634
moduleHolder->setModule(module);
612635
moduleHolder->endCreatingModule();
613636
}
614-
615637
moduleHolder->cv().notify_all();
616638

617639
return module;
@@ -998,7 +1020,7 @@ - (id)moduleForName:(const char *)moduleName warnOnLookupFailure:(BOOL)warnOnLoo
9981020
return nil;
9991021
}
10001022

1001-
id<RCTBridgeModule> module = [self _provideObjCModule:moduleName];
1023+
id<RCTBridgeModule> module = [self _provideObjCModule:moduleName moduleProvider:nil];
10021024

10031025
if (warnOnLookupFailure && !module) {
10041026
RCTLogError(@"Unable to find module for %@", [NSString stringWithUTF8String:moduleName]);
@@ -1064,7 +1086,8 @@ - (void)_invalidateModules
10641086
*/
10651087
id<RCTBridgeModule> module = [self _provideObjCModule:moduleName.c_str()
10661088
moduleHolder:moduleHolder
1067-
shouldPerfLog:NO];
1089+
shouldPerfLog:NO
1090+
moduleProvider:nil];
10681091

10691092
if ([module respondsToSelector:@selector(invalidate)]) {
10701093
dispatch_queue_t methodQueue = (dispatch_queue_t)objc_getAssociatedObject(module, &kAssociatedMethodQueueKey);

packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,14 @@ - (Class)getModuleClassFromName:(const char *)name
235235
return @[];
236236
}
237237

238+
- (nullable id<RCTModuleProvider>)getModuleProvider:(const char *)name
239+
{
240+
if ([_appTMMDelegate respondsToSelector:@selector(getModuleProvider:)]) {
241+
return [_appTMMDelegate getModuleProvider:name];
242+
}
243+
return nil;
244+
}
245+
238246
#pragma mark - Private
239247

240248
- (void)_start

0 commit comments

Comments
 (0)