2015-02-02 updates

- Removed special-case treatment of RCTTiming and RCTUIManager modules | Nick Lockwood
This commit is contained in:
Christopher Chedeau
2015-02-03 16:12:07 -08:00
parent 00f0ebccdf
commit ccd8f184af
11 changed files with 340 additions and 360 deletions

View File

@@ -6,33 +6,8 @@
@protocol RCTNativeModule;
@class RCTUIManager;
@class RCTEventDispatcher;
/**
* Functions are the one thing that aren't automatically converted to OBJC
* blocks, according to this revert: http://trac.webkit.org/changeset/144489
* They must be expressed as `JSValue`s.
*
* But storing callbacks causes reference cycles!
* http://stackoverflow.com/questions/19202248/how-can-i-use-jsmanagedvalue-to-avoid-a-reference-cycle-without-the-jsvalue-gett
* We'll live with the leak for now, but need to clean this up asap:
* Passing a reference to the `context` to the bridge would make it easy to
* execute JS. We can add `JSManagedValue`s to protect against this. The same
* needs to be done in `RCTTiming` and friends.
*/
/**
* Must be kept in sync with `MessageQueue.js`.
*/
typedef NS_ENUM(NSUInteger, RCTBridgeFields) {
RCTBridgeFieldRequestModuleIDs = 0,
RCTBridgeFieldMethodIDs,
RCTBridgeFieldParamss,
RCTBridgeFieldResponseCBIDs,
RCTBridgeFieldResponseReturnValues,
RCTBridgeFieldFlushDateMillis
};
@class RCTRootView;
/**
* Utilities for constructing common response objects. When sending a
@@ -59,19 +34,19 @@ static inline NSDictionary *RCTAPIErrorObject(NSString *msg)
@interface RCTBridge : NSObject <RCTInvalidating>
- (instancetype)initWithJavaScriptExecutor:(id<RCTJavaScriptExecutor>)javaScriptExecutor
shadowQueue:(dispatch_queue_t)shadowQueue
javaScriptModulesConfig:(NSDictionary *)javaScriptModulesConfig;
- (void)enqueueJSCall:(NSUInteger)moduleID methodID:(NSUInteger)methodID args:(NSArray *)args;
- (void)enqueueApplicationScript:(NSString *)script url:(NSURL *)url onComplete:(RCTJavaScriptCompleteBlock)onComplete;
- (void)enqueueUpdateTimers;
@property (nonatomic, readonly) RCTUIManager *uiManager;
@property (nonatomic, readonly) RCTEventDispatcher *eventDispatcher;
@property (nonatomic, readonly) dispatch_queue_t shadowQueue;
// For use in implementing delegates, which may need to queue responses.
- (RCTResponseSenderBlock)createResponseSenderBlock:(NSInteger)callbackID;
- (void)registerRootView:(RCTRootView *)rootView;
/**
* Global logging function will print to both xcode and js debugger consoles.
*

View File

@@ -9,11 +9,34 @@
#import "RCTEventDispatcher.h"
#import "RCTLog.h"
#import "RCTModuleIDs.h"
#import "RCTTiming.h"
#import "RCTUIManager.h"
#import "RCTUtils.h"
NSString *RCTModuleName(Class moduleClass)
/**
* Functions are the one thing that aren't automatically converted to OBJC
* blocks, according to this revert: http://trac.webkit.org/changeset/144489
* They must be expressed as `JSValue`s.
*
* But storing callbacks causes reference cycles!
* http://stackoverflow.com/questions/19202248/how-can-i-use-jsmanagedvalue-to-avoid-a-reference-cycle-without-the-jsvalue-gett
* We'll live with the leak for now, but need to clean this up asap:
* Passing a reference to the `context` to the bridge would make it easy to
* execute JS. We can add `JSManagedValue`s to protect against this. The same
* needs to be done in `RCTTiming` and friends.
*/
/**
* Must be kept in sync with `MessageQueue.js`.
*/
typedef NS_ENUM(NSUInteger, RCTBridgeFields) {
RCTBridgeFieldRequestModuleIDs = 0,
RCTBridgeFieldMethodIDs,
RCTBridgeFieldParamss,
RCTBridgeFieldResponseCBIDs,
RCTBridgeFieldResponseReturnValues,
RCTBridgeFieldFlushDateMillis
};
static NSString *RCTModuleName(Class moduleClass)
{
if ([moduleClass respondsToSelector:@selector(moduleName)]) {
@@ -22,22 +45,11 @@ NSString *RCTModuleName(Class moduleClass)
} else {
// Default implementation, works in most cases
NSString *className = NSStringFromClass(moduleClass);
// TODO: be more consistent with naming so that this check isn't needed
if ([moduleClass conformsToProtocol:@protocol(RCTNativeViewModule)]) {
if ([className hasPrefix:@"RCTUI"]) {
className = [className substringFromIndex:@"RCT".length];
}
if ([className hasSuffix:@"Manager"]) {
className = [className substringToIndex:className.length - @"Manager".length];
}
}
return className;
return NSStringFromClass(moduleClass);
}
}
NSDictionary *RCTNativeModuleClasses(void)
static NSDictionary *RCTNativeModuleClasses(void)
{
static NSMutableDictionary *modules;
static dispatch_once_t onceToken;
@@ -79,46 +91,30 @@ NSDictionary *RCTNativeModuleClasses(void)
{
NSMutableDictionary *_moduleInstances;
NSDictionary *_javaScriptModulesConfig;
dispatch_queue_t _shadowQueue;
RCTTiming *_timing;
id<RCTJavaScriptExecutor> _javaScriptExecutor;
}
static id<RCTJavaScriptExecutor> _latestJSExecutor;
- (instancetype)initWithJavaScriptExecutor:(id<RCTJavaScriptExecutor>)javaScriptExecutor
shadowQueue:(dispatch_queue_t)shadowQueue
javaScriptModulesConfig:(NSDictionary *)javaScriptModulesConfig
{
if ((self = [super init])) {
_javaScriptExecutor = javaScriptExecutor;
_latestJSExecutor = _javaScriptExecutor;
_shadowQueue = shadowQueue;
_eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self];
_moduleInstances = [[NSMutableDictionary alloc] init];
// TODO (#5906496): Remove special case
_timing = [[RCTTiming alloc] initWithBridge:self];
_javaScriptModulesConfig = javaScriptModulesConfig;
_moduleInstances[RCTModuleName([RCTTiming class])] = _timing;
// TODO (#5906496): Remove special case
NSMutableDictionary *viewManagers = [[NSMutableDictionary alloc] init];
[RCTNativeModuleClasses() enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, Class moduleClass, BOOL *stop) {
if ([moduleClass conformsToProtocol:@protocol(RCTNativeViewModule)]) {
viewManagers[moduleName] = [[moduleClass alloc] init];
}
}];
_uiManager = [[RCTUIManager alloc] initWithShadowQueue:_shadowQueue viewManagers:viewManagers];
_uiManager.eventDispatcher = _eventDispatcher;
_moduleInstances[RCTModuleName([RCTUIManager class])] = _uiManager;
[_moduleInstances addEntriesFromDictionary:viewManagers];
// Register remaining modules
_shadowQueue = dispatch_queue_create("com.facebook.ReactKit.ShadowQueue", DISPATCH_QUEUE_SERIAL);
// Register modules
_moduleInstances = [[NSMutableDictionary alloc] init];
[RCTNativeModuleClasses() enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, Class moduleClass, BOOL *stop) {
if (_moduleInstances[moduleName] == nil) {
_moduleInstances[moduleName] = [[moduleClass alloc] init];
if ([moduleClass instancesRespondToSelector:@selector(initWithBridge:)]) {
_moduleInstances[moduleName] = [[moduleClass alloc] initWithBridge:self];
} else {
_moduleInstances[moduleName] = [[moduleClass alloc] init];
}
}
}];
@@ -148,18 +144,16 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
_javaScriptExecutor = nil;
dispatch_sync(_shadowQueue, ^{
// Make sure all dispatchers have been executed before
// freeing up memory from _asyncHookMapByModuleID
// Make sure all dispatchers have been executed before continuing
// TODO: is this still needed?
});
for (id target in _moduleInstances.objectEnumerator) {
if ([target respondsToSelector:@selector(invalidate)]) {
[(id<RCTInvalidating>)target invalidate];
}
}
[_moduleInstances removeAllObjects];
_timing = nil;
}
/**
@@ -199,11 +193,6 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
}];
}
- (void)enqueueUpdateTimers
{
[_timing enqueueUpdateTimers];
}
#pragma mark - Payload Generation
- (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args
@@ -274,12 +263,12 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
}
}
NSArray *moduleIDs = [requestsArray objectAtIndex:RCTBridgeFieldRequestModuleIDs];
NSArray *methodIDs = [requestsArray objectAtIndex:RCTBridgeFieldMethodIDs];
NSArray *paramss = [requestsArray objectAtIndex:RCTBridgeFieldParamss];
NSArray *moduleIDs = requestsArray[RCTBridgeFieldRequestModuleIDs];
NSArray *methodIDs = requestsArray[RCTBridgeFieldMethodIDs];
NSArray *paramsArrays = requestsArray[RCTBridgeFieldParamss];
NSUInteger numRequests = [moduleIDs count];
BOOL allSame = numRequests == [methodIDs count] && numRequests == [paramss count];
BOOL allSame = numRequests == [methodIDs count] && numRequests == [paramsArrays count];
if (!allSame) {
RCTLogMustFix(@"Invalid data message - all must be length: %zd", numRequests);
return;
@@ -288,31 +277,102 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
for (NSUInteger i = 0; i < numRequests; i++) {
@autoreleasepool {
[self _handleRequestNumber:i
moduleID:[moduleIDs objectAtIndex:i]
methodID:[methodIDs objectAtIndex:i]
params:[paramss objectAtIndex:i]];
moduleID:[moduleIDs[i] integerValue]
methodID:[methodIDs[i] integerValue]
params:paramsArrays[i]];
}
}
// Update modules
for (id target in _moduleInstances.objectEnumerator) {
if ([target respondsToSelector:@selector(batchDidComplete)]) {
dispatch_async(_shadowQueue, ^{
// TODO: only used by RCTUIManager - can we eliminate this special case?
dispatch_async(_shadowQueue, ^{
for (id target in _moduleInstances.objectEnumerator) {
if ([target respondsToSelector:@selector(batchDidComplete)]) {
[target batchDidComplete];
});
}
}
}
});
}
- (void)_handleRequestNumber:(NSUInteger)i moduleID:(id)moduleID methodID:(id)methodID params:(id)params
- (BOOL)_handleRequestNumber:(NSUInteger)i
moduleID:(NSInteger)moduleID
methodID:(NSInteger)methodID
params:(NSArray *)params
{
if (![moduleID isKindOfClass:[NSNumber class]] || ![methodID isKindOfClass:[NSNumber class]] || ![params isKindOfClass:[NSArray class]]) {
if (![params isKindOfClass:[NSArray class]]) {
RCTLogMustFix(@"Invalid module/method/params tuple for request #%zd", i);
return;
return NO;
}
[self _dispatchUsingAsyncHookMapWithModuleID:[moduleID integerValue]
methodID:[methodID integerValue]
params:params];
if (moduleID < 0 || moduleID >= RCTExportedMethodsByModule().count) {
return NO;
}
NSString *moduleName = RCTExportedModuleNameAtSortedIndex(moduleID);
NSArray *methods = RCTExportedMethodsByModule()[moduleName];
if (methodID < 0 || methodID >= methods.count) {
return NO;
}
RCTModuleMethod *method = methods[methodID];
NSUInteger methodArity = method.arity;
if (params.count != methodArity) {
RCTLogMustFix(@"Expected %tu arguments but got %tu invoking %@.%@",
methodArity,
params.count,
moduleName,
method.JSMethodName);
return NO;
}
__weak RCTBridge *weakSelf = self;
dispatch_async(_shadowQueue, ^{
__strong RCTBridge *strongSelf = weakSelf;
if (!strongSelf.isValid) {
// strongSelf has been invalidated since the dispatch_async call and this
// invocation should not continue.
return;
}
NSInvocation *invocation = [RCTBridge invocationForAdditionalArguments:methodArity];
// TODO: we should just store module instances by index, since that's how we look them up anyway
id target = strongSelf->_moduleInstances[moduleName];
RCTAssert(target != nil, @"No module found for name '%@'", moduleName);
[invocation setArgument:&target atIndex:0];
SEL selector = method.selector;
[invocation setArgument:&selector atIndex:1];
// Retain used blocks until after invocation completes.
NSMutableArray *blocks = [NSMutableArray array];
[params enumerateObjectsUsingBlock:^(id param, NSUInteger idx, BOOL *stop) {
if ([param isEqual:[NSNull null]]) {
param = nil;
} else if ([method.blockArgumentIndexes containsIndex:idx]) {
id block = [strongSelf createResponseSenderBlock:[param integerValue]];
[blocks addObject:block];
param = block;
}
[invocation setArgument:&param atIndex:idx + 2];
}];
@try {
[invocation invoke];
}
@catch (NSException *exception) {
RCTLogMustFix(@"Exception thrown while invoking %@ on target %@ with params %@: %@", method.JSMethodName, target, params, exception);
}
@finally {
// Force `blocks` to remain alive until here.
blocks = nil;
}
});
return YES;
}
/**
@@ -352,84 +412,6 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
return invocation;
}
- (BOOL)_dispatchUsingAsyncHookMapWithModuleID:(NSInteger)moduleID
methodID:(NSInteger)methodID
params:(NSArray *)params
{
if (moduleID < 0 || moduleID >= RCTExportedMethodsByModule().count) {
return NO;
}
NSString *moduleName = RCTExportedModuleNameAtSortedIndex(moduleID);
NSArray *methods = RCTExportedMethodsByModule()[moduleName];
if (methodID < 0 || methodID >= methods.count) {
return NO;
}
RCTModuleMethod *method = methods[methodID];
NSUInteger methodArity = method.arity;
if (params.count != methodArity) {
RCTLogMustFix(
@"Expected %tu arguments but got %tu invoking %@.%@",
methodArity,
params.count,
moduleName,
method.JSMethodName
);
return NO;
}
__weak RCTBridge *weakSelf = self;
dispatch_async(_shadowQueue, ^{
__strong RCTBridge *strongSelf = weakSelf;
if (!strongSelf.isValid) {
// strongSelf has been invalidated since the dispatch_async call and this
// invocation should not continue.
return;
}
NSInvocation *invocation = [RCTBridge invocationForAdditionalArguments:methodArity];
// TODO: we should just store module instances by index, since that's how we look them up anyway
id target = strongSelf->_moduleInstances[moduleName];
RCTAssert(target != nil, @"No module found for name '%@'", moduleName);
[invocation setArgument:&target atIndex:0];
SEL selector = method.selector;
[invocation setArgument:&selector atIndex:1];
// Retain used blocks until after invocation completes.
NSMutableArray *blocks = [NSMutableArray array];
[params enumerateObjectsUsingBlock:^(id param, NSUInteger idx, BOOL *stop) {
if ([param isEqual:[NSNull null]]) {
param = nil;
} else if ([method.blockArgumentIndexes containsIndex:idx]) {
id block = [strongSelf createResponseSenderBlock:[param integerValue]];
[blocks addObject:block];
param = block;
}
[invocation setArgument:&param atIndex:idx + 2];
}];
@try {
[invocation invoke];
}
@catch (NSException *exception) {
RCTLogMustFix(@"Exception thrown while invoking %@ on target %@ with params %@: %@", method.JSMethodName, target, params, exception);
}
@finally {
// Force `blocks` to remain alive until here.
blocks = nil;
}
});
return YES;
}
- (void)doneRegisteringModules
{
RCTAssertMainThread();
@@ -456,8 +438,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
moduleConfig[@"methods"] = methods;
id target = [_moduleInstances objectForKey:moduleName];
if ([target respondsToSelector:@selector(constantsToExport)] && ![target conformsToProtocol:@protocol(RCTNativeViewModule)]) {
// TODO: find a more elegant way to handle RCTNativeViewModule constants as a special case
if ([target respondsToSelector:@selector(constantsToExport)]) {
moduleConfig[@"constants"] = [target constantsToExport];
}
moduleConfigs[moduleName] = moduleConfig;
@@ -484,6 +465,16 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
}
}
- (void)registerRootView:(RCTRootView *)rootView
{
// TODO: only used by RCTUIManager - can we eliminate this special case?
for (id target in _moduleInstances.objectEnumerator) {
if ([target respondsToSelector:@selector(registerRootView:)]) {
[target registerRootView:rootView];
}
}
}
+ (BOOL)hasValidJSExecutor
{
return (_latestJSExecutor != nil && [_latestJSExecutor isValid]);

View File

@@ -4,6 +4,7 @@
#import "RCTLog.h"
@class RCTBridge;
@class RCTSparseArray;
@class RCTUIManager;
@@ -42,6 +43,12 @@ typedef void (^RCTResponseSenderBlock)(NSArray *response);
@protocol RCTNativeModule <NSObject>
@optional
/**
* Optional initializer for modules that require access
* to bridge features, such as sending events or making JS calls
*/
- (instancetype)initWithBridge:(RCTBridge *)bridge;
/**
* Place this macro inside the method body of any method you want
* to expose to JS. The optional js_name argument will be used as
@@ -74,7 +81,7 @@ _RCTExportSectionName))) static const RCTExportEntry __rct_export_entry__ = { __
/**
* Provides minimal interface needed to register a UIViewManager module
*/
@protocol RCTNativeViewModule <RCTNativeModule>
@protocol RCTNativeViewModule <NSObject>
/**
* This method instantiates a native view to be managed by the module.
@@ -83,6 +90,12 @@ _RCTExportSectionName))) static const RCTExportEntry __rct_export_entry__ = { __
@optional
/**
* The module name exposed to JS. If omitted, this will be inferred
* automatically by using the view module's class name.
*/
+ (NSString *)moduleName;
/**
* This method instantiates a shadow view to be managed by the module. If omitted,
* an ordinary RCTShadowView instance will be created.
@@ -167,6 +180,12 @@ RCT_REMAP_VIEW_PROPERTY(name, name)
*/
- (NSDictionary *)customDirectEventTypes;
/**
* Injects constants into JS. These constants are made accessible via
* NativeModules.moduleName.X.
*/
- (NSDictionary *)constantsToExport;
/**
* To deprecate, hopefully
*/

View File

@@ -14,9 +14,7 @@
* JavaScript so that applications can clean up resources. (launch blocker).
* TODO: Incremental module loading. (low pri).
*/
@interface RCTJavaScriptAppEngine : NSObject <RCTInvalidating>
@property (nonatomic, readonly, strong) RCTBridge *bridge;
@interface RCTJavaScriptAppEngine : NSObject
- (instancetype)initWithBridge:(RCTBridge *)bridge;
- (void)loadBundleAtURL:(NSURL *)moduleURL useCache:(BOOL)useCache onComplete:(RCTJavaScriptCompleteBlock)onComplete;

View File

@@ -22,11 +22,8 @@
*/
@implementation RCTJavaScriptAppEngine
{
BOOL _isPaused; // Pauses drawing/updating of the JSView
BOOL _pauseOnEnterBackground;
CADisplayLink *_displayLink;
NSTimer *_runTimer;
NSDictionary *_loadedResource;
__weak RCTBridge *_bridge;
}
- (instancetype)init
@@ -54,118 +51,13 @@
- (instancetype)initWithBridge:(RCTBridge *)bridge
{
RCTAssertMainThread();
if ((self = [super init])) {
_bridge = bridge;
_isPaused = NO;
self.pauseOnEnterBackground = YES;
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(run:)];
if (_displayLink) {
[_displayLink setFrameInterval:1];
[_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
} else {
RCTLogWarn(@"Failed to create a display link (probably on buildbot) - using an NSTimer for AppEngine instead.");
_runTimer = [NSTimer scheduledTimerWithTimeInterval:(1.0 / 60.0) target:self selector:@selector(run:) userInfo:nil repeats:YES];
}
}
return self;
}
/**
* TODO: Wait until operations on `javaScriptQueue` are complete.
*/
- (void)dealloc
{
RCTAssert(!self.valid, @"-invalidate must be called before -dealloc");
}
#pragma mark - RCTInvalidating
- (BOOL)isValid
{
return _displayLink != nil;
}
- (void)invalidate
{
[_bridge invalidate];
_bridge = nil;
[_displayLink invalidate];
_displayLink = nil;
// Remove from notification center
self.pauseOnEnterBackground = NO;
}
#pragma mark - Run loop
- (void)run:(CADisplayLink *)sender
{
if (!_isPaused) {
RCTAssertMainThread();
[_bridge enqueueUpdateTimers];
}
}
- (void)pauseRunLoop
{
if (!_isPaused) {
[_displayLink removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
_isPaused = YES;
}
}
- (void)resumeRunLoop
{
if (_isPaused) {
[_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
_isPaused = NO;
}
}
/**
* See warnings from lint: UIApplicationDidBecomeActive fires in a critical
* foreground path, and prevents the app from prioritizing the foreground
* processing. Consider using
* FBApplicationDidFinishEnteringForegroundAndIsNowIdleNotification.
*/
- (void)setPauseOnEnterBackground:(BOOL)pauses
{
NSArray *pauseN = @[
UIApplicationWillResignActiveNotification,
UIApplicationDidEnterBackgroundNotification,
UIApplicationWillTerminateNotification
];
NSArray *resumeN =
@[UIApplicationWillEnterForegroundNotification, UIApplicationDidBecomeActiveNotification];
if (pauses) {
[self observeKeyPaths:pauseN selector:@selector(pauseRunLoop)];
[self observeKeyPaths:resumeN selector:@selector(resumeRunLoop)];
}
else {
[self removeObserverForKeyPaths:pauseN];
[self removeObserverForKeyPaths:resumeN];
}
_pauseOnEnterBackground = pauses;
}
- (void)removeObserverForKeyPaths:(NSArray*)keyPaths
{
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
for (NSString *name in keyPaths) {
[nc removeObserver:self name:name object:nil];
}
}
- (void)observeKeyPaths:(NSArray*)keyPaths selector:(SEL)selector
{
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
for (NSString *name in keyPaths) {
[nc addObserver:self selector:selector name:name object:nil];
}
}
#pragma mark - Module and script loading
+ (void)resetCacheForBundleAtURL:(NSURL *)moduleURL

View File

@@ -18,7 +18,6 @@ NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification"
@implementation RCTRootView
{
dispatch_queue_t _shadowQueue;
RCTBridge *_bridge;
RCTJavaScriptAppEngine *_appEngine;
RCTTouchHandler *_touchHandler;
@@ -62,9 +61,6 @@ NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification"
- (void)setUp
{
// TODO: does it make sense to do this here? What if there's more than one host view?
_shadowQueue = dispatch_queue_create("com.facebook.ReactKit.ShadowQueue", DISPATCH_QUEUE_SERIAL);
// Every root view that is created must have a unique react tag.
// Numbering of these tags goes from 1, 11, 21, 31, etc
static NSInteger rootViewTag = 1;
@@ -95,16 +91,16 @@ NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification"
}
} else {
[_bridge.uiManager registerRootView:self];
[_bridge registerRootView:self];
NSString *moduleName = _moduleName ?: @"";
NSDictionary *appParameters = @{
@"rootTag": self.reactTag ?: @0,
@"initialProps": self.initialProperties ?: @{},
};
[_appEngine.bridge enqueueJSCall:RCTModuleIDBundler
methodID:RCTBundlerRunApplication
args:@[moduleName, appParameters]];
[_bridge enqueueJSCall:RCTModuleIDBundler
methodID:RCTBundlerRunApplication
args:@[moduleName, appParameters]];
}
}
@@ -125,12 +121,10 @@ NSString *const RCTRootViewReloadNotification = @"RCTRootViewReloadNotification"
};
[_executor invalidate];
[_appEngine invalidate];
[_bridge invalidate];
_executor = [[RCTContextExecutor alloc] init];
_bridge = [[RCTBridge alloc] initWithJavaScriptExecutor:_executor
shadowQueue:_shadowQueue
javaScriptModulesConfig:[RCTModuleIDs config]];
_appEngine = [[RCTJavaScriptAppEngine alloc] initWithBridge:_bridge];