Skip to content

Commit

Permalink
iOS: register lazy nativemodules on startup when Chrome is attached
Browse files Browse the repository at this point in the history
Summary:
@public
This allows apps to specify custom lazy modules that they need to load during chrome debugging. This is because lazy modules won't be available on start up, hence these modules will be missing in JS land, causing problems.

Reviewed By: shergin

Differential Revision: D12899408

fbshipit-source-id: dca313648e994b22e3ee5afec856ef76470065f9
  • Loading branch information
fkgozali authored and facebook-github-bot committed Nov 2, 2018
1 parent 5431607 commit 04ea976
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 0 deletions.
5 changes: 5 additions & 0 deletions React/Base/RCTBridgeDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,9 @@
- (void)loadSourceForBridge:(RCTBridge *)bridge
withBlock:(RCTSourceLoadBlock)loadCallback;

/**
* Retrieve the list of lazy-native-modules names for the given bridge.
*/
- (NSDictionary<NSString *, Class> *)extraLazyModuleClassesForBridge:(RCTBridge *)bridge;

@end
52 changes: 52 additions & 0 deletions React/CxxBridge/RCTCxxBridge.mm
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ - (void)start
[self registerExtraModules];
// Initialize all native modules that cannot be loaded lazily
(void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
[self registerExtraLazyModules];

[_performanceLogger markStopForTag:RCTPLNativeModuleInit];

Expand Down Expand Up @@ -600,6 +601,57 @@ - (void)registerExtraModules
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
}

- (void)registerExtraLazyModules
{
#if RCT_DEBUG
// This is debug-only and only when Chrome is attached, since it expects all modules to be already
// available on start up. Otherwise, we can let the lazy module discovery to load them on demand.
Class executorClass = [_parentBridge executorClass];
if (executorClass && [NSStringFromClass(executorClass) isEqualToString:@"RCTWebSocketExecutor"]) {
NSDictionary<NSString *, Class> *moduleClasses = nil;
if ([self.delegate respondsToSelector:@selector(extraLazyModuleClassesForBridge:)]) {
moduleClasses = [self.delegate extraLazyModuleClassesForBridge:_parentBridge];
}

if (!moduleClasses) {
return;
}

// This logic is mostly copied from `registerModulesForClasses:`, but with one difference:
// we must use the names provided by the delegate method here.
for (NSString *moduleName in moduleClasses) {
Class moduleClass = moduleClasses[moduleName];
if (RCTJSINativeModuleEnabled() && [moduleClass conformsToProtocol:@protocol(RCTJSINativeModule)]) {
continue;
}

// Check for module name collisions
RCTModuleData *moduleData = _moduleDataByName[moduleName];
if (moduleData) {
if (moduleData.hasInstance) {
// Existing module was preregistered, so it takes precedence
continue;
} else if ([moduleClass new] == nil) {
// The new module returned nil from init, so use the old module
continue;
} else if ([moduleData.moduleClass new] != nil) {
// Both modules were non-nil, so it's unclear which should take precedence
RCTLogError(@"Attempted to register RCTBridgeModule class %@ for the "
"name '%@', but name was already registered by class %@",
moduleClass, moduleName, moduleData.moduleClass);
}
}

moduleData = [[RCTModuleData alloc] initWithModuleClass:moduleClass bridge:self];

_moduleDataByName[moduleName] = moduleData;
[_moduleClassesByID addObject:moduleClass];
[_moduleDataByID addObject:moduleData];
}
}
#endif
}

- (NSArray<RCTModuleData *> *)_initializeModules:(NSArray<id<RCTBridgeModule>> *)modules
withDispatchGroup:(dispatch_group_t)dispatchGroup
lazilyDiscovered:(BOOL)lazilyDiscovered
Expand Down

0 comments on commit 04ea976

Please sign in to comment.