react-native/React/Modules/RCTAppState.m
Adam Comella 68b0ce657e iOS: Provide correct initial value for AppState.currentState
Summary:
Attempt to fix #7919.

Currently, if the app is launched into the background and you read `AppState.currentState` too soon, you will see the value `'active'` instead of `'background'`. This is because the default value of `AppState.currentState` is hardcoded to be `'active'` and it is initialized with the actual value asynchronously.

This attempts to fix the bug by having the `RCTAppState` module provide the initial state as a module constant.

As noted in #7919, it looks like this fix was already tried and reverted with 0fb3d8de83c4d8b79a8a110e9cd1300b39ab8918. zjj010104, hedgerwang, nicklockwood -- can you explain why? I would very much like to get this bug fixed. Nobody has followed up on the issue I filed so I decided to just go ahead and make a PR with my best guess at a fix.

**Test plan (required)**

Built a small app as described in the repro steps for #7919 and verified that, when the app is launched into the background, `init currentState: background` is printed. Also verified that  `i
Closes https://github.com/facebook/react-native/pull/8058

Differential Revision: D3554619

fbshipit-source-id: 5d950b85e335765552bbd3cf6ed91534062e35a1
2016-07-12 23:28:22 -07:00

124 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()
{
RCTAssertMainQueue();
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;
}
RCT_EXPORT_MODULE()
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
- (NSDictionary *)constantsToExport
{
return @{@"initialAppState": RCTCurrentAppBackgroundState()};
}
#pragma mark - Lifecycle
- (NSArray<NSString *> *)supportedEvents
{
return @[@"appStateDidChange", @"memoryWarning"];
}
- (void)startObserving
{
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)stopObserving
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
#pragma mark - App Notification Methods
- (void)handleMemoryWarning
{
[self sendEventWithName:@"memoryWarning" body:nil];
}
- (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;
[self sendEventWithName:@"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": RCTCurrentAppBackgroundState()}]);
}
@end