diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTModuleInitTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTModuleInitTests.m index 83a549736..a1056a81e 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTModuleInitTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTModuleInitTests.m @@ -221,10 +221,16 @@ RCT_EXPORT_MODULE() - (void)testCustomSetBridgeModuleInitializedAtBridgeStartup { - RUN_RUNLOOP_WHILE(!_customSetBridgeModuleNotificationSent); + XCTAssertFalse(_customSetBridgeModuleNotificationSent); + + __block RCTTestCustomSetBridgeModule *module; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + module = [_bridge moduleForClass:[RCTTestCustomSetBridgeModule class]]; + }); + + RUN_RUNLOOP_WHILE(!module); XCTAssertTrue(_customSetBridgeModuleNotificationSent); - RCTTestCustomSetBridgeModule *module = [_bridge moduleForClass:[RCTTestCustomSetBridgeModule class]]; - XCTAssertTrue(module.setBridgeOnMainThread); + XCTAssertFalse(module.setBridgeOnMainThread); XCTAssertEqual(module.bridge, _bridge.batchedBridge); XCTAssertNotNil(module.methodQueue); } diff --git a/React/Base/RCTModuleData.m b/React/Base/RCTModuleData.m index 8daaba591..a4f0b27ed 100644 --- a/React/Base/RCTModuleData.m +++ b/React/Base/RCTModuleData.m @@ -37,19 +37,15 @@ _instanceLock = [NSLock new]; static IMP objectInitMethod; - static SEL setBridgeSelector; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ objectInitMethod = [NSObject instanceMethodForSelector:@selector(init)]; - setBridgeSelector = NSSelectorFromString(@"setBridge:"); }); - // If a module overrides `init`, `setBridge:` then we must assume that it - // expects for both of those methods to be called on the main thread, because - // they may need to access UIKit. - _requiresMainThreadSetup = - [_moduleClass instancesRespondToSelector:setBridgeSelector] || - (!_instance && [_moduleClass instanceMethodForSelector:@selector(init)] != objectInitMethod); + // If a module overrides `init` then we must assume that it expects to be + // initialized on the main thread, because it may need to access UIKit. + _requiresMainThreadSetup = !_instance && + [_moduleClass instanceMethodForSelector:@selector(init)] != objectInitMethod; // If a module overrides `constantsToExport` then we must assume that it // must be called on the main thread, because it may need to access UIKit. diff --git a/React/Base/RCTUtils.m b/React/Base/RCTUtils.m index ba936002d..aa0d11645 100644 --- a/React/Base/RCTUtils.m +++ b/React/Base/RCTUtils.m @@ -258,6 +258,10 @@ CGFloat RCTScreenScale() CGSize RCTScreenSize() { + // FIXME: this caches the bounds at app start, whatever those were, and then + // doesn't update when the device is rotated. We need to find another thread- + // safe way to get the screen size. + static CGSize size; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ diff --git a/React/Modules/RCTAppState.m b/React/Modules/RCTAppState.m index 011e299fb..2cdac21bf 100644 --- a/React/Modules/RCTAppState.m +++ b/React/Modules/RCTAppState.m @@ -43,13 +43,20 @@ RCT_EXPORT_MODULE() #pragma mark - Lifecycle +- (instancetype)init +{ + if ((self = [super init])) { + + // Needs to be called on the main thread, as it accesses UIApplication + _lastKnownState = RCTCurrentAppBackgroundState(); + } + return self; +} + - (void)setBridge:(RCTBridge *)bridge { _bridge = bridge; - // Is this thread-safe? - _lastKnownState = RCTCurrentAppBackgroundState(); - for (NSString *name in @[UIApplicationDidBecomeActiveNotification, UIApplicationDidEnterBackgroundNotification, UIApplicationDidFinishLaunchingNotification, diff --git a/React/Modules/RCTDevLoadingView.m b/React/Modules/RCTDevLoadingView.m index 19076288e..7305c186b 100644 --- a/React/Modules/RCTDevLoadingView.m +++ b/React/Modules/RCTDevLoadingView.m @@ -40,6 +40,13 @@ RCT_EXPORT_MODULE() [[NSNotificationCenter defaultCenter] removeObserver:self]; } +- (instancetype)init +{ + // We're only overriding this to ensure the module gets created at startup + // TODO (D3175632): Remove once we have more declarative control over module setup. + return [super init]; +} + - (void)setBridge:(RCTBridge *)bridge { _bridge = bridge; diff --git a/React/Modules/RCTUIManager.m b/React/Modules/RCTUIManager.m index 283c1b55e..d16141335 100644 --- a/React/Modules/RCTUIManager.m +++ b/React/Modules/RCTUIManager.m @@ -730,7 +730,7 @@ RCT_EXPORT_METHOD(removeSubviewsFromContainerWithID:(nonnull NSNumber *)containe - (void)_removeChildren:(NSArray> *)children fromContainer:(id)container - permanent: (BOOL)permanent + permanent:(BOOL)permanent { RCTLayoutAnimation *layoutAnimation = _layoutAnimation; RCTAnimation *deleteAnimation = layoutAnimation.deleteAnimation; diff --git a/React/Profiler/RCTPerfMonitor.m b/React/Profiler/RCTPerfMonitor.m index 8bb54e49c..27e0b9bd4 100644 --- a/React/Profiler/RCTPerfMonitor.m +++ b/React/Profiler/RCTPerfMonitor.m @@ -125,6 +125,25 @@ static vm_size_t RCTGetResidentMemorySize(void) RCT_EXPORT_MODULE() +- (instancetype)init +{ + // We're only overriding this to ensure the module gets created at startup + // TODO (D3175632): Remove once we have more declarative control over module setup. + return [super init]; +} + +- (dispatch_queue_t)methodQueue +{ + return dispatch_get_main_queue(); +} + +- (void)setBridge:(RCTBridge *)bridge +{ + _bridge = bridge; + + [_bridge.devMenu addItem:self.devMenuItem]; +} + - (void)invalidate { [self hide]; @@ -272,18 +291,6 @@ RCT_EXPORT_MODULE() return _metrics; } -- (dispatch_queue_t)methodQueue -{ - return dispatch_get_main_queue(); -} - -- (void)setBridge:(RCTBridge *)bridge -{ - _bridge = bridge; - - [_bridge.devMenu addItem:self.devMenuItem]; -} - - (void)show { if (_container) { diff --git a/React/Views/RCTViewManager.h b/React/Views/RCTViewManager.h index 021cc21dd..df532c1eb 100644 --- a/React/Views/RCTViewManager.h +++ b/React/Views/RCTViewManager.h @@ -30,7 +30,7 @@ typedef void (^RCTViewManagerUIBlock)(RCTUIManager *uiManager, NSDictionary