99#import < objc/runtime.h>
1010
1111#import " RCTConvert.h"
12- #import " RCTInvalidating.h"
1312#import " RCTEventDispatcher.h"
1413#import " RCTLog.h"
1514#import " RCTSparseArray.h"
@@ -101,6 +100,40 @@ - (NSString *)description
101100 }
102101}
103102
103+ /* *
104+ * This function scans all classes available at runtime and returns an array
105+ * of all JSMethods registered.
106+ */
107+ static NSArray *RCTJSMethods (void )
108+ {
109+ static NSArray *JSMethods;
110+ static dispatch_once_t onceToken;
111+ dispatch_once (&onceToken, ^{
112+ NSMutableSet *uniqueMethods = [NSMutableSet set ];
113+
114+ unsigned int classCount;
115+ Class *classes = objc_copyClassList (&classCount);
116+ for (unsigned int i = 0 ; i < classCount; i++) {
117+
118+ Class cls = classes[i];
119+
120+ if (!class_getSuperclass (cls)) {
121+ // Class has no superclass - it's probably something weird
122+ continue ;
123+ }
124+
125+ if (RCTClassOverridesClassMethod (cls, @selector (JSMethods ))) {
126+ [uniqueMethods addObjectsFromArray: [cls JSMethods ]];
127+ }
128+ }
129+ free (classes);
130+
131+ JSMethods = [uniqueMethods allObjects ];
132+ });
133+
134+ return JSMethods;
135+ }
136+
104137/* *
105138 * This function scans all classes available at runtime and returns an array
106139 * of all classes that implement the RTCBridgeModule protocol.
@@ -264,7 +297,7 @@ - (NSString *)description
264297
265298 NSArray *methods = RCTExportedMethodsByModuleID ()[moduleID];
266299 NSMutableDictionary *methodsByName = [NSMutableDictionary dictionaryWithCapacity: methods.count];
267- [methods enumerateObjectsUsingBlock: ^(RCTModuleMethod *method, NSUInteger methodID, BOOL *stop ) {
300+ [methods enumerateObjectsUsingBlock: ^(RCTModuleMethod *method, NSUInteger methodID, BOOL *_stop ) {
268301 methodsByName[method.JSMethodName] = @{
269302 @" methodID" : @(methodID),
270303 @" type" : @" remote" ,
@@ -335,38 +368,16 @@ - (NSString *)description
335368 static NSMutableDictionary *localModules;
336369 static dispatch_once_t onceToken;
337370 dispatch_once (&onceToken, ^{
338-
371+
339372 RCTLocalModuleIDs = [[NSMutableDictionary alloc ] init ];
340373 RCTLocalMethodIDs = [[NSMutableDictionary alloc ] init ];
341-
342- NSMutableArray *JSMethods = [[NSMutableArray alloc ] init ];
343-
344- // Add globally used methods
345- [JSMethods addObjectsFromArray: @[
346- @" AppRegistry.runApplication" ,
347- @" RCTDeviceEventEmitter.emit" ,
348- @" RCTEventEmitter.receiveEvent" ,
349- @" RCTEventEmitter.receiveTouches" ,
350- ]];
351-
352- // NOTE: these methods are currently unused in the OSS project
353- // @"Dimensions.set",
354- // @"RCTNativeAppEventEmitter.emit",
355- // @"ReactIOS.unmountComponentAtNodeAndRemoveContainer",
356-
357- // Register individual methods from modules
358- for (Class cls in RCTBridgeModuleClassesByModuleID ()) {
359- if (RCTClassOverridesClassMethod (cls, @selector (JSMethods ))) {
360- [JSMethods addObjectsFromArray: [cls JSMethods ]];
361- }
362- }
363-
374+
364375 localModules = [[NSMutableDictionary alloc ] init ];
365- for (NSString *moduleDotMethod in JSMethods ) {
366-
376+ for (NSString *moduleDotMethod in RCTJSMethods () ) {
377+
367378 NSArray *parts = [moduleDotMethod componentsSeparatedByString: @" ." ];
368379 RCTCAssert (parts.count == 2 , @" '%@ ' is not a valid JS method definition - expected 'Module.method' format." , moduleDotMethod);
369-
380+
370381 // Add module if it doesn't already exist
371382 NSString *moduleName = parts[0 ];
372383 NSDictionary *module = localModules[moduleName];
@@ -377,7 +388,7 @@ - (NSString *)description
377388 };
378389 localModules[moduleName] = module;
379390 }
380-
391+
381392 // Add method if it doesn't already exist
382393 NSString *methodName = parts[1 ];
383394 NSMutableDictionary *methods = module[@" methods" ];
@@ -387,27 +398,27 @@ - (NSString *)description
387398 @" type" : @" local"
388399 };
389400 }
390-
401+
391402 // Add module and method lookup
392403 RCTLocalModuleIDs[moduleDotMethod] = module[@" moduleID" ];
393404 RCTLocalMethodIDs[moduleDotMethod] = methods[methodName][@" methodID" ];
394405 }
395406 });
396-
407+
397408 return localModules;
398409}
399410
400411@implementation RCTBridge
401412{
402413 RCTSparseArray *_modulesByID;
403- NSMutableDictionary *_modulesByName;
414+ NSDictionary *_modulesByName;
404415 id <RCTJavaScriptExecutor> _javaScriptExecutor;
405416}
406417
407418static id <RCTJavaScriptExecutor> _latestJSExecutor;
408419
409420- (instancetype )initWithJavaScriptExecutor : (id <RCTJavaScriptExecutor>)javaScriptExecutor
410- moduleInstances : ( NSArray *) moduleInstances
421+ moduleProvider : (RCTBridgeModuleProviderBlock) block
411422{
412423 if ((self = [super init ])) {
413424 _javaScriptExecutor = javaScriptExecutor;
@@ -417,31 +428,39 @@ - (instancetype)initWithJavaScriptExecutor:(id<RCTJavaScriptExecutor>)javaScript
417428
418429 // Register passed-in module instances
419430 NSMutableDictionary *preregisteredModules = [[NSMutableDictionary alloc ] init ];
420- for (id <RCTBridgeModule> module in moduleInstances) {
421- preregisteredModules[RCTModuleNameForClass ([module class ])] = module;
431+ if (block) {
432+ for (id <RCTBridgeModule> module in block (self)) {
433+ preregisteredModules[RCTModuleNameForClass ([module class ])] = module;
434+ }
422435 }
423436
424437 // Instantiate modules
425438 _modulesByID = [[RCTSparseArray alloc ] init ];
426- _modulesByName = [[ NSMutableDictionary alloc ] initWithDictionary: preregisteredModules];
439+ NSMutableDictionary *modulesByName = [preregisteredModules mutableCopy ];
427440 [RCTBridgeModuleClassesByModuleID () enumerateObjectsUsingBlock: ^(Class moduleClass, NSUInteger moduleID, BOOL *stop) {
428441 NSString *moduleName = RCTModuleNamesByID[moduleID];
429442 // Check if module instance has already been registered for this name
430- if (_modulesByName[moduleName] != nil ) {
443+ if ((_modulesByID[moduleID] = modulesByName[moduleName]) ) {
431444 // Preregistered instances takes precedence, no questions asked
432445 if (!preregisteredModules[moduleName]) {
433446 // It's OK to have a name collision as long as the second instance is nil
434447 RCTAssert (RCTCreateModuleInstance (moduleClass, self) == nil ,
435448 @" Attempted to register RCTBridgeModule class %@ for the name '%@ ', \
436449 but name was already registered by class %@ " , moduleClass,
437- moduleName, [_modulesByName [moduleName] class ]);
450+ moduleName, [modulesByName [moduleName] class ]);
438451 }
439452 } else {
440453 // Module name hasn't been used before, so go ahead and instantiate
441- _modulesByID[moduleID] = _modulesByName[moduleName] = RCTCreateModuleInstance (moduleClass, self);
454+ id <RCTBridgeModule> module = RCTCreateModuleInstance (moduleClass, self);
455+ if (module) {
456+ _modulesByID[moduleID] = modulesByName[moduleName] = module;
457+ }
442458 }
443459 }];
444-
460+
461+ // Store modules
462+ _modulesByName = [modulesByName copy ];
463+
445464 // Inject module data into JS context
446465 NSString *configJSON = RCTJSONStringify (@{
447466 @" remoteModuleConfig" : RCTRemoteModulesConfig (_modulesByName),
@@ -460,6 +479,14 @@ - (instancetype)initWithJavaScriptExecutor:(id<RCTJavaScriptExecutor>)javaScript
460479 return self;
461480}
462481
482+ - (NSDictionary *)modules
483+ {
484+ RCTAssert (_modulesByName != nil , @" Bridge modules have not yet been initialized. \
485+ You may be trying to access a module too early in the startup procedure." );
486+
487+ return _modulesByName;
488+ }
489+
463490- (void )dealloc
464491{
465492 RCTAssert (!self.valid , @" must call -invalidate before -dealloc" );
@@ -516,7 +543,7 @@ - (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args
516543
517544 [self _invokeAndProcessModule: @" BatchedBridge"
518545 method: @" callFunctionReturnFlushedQueue"
519- arguments: @[moduleID, methodID, args]];
546+ arguments: @[moduleID, methodID, args ?: @[] ]];
520547}
521548
522549- (void )enqueueApplicationScript : (NSString *)script url : (NSURL *)url onComplete : (RCTJavaScriptCompleteBlock)onComplete
@@ -699,8 +726,8 @@ - (BOOL)_handleRequestNumber:(NSUInteger)i
699726 switch (argumentType[0 ]) {
700727 case ' :' :
701728 if ([param isKindOfClass: [NSString class ]]) {
702- SEL selector = NSSelectorFromString (param);
703- [invocation setArgument: &selector atIndex: argIdx];
729+ SEL sel = NSSelectorFromString (param);
730+ [invocation setArgument: &sel atIndex: argIdx];
704731 shouldSet = NO ;
705732 }
706733 break ;
@@ -813,7 +840,7 @@ + (BOOL)hasValidJSExecutor
813840+ (void )log : (NSArray *)objects level : (NSString *)level
814841{
815842 if (!_latestJSExecutor || ![_latestJSExecutor isValid ]) {
816- RCTLogError (@" %@ " , RCTLogFormatString ( @" ERROR: No valid JS executor to log %@ ." , objects) );
843+ RCTLogError (@" ERROR: No valid JS executor to log %@ ." , objects);
817844 return ;
818845 }
819846 NSMutableArray *args = [NSMutableArray arrayWithObject: level];
0 commit comments