mirror of
https://github.com/status-im/react-native.git
synced 2025-01-30 03:05:44 +00:00
f7edcda5d7
Summary: public Thanks to the new lazy initialization system for modules, `RCTDidCreateNativeModules` no longer does what the name implies. Previously, `RCTDidCreateNativeModules` was fired after all native modules had been initialized. Now, it simply fires each time the bridge is reloaded. Modules are created on demand when they are needed, so most of the assumptions about when `RCTDidCreateNativeModules` will fire are now incorrect. This diff deprecates `RCTDidCreateNativeModules`, and adds a new notification, `RCTDidInitializeModuleNotification`, which fires each time a module a new module is instantiated. If you need to access a module at any time you can just call `-[bridge moduleForClass:]` and the module will be instantiated on demand. If you want to access a module *only* after it has already been instantiated, you can use the `RCTDidInitializeModuleNotification` notification. Reviewed By: tadeuzagallo Differential Revision: D2755036 fb-gh-sync-id: 25bab6d5eb6fcd35d43125ac45908035eea01487
316 lines
7.7 KiB
Objective-C
316 lines
7.7 KiB
Objective-C
/**
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*/
|
|
|
|
#import "RCTBridge+Private.h"
|
|
|
|
#import <objc/runtime.h>
|
|
|
|
#import "RCTConvert.h"
|
|
#import "RCTEventDispatcher.h"
|
|
#import "RCTKeyCommands.h"
|
|
#import "RCTLog.h"
|
|
#import "RCTPerformanceLogger.h"
|
|
#import "RCTUtils.h"
|
|
|
|
NSString *const RCTReloadNotification = @"RCTReloadNotification";
|
|
NSString *const RCTJavaScriptWillStartLoadingNotification = @"RCTJavaScriptWillStartLoadingNotification";
|
|
NSString *const RCTJavaScriptDidLoadNotification = @"RCTJavaScriptDidLoadNotification";
|
|
NSString *const RCTJavaScriptDidFailToLoadNotification = @"RCTJavaScriptDidFailToLoadNotification";
|
|
NSString *const RCTDidInitializeModuleNotification = @"RCTDidInitializeModuleNotification";
|
|
|
|
@interface RCTBatchedBridge : RCTBridge <RCTInvalidating>
|
|
|
|
@property (nonatomic, weak) RCTBridge *parentBridge;
|
|
|
|
- (instancetype)initWithParentBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER;
|
|
|
|
@end
|
|
|
|
static NSMutableArray<Class> *RCTModuleClasses;
|
|
NSArray<Class> *RCTGetModuleClasses(void);
|
|
NSArray<Class> *RCTGetModuleClasses(void)
|
|
{
|
|
return RCTModuleClasses;
|
|
}
|
|
|
|
/**
|
|
* Register the given class as a bridge module. All modules must be registered
|
|
* prior to the first bridge initialization.
|
|
*/
|
|
void RCTRegisterModule(Class);
|
|
void RCTRegisterModule(Class moduleClass)
|
|
{
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^{
|
|
RCTModuleClasses = [NSMutableArray new];
|
|
});
|
|
|
|
RCTAssert([moduleClass conformsToProtocol:@protocol(RCTBridgeModule)],
|
|
@"%@ does not conform to the RCTBridgeModule protocol",
|
|
moduleClass);
|
|
|
|
// Register module
|
|
[RCTModuleClasses addObject:moduleClass];
|
|
}
|
|
|
|
/**
|
|
* This function returns the module name for a given class.
|
|
*/
|
|
NSString *RCTBridgeModuleNameForClass(Class cls)
|
|
{
|
|
#if RCT_DEV
|
|
RCTAssert([cls conformsToProtocol:@protocol(RCTBridgeModule)], @"Bridge module classes must conform to RCTBridgeModule");
|
|
#endif
|
|
|
|
NSString *name = [cls moduleName];
|
|
if (name.length == 0) {
|
|
name = NSStringFromClass(cls);
|
|
}
|
|
if ([name hasPrefix:@"RK"]) {
|
|
name = [name stringByReplacingCharactersInRange:(NSRange){0,@"RK".length} withString:@"RCT"];
|
|
}
|
|
return name;
|
|
}
|
|
|
|
/**
|
|
* This function checks if a class has been registered
|
|
*/
|
|
BOOL RCTBridgeModuleClassIsRegistered(Class cls)
|
|
{
|
|
return [objc_getAssociatedObject(cls, &RCTBridgeModuleClassIsRegistered) ?: @YES boolValue];
|
|
}
|
|
|
|
@implementation RCTBridge
|
|
|
|
dispatch_queue_t RCTJSThread;
|
|
|
|
+ (void)initialize
|
|
{
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^{
|
|
|
|
// Set up JS thread
|
|
RCTJSThread = (id)kCFNull;
|
|
|
|
#if RCT_DEBUG
|
|
|
|
// Set up module classes
|
|
static unsigned int classCount;
|
|
Class *classes = objc_copyClassList(&classCount);
|
|
|
|
for (unsigned int i = 0; i < classCount; i++)
|
|
{
|
|
Class cls = classes[i];
|
|
Class superclass = cls;
|
|
while (superclass)
|
|
{
|
|
if (class_conformsToProtocol(superclass, @protocol(RCTBridgeModule)))
|
|
{
|
|
if (![RCTModuleClasses containsObject:cls]) {
|
|
RCTLogWarn(@"Class %@ was not exported. Did you forget to use "
|
|
"RCT_EXPORT_MODULE()?", cls);
|
|
|
|
RCTRegisterModule(cls);
|
|
objc_setAssociatedObject(cls, &RCTBridgeModuleClassIsRegistered,
|
|
@NO, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
|
}
|
|
break;
|
|
}
|
|
superclass = class_getSuperclass(superclass);
|
|
}
|
|
}
|
|
|
|
free(classes);
|
|
|
|
#endif
|
|
|
|
});
|
|
}
|
|
|
|
static RCTBridge *RCTCurrentBridgeInstance = nil;
|
|
|
|
/**
|
|
* The last current active bridge instance. This is set automatically whenever
|
|
* the bridge is accessed. It can be useful for static functions or singletons
|
|
* that need to access the bridge for purposes such as logging, but should not
|
|
* be relied upon to return any particular instance, due to race conditions.
|
|
*/
|
|
+ (instancetype)currentBridge
|
|
{
|
|
return RCTCurrentBridgeInstance;
|
|
}
|
|
|
|
+ (void)setCurrentBridge:(RCTBridge *)currentBridge
|
|
{
|
|
RCTCurrentBridgeInstance = currentBridge;
|
|
}
|
|
|
|
- (instancetype)initWithDelegate:(id<RCTBridgeDelegate>)delegate
|
|
launchOptions:(NSDictionary *)launchOptions
|
|
{
|
|
RCTAssertMainThread();
|
|
|
|
if ((self = [super init])) {
|
|
RCTPerformanceLoggerStart(RCTPLTTI);
|
|
|
|
_delegate = delegate;
|
|
_launchOptions = [launchOptions copy];
|
|
[self setUp];
|
|
[self bindKeys];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (instancetype)initWithBundleURL:(NSURL *)bundleURL
|
|
moduleProvider:(RCTBridgeModuleProviderBlock)block
|
|
launchOptions:(NSDictionary *)launchOptions
|
|
{
|
|
RCTAssertMainThread();
|
|
|
|
if ((self = [super init])) {
|
|
RCTPerformanceLoggerStart(RCTPLTTI);
|
|
|
|
_bundleURL = bundleURL;
|
|
_moduleProvider = block;
|
|
_launchOptions = [launchOptions copy];
|
|
[self setUp];
|
|
[self bindKeys];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|
|
|
- (void)dealloc
|
|
{
|
|
/**
|
|
* This runs only on the main thread, but crashes the subclass
|
|
* RCTAssertMainThread();
|
|
*/
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
[self invalidate];
|
|
}
|
|
|
|
- (void)bindKeys
|
|
{
|
|
RCTAssertMainThread();
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(reload)
|
|
name:RCTReloadNotification
|
|
object:nil];
|
|
|
|
#if TARGET_IPHONE_SIMULATOR
|
|
RCTKeyCommands *commands = [RCTKeyCommands sharedInstance];
|
|
|
|
// reload in current mode
|
|
[commands registerKeyCommandWithInput:@"r"
|
|
modifierFlags:UIKeyModifierCommand
|
|
action:^(__unused UIKeyCommand *command) {
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTReloadNotification
|
|
object:nil
|
|
userInfo:nil];
|
|
}];
|
|
|
|
#endif
|
|
}
|
|
|
|
- (NSArray<Class> *)moduleClasses
|
|
{
|
|
return self.batchedBridge.moduleClasses;
|
|
}
|
|
|
|
- (id)moduleForName:(NSString *)moduleName
|
|
{
|
|
return [self.batchedBridge moduleForName:moduleName];
|
|
}
|
|
|
|
- (id)moduleForClass:(Class)moduleClass
|
|
{
|
|
return [self moduleForName:RCTBridgeModuleNameForClass(moduleClass)];
|
|
}
|
|
|
|
- (RCTEventDispatcher *)eventDispatcher
|
|
{
|
|
return [self moduleForClass:[RCTEventDispatcher class]];
|
|
}
|
|
|
|
- (void)reload
|
|
{
|
|
/**
|
|
* AnyThread
|
|
*/
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[self invalidate];
|
|
[self setUp];
|
|
});
|
|
}
|
|
|
|
- (void)setUp
|
|
{
|
|
RCTAssertMainThread();
|
|
|
|
_bundleURL = [self.delegate sourceURLForBridge:self] ?: _bundleURL;
|
|
|
|
// Sanitize the bundle URL
|
|
_bundleURL = [RCTConvert NSURL:_bundleURL.absoluteString];
|
|
|
|
self.batchedBridge = [[RCTBatchedBridge alloc] initWithParentBridge:self];
|
|
}
|
|
|
|
- (BOOL)isLoading
|
|
{
|
|
return self.batchedBridge.loading;
|
|
}
|
|
|
|
- (BOOL)isValid
|
|
{
|
|
return self.batchedBridge.valid;
|
|
}
|
|
|
|
- (BOOL)isBatchActive
|
|
{
|
|
return [_batchedBridge isBatchActive];
|
|
}
|
|
|
|
- (void)invalidate
|
|
{
|
|
RCTBatchedBridge *batchedBridge = (RCTBatchedBridge *)self.batchedBridge;
|
|
self.batchedBridge = nil;
|
|
|
|
if (batchedBridge) {
|
|
RCTExecuteOnMainThread(^{
|
|
[batchedBridge invalidate];
|
|
}, NO);
|
|
}
|
|
}
|
|
|
|
- (void)enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args
|
|
{
|
|
[self.batchedBridge enqueueJSCall:moduleDotMethod args:args];
|
|
}
|
|
|
|
- (void)enqueueCallback:(NSNumber *)cbID args:(NSArray *)args
|
|
{
|
|
[self.batchedBridge enqueueCallback:cbID args:args];
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation RCTBridge(Deprecated)
|
|
|
|
NSString *const RCTDidCreateNativeModules = @"RCTDidCreateNativeModules";
|
|
|
|
- (NSDictionary *)modules
|
|
{
|
|
return self.batchedBridge.modules;
|
|
}
|
|
|
|
@end
|