react-native/React/Modules/RCTAppState.m
Nick Lockwood 34ec6a91a9 Move setBridge: off main thread
Summary:
Previously, if a module implemented `setBridge:` we assumed that it needs to be initialised on the main thread. This assumption was not really warranted however, and it was a barrier to deferring module initialization.

This diff tweaks the rules so that only modules that override `init` or `constantsToExport**` are assumed to require main thread initialization, and others can be created lazily when they are first used.

WARNING: this will be a breaking change to any 3rd party modules that are assuming `setBridge:` is called on the main thread. Those modules should be rewritten to move any code that requires the main thread into `init` or `constantsToExport` instead.

`**` We will also be examining whether `constantsToExport` can be done lazily, but for now any module that uses it will still be created eagerly when the bridge starts up.

Reviewed By: javache

Differential Revision: D3240682

fb-gh-sync-id: 48f309e3158bbccb52141032baf70def3e609371
fbshipit-source-id: 48f309e3158bbccb52141032baf70def3e609371
2016-05-03 09:09:21 -07:00

122 lines
3.3 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 "RCTAppState.h"
#import "RCTAssert.h"
#import "RCTBridge.h"
#import "RCTEventDispatcher.h"
#import "RCTUtils.h"
static NSString *RCTCurrentAppBackgroundState()
{
static NSDictionary *states;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
states = @{
@(UIApplicationStateActive): @"active",
@(UIApplicationStateBackground): @"background"
};
});
if (RCTRunningInAppExtension()) {
return @"extension";
}
return states[@(RCTSharedApplication().applicationState)] ?: @"unknown";
}
@implementation RCTAppState
{
NSString *_lastKnownState;
}
@synthesize bridge = _bridge;
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;
for (NSString *name in @[UIApplicationDidBecomeActiveNotification,
UIApplicationDidEnterBackgroundNotification,
UIApplicationDidFinishLaunchingNotification,
UIApplicationWillResignActiveNotification,
UIApplicationWillEnterForegroundNotification]) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleAppStateDidChange:)
name:name
object:nil];
}
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleMemoryWarning)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
}
- (void)handleMemoryWarning
{
[_bridge.eventDispatcher sendDeviceEventWithName:@"memoryWarning"
body:nil];
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#pragma mark - App Notification Methods
- (void)handleAppStateDidChange:(NSNotification *)notification
{
NSString *newState;
if ([notification.name isEqualToString:UIApplicationWillResignActiveNotification]) {
newState = @"inactive";
} else if ([notification.name isEqualToString:UIApplicationWillEnterForegroundNotification]) {
newState = @"background";
} else {
newState = RCTCurrentAppBackgroundState();
}
if (![newState isEqualToString:_lastKnownState]) {
_lastKnownState = newState;
[_bridge.eventDispatcher sendDeviceEventWithName:@"appStateDidChange"
body:@{@"app_state": _lastKnownState}];
}
}
#pragma mark - Public API
/**
* Get the current background/foreground state of the app
*/
RCT_EXPORT_METHOD(getCurrentAppState:(RCTResponseSenderBlock)callback
error:(__unused RCTResponseSenderBlock)error)
{
callback(@[@{@"app_state": _lastKnownState}]);
}
@end