mirror of
https://github.com/status-im/react-native.git
synced 2025-01-17 04:50:59 +00:00
006907bdaa
Summary: This diff adds support for initializing the bridge on an arbitrary thread. This is helpful if you want to defer bridge creation, or prevent it from delaying your app startup. Reviewed By: javache Differential Revision: D2965725 fb-gh-sync-id: 8065fa89e850031c72ee4427351300986985e9de shipit-source-id: 8065fa89e850031c72ee4427351300986985e9de
303 lines
7.6 KiB
Objective-C
303 lines
7.6 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 "RCTModuleData.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];
|
|
|
|
objc_setAssociatedObject(moduleClass, &RCTBridgeModuleClassIsRegistered,
|
|
@NO, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
|
}
|
|
|
|
/**
|
|
* This function returns the module name for a given class.
|
|
*/
|
|
NSString *RCTBridgeModuleNameForClass(Class cls)
|
|
{
|
|
#if RCT_DEV
|
|
RCTAssert([cls conformsToProtocol:@protocol(RCTBridgeModule)],
|
|
@"Bridge module `%@` does not conform to RCTBridgeModule", cls);
|
|
#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
|
|
{
|
|
NSURL *_delegateBundleURL;
|
|
}
|
|
|
|
dispatch_queue_t RCTJSThread;
|
|
|
|
+ (void)initialize
|
|
{
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^{
|
|
|
|
// Set up JS thread
|
|
RCTJSThread = (id)kCFNull;
|
|
});
|
|
}
|
|
|
|
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
|
|
{
|
|
if ((self = [super init])) {
|
|
RCTPerformanceLoggerStart(RCTPLTTI);
|
|
|
|
_delegate = delegate;
|
|
_launchOptions = [launchOptions copy];
|
|
[self setUp];
|
|
RCTExecuteOnMainThread(^{ [self bindKeys]; }, NO);
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (instancetype)initWithBundleURL:(NSURL *)bundleURL
|
|
moduleProvider:(RCTBridgeModuleProviderBlock)block
|
|
launchOptions:(NSDictionary *)launchOptions
|
|
{
|
|
if ((self = [super init])) {
|
|
RCTPerformanceLoggerStart(RCTPLTTI);
|
|
|
|
_bundleURL = bundleURL;
|
|
_moduleProvider = block;
|
|
_launchOptions = [launchOptions copy];
|
|
[self setUp];
|
|
RCTExecuteOnMainThread(^{ [self bindKeys]; }, NO);
|
|
}
|
|
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)];
|
|
}
|
|
|
|
- (NSArray *)modulesConformingToProtocol:(Protocol *)protocol
|
|
{
|
|
NSMutableArray *modules = [NSMutableArray new];
|
|
for (Class moduleClass in self.moduleClasses) {
|
|
if ([moduleClass conformsToProtocol:protocol]) {
|
|
id module = [self moduleForClass:moduleClass];
|
|
if (module) {
|
|
[modules addObject:module];
|
|
}
|
|
}
|
|
}
|
|
return [modules copy];
|
|
}
|
|
|
|
- (BOOL)moduleIsInitialized:(Class)moduleClass
|
|
{
|
|
return [self.batchedBridge moduleIsInitialized:moduleClass];
|
|
}
|
|
|
|
- (RCTEventDispatcher *)eventDispatcher
|
|
{
|
|
return [self moduleForClass:[RCTEventDispatcher class]];
|
|
}
|
|
|
|
- (void)reload
|
|
{
|
|
/**
|
|
* Any thread
|
|
*/
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[self invalidate];
|
|
[self setUp];
|
|
});
|
|
}
|
|
|
|
- (void)setUp
|
|
{
|
|
// Only update bundleURL from delegate if delegate bundleURL has changed
|
|
NSURL *previousDelegateURL = _delegateBundleURL;
|
|
_delegateBundleURL = [self.delegate sourceURLForBridge:self];
|
|
if (_delegateBundleURL && ![_delegateBundleURL isEqual:previousDelegateURL]) {
|
|
_bundleURL = _delegateBundleURL;
|
|
}
|
|
|
|
// Sanitize the bundle URL
|
|
_bundleURL = [RCTConvert NSURL:_bundleURL.absoluteString];
|
|
|
|
[self createBatchedBridge];
|
|
}
|
|
|
|
- (void)createBatchedBridge
|
|
{
|
|
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
|
|
{
|
|
RCTBridge *batchedBridge = 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
|