Skip to content

Commit 72ed849

Browse files
committed
rebase
1 parent 83cdba1 commit 72ed849

File tree

23 files changed

+210
-141
lines changed

23 files changed

+210
-141
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Xcode
22
#
3+
!**/*.xcodeproj
4+
!**/*.pbxproj
5+
!**/*.xcworkspacedata
6+
!**/*.xcsettings
7+
!**/*.xcscheme
38
build/
49
*.pbxuser
510
!default.pbxuser

Examples/Movies/Movies.xcodeproj/project.xcworkspace/contents.xcworkspacedata

Lines changed: 0 additions & 7 deletions
This file was deleted.

Examples/Movies/Movies.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings

Lines changed: 0 additions & 8 deletions
This file was deleted.

Examples/TicTacToe/TicTacToe.xcodeproj/project.xcworkspace/contents.xcworkspacedata

Lines changed: 0 additions & 7 deletions
This file was deleted.

Examples/TicTacToe/TicTacToe.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings

Lines changed: 0 additions & 8 deletions
This file was deleted.

Examples/UIExplorer/UIExplorer.xcodeproj/project.xcworkspace/contents.xcworkspacedata

Lines changed: 0 additions & 7 deletions
This file was deleted.

Examples/UIExplorer/UIExplorer.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings

Lines changed: 0 additions & 8 deletions
This file was deleted.

ReactKit/Base/RCTBridge.h

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#import "RCTInvalidating.h"
55
#import "RCTJavaScriptExecutor.h"
66

7+
@class RCTBridge;
78
@class RCTEventDispatcher;
89
@class RCTRootView;
910

@@ -26,6 +27,12 @@ static inline NSDictionary *RCTAPIErrorObject(NSString *msg)
2627
return @{@"apiError": msg ?: @""};
2728
}
2829

30+
/**
31+
* This block can be used to instantiate modules that require additional
32+
* init parameters, or additional configuration prior to being used.
33+
*/
34+
typedef NSArray *(^RCTBridgeModuleProviderBlock)(RCTBridge *bridge);
35+
2936
/**
3037
* Async batched bridge used to communicate with the JavaScript application.
3138
*/
@@ -34,11 +41,13 @@ static inline NSDictionary *RCTAPIErrorObject(NSString *msg)
3441
/**
3542
* The designated initializer. This creates a new bridge on top of the specified
3643
* executor. The bridge should then be used for all subsequent communication
37-
* with the JavaScript code running in the executor. You can optionally pass in
38-
* a list of module instances to be used instead of the auto-instantiated versions.
44+
* with the JavaScript code running in the executor. Modules will be automatically
45+
* instantiated using the default contructor, but you can optionally pass in a
46+
* module provider block to manually instantiate modules that require additional
47+
* init parameters or configuration.
3948
*/
4049
- (instancetype)initWithJavaScriptExecutor:(id<RCTJavaScriptExecutor>)javaScriptExecutor
41-
moduleInstances:(NSArray *)moduleInstances NS_DESIGNATED_INITIALIZER;
50+
moduleProvider:(RCTBridgeModuleProviderBlock)block NS_DESIGNATED_INITIALIZER;
4251

4352
/**
4453
* This method is used to call functions in the JavaScript application context.
@@ -60,6 +69,11 @@ static inline NSDictionary *RCTAPIErrorObject(NSString *msg)
6069
*/
6170
@property (nonatomic, readonly) RCTEventDispatcher *eventDispatcher;
6271

72+
/**
73+
* A dictionary of all registered RCTBridgeModule instances, keyed by moduleName.
74+
*/
75+
@property (nonatomic, copy, readonly) NSDictionary *modules;
76+
6377
/**
6478
* The shadow queue is used to execute callbacks from the JavaScript code. All
6579
* native hooks (e.g. exported module methods) will be executed on the shadow

ReactKit/Base/RCTBridge.m

Lines changed: 72 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
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

407418
static 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];

ReactKit/Base/RCTBridgeModule.h

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
#import <Foundation/Foundation.h>
44

5+
#import "RCTJSMethodRegistrar.h"
6+
57
@class RCTBridge;
68

79
/**
@@ -11,9 +13,9 @@
1113
typedef void (^RCTResponseSenderBlock)(NSArray *response);
1214

1315
/**
14-
* Provides minimal interface needed to register a bridge module
16+
* Provides the interface needed to register a bridge module.
1517
*/
16-
@protocol RCTBridgeModule <NSObject>
18+
@protocol RCTBridgeModule <RCTJSMethodRegistrar>
1719
@optional
1820

1921
/**
@@ -47,15 +49,12 @@ typedef void (^RCTResponseSenderBlock)(NSArray *response);
4749
+ (NSDictionary *)constantsToExport;
4850

4951
/**
50-
* An array of JavaScript methods that the module will call via the
51-
* -[RCTBridge enqueueJSCall:args:] method. Each method should be specified
52-
* as a string of the form "JSModuleName.jsMethodName". Attempting to call a
53-
* method that has not been registered will result in an error. If a method
54-
* has already been regsistered by another module, it is not necessary to
55-
* register it again, but it is good pratice. Registering the same method
56-
* more than once is silently ignored and will not result in an error.
52+
* Some "constants" are not really constant, and need to be re-generated
53+
* each time the bridge module is created. Support for this feature is
54+
* deprecated and may be going away or changing, but for now you can use
55+
* the -constantsToExport instance method to register these "pseudo-constants".
5756
*/
58-
+ (NSArray *)JSMethods;
57+
- (NSDictionary *)constantsToExport;
5958

6059
/**
6160
* Notifies the module that a batch of JS method invocations has just completed.

0 commit comments

Comments
 (0)