Use Apple's significant-change API (for iOS 11 UX)

Summary:
In the yet-to-be-released iOS 11, Apple has changed the way they notify the user of location services. (You can watch their session from WWDC about all the changes [here](https://developer.apple.com/videos/play/wwdc2017/713/).)

The current implementation of `RCTLocationObserver` uses the standard location services from Apple. When the user has granted `Always` location permission and the application uses the background location service, the user is presented with the *_Blue Bar of Shame_* (for more information check out [this blog post](https://blog.set.gl/ios-11-location-permissions-and-avoiding-the-blue-bar-of-shame-1cee6cd93bbe)):

![image](https://user-images.githubusercontent.com/15896334/28285133-281e425c-6af9-11e7-9177-61b879ab593c.png)

* Added `useSignificantChanges` boolean to the options passed.

* Added `_usingSignificantChanges` boolean based on user options. If `true`, then the CLLocationManager will use functions `startMonitoringSignificantLocationChanges`/ `stopMonitoringSignificantLocationChanges` rather than the standard location services.
* Changed method signature of `beginLocationUpdatesWithDesiredAccuracy` to include `useSignificantChanges` flag
* Added check for new `NSLocationAlwaysAndWhenInUseUsageDescription`

All unit tests passed.

Tested in simulator and on device, toggling `useSignificantChanges` option when calling `watchPosition`. Results were as expected. **When `TRUE`, the _Blue Bar of Shame_ was not present.**

Changes do not affect Android and location services still work as expected on Android.

* Change is for iOS only
* Using a different API will have different accuracy results. Adding `useSignificantChanges` as an option was by design so apps that want to have most accurate and most frequent update can still use standard location services.
Closes https://github.com/facebook/react-native/pull/15062

Differential Revision: D5443331

Pulled By: javache

fbshipit-source-id: 0cf5b6cd831c5a7c8c25a5ddc2e410a9aa989bf4
This commit is contained in:
Jason Gaare 2017-07-24 11:15:27 -07:00 committed by Facebook Github Bot
parent 64899c08f3
commit f7043699b0
2 changed files with 37 additions and 15 deletions

View File

@ -27,10 +27,11 @@ var subscriptions = [];
var updatesEnabled = false; var updatesEnabled = false;
type GeoOptions = { type GeoOptions = {
timeout: number, timeout?: number,
maximumAge: number, maximumAge?: number,
enableHighAccuracy: bool, enableHighAccuracy?: bool,
distanceFilter: number, distanceFilter: number,
useSignificantChanges?: bool,
} }
/** /**
@ -124,7 +125,7 @@ var Geolocation = {
/* /*
* Invokes the success callback whenever the location changes. Supported * Invokes the success callback whenever the location changes. Supported
* options: timeout (ms), maximumAge (ms), enableHighAccuracy (bool), distanceFilter(m) * options: timeout (ms), maximumAge (ms), enableHighAccuracy (bool), distanceFilter(m), useSignificantChanges (bool)
*/ */
watchPosition: function(success: Function, error?: Function, options?: GeoOptions): number { watchPosition: function(success: Function, error?: Function, options?: GeoOptions): number {
if (!updatesEnabled) { if (!updatesEnabled) {

View File

@ -32,6 +32,7 @@ typedef struct {
double maximumAge; double maximumAge;
double accuracy; double accuracy;
double distanceFilter; double distanceFilter;
BOOL useSignificantChanges;
} RCTLocationOptions; } RCTLocationOptions;
@implementation RCTConvert (RCTLocationOptions) @implementation RCTConvert (RCTLocationOptions)
@ -47,7 +48,8 @@ typedef struct {
.timeout = [RCTConvert NSTimeInterval:options[@"timeout"]] ?: INFINITY, .timeout = [RCTConvert NSTimeInterval:options[@"timeout"]] ?: INFINITY,
.maximumAge = [RCTConvert NSTimeInterval:options[@"maximumAge"]] ?: INFINITY, .maximumAge = [RCTConvert NSTimeInterval:options[@"maximumAge"]] ?: INFINITY,
.accuracy = [RCTConvert BOOL:options[@"enableHighAccuracy"]] ? kCLLocationAccuracyBest : RCT_DEFAULT_LOCATION_ACCURACY, .accuracy = [RCTConvert BOOL:options[@"enableHighAccuracy"]] ? kCLLocationAccuracyBest : RCT_DEFAULT_LOCATION_ACCURACY,
.distanceFilter = distanceFilter .distanceFilter = distanceFilter,
.useSignificantChanges = [RCTConvert BOOL:options[@"useSignificantChanges"]] ?: NO,
}; };
} }
@ -108,6 +110,7 @@ static NSDictionary<NSString *, id> *RCTPositionError(RCTPositionErrorCode code,
NSDictionary<NSString *, id> *_lastLocationEvent; NSDictionary<NSString *, id> *_lastLocationEvent;
NSMutableArray<RCTLocationRequest *> *_pendingRequests; NSMutableArray<RCTLocationRequest *> *_pendingRequests;
BOOL _observingLocation; BOOL _observingLocation;
BOOL _usingSignificantChanges;
RCTLocationOptions _observerOptions; RCTLocationOptions _observerOptions;
} }
@ -117,7 +120,10 @@ RCT_EXPORT_MODULE()
- (void)dealloc - (void)dealloc
{ {
[_locationManager stopUpdatingLocation]; _usingSignificantChanges ?
[_locationManager stopMonitoringSignificantLocationChanges] :
[_locationManager stopUpdatingLocation];
_locationManager.delegate = nil; _locationManager.delegate = nil;
} }
@ -133,14 +139,18 @@ RCT_EXPORT_MODULE()
#pragma mark - Private API #pragma mark - Private API
- (void)beginLocationUpdatesWithDesiredAccuracy:(CLLocationAccuracy)desiredAccuracy distanceFilter:(CLLocationDistance)distanceFilter - (void)beginLocationUpdatesWithDesiredAccuracy:(CLLocationAccuracy)desiredAccuracy distanceFilter:(CLLocationDistance)distanceFilter useSignificantChanges:(BOOL)useSignificantChanges
{ {
[self requestAuthorization]; [self requestAuthorization];
_locationManager.distanceFilter = distanceFilter; _locationManager.distanceFilter = distanceFilter;
_locationManager.desiredAccuracy = desiredAccuracy; _locationManager.desiredAccuracy = desiredAccuracy;
_usingSignificantChanges = useSignificantChanges;
// Start observing location // Start observing location
[_locationManager startUpdatingLocation]; _usingSignificantChanges ?
[_locationManager startMonitoringSignificantLocationChanges] :
[_locationManager startUpdatingLocation];
} }
#pragma mark - Timeout handler #pragma mark - Timeout handler
@ -154,7 +164,9 @@ RCT_EXPORT_MODULE()
// Stop updating if no pending requests // Stop updating if no pending requests
if (_pendingRequests.count == 0 && !_observingLocation) { if (_pendingRequests.count == 0 && !_observingLocation) {
[_locationManager stopUpdatingLocation]; _usingSignificantChanges ?
[_locationManager stopMonitoringSignificantLocationChanges] :
[_locationManager stopUpdatingLocation];
} }
} }
@ -195,7 +207,9 @@ RCT_EXPORT_METHOD(startObserving:(RCTLocationOptions)options)
_observerOptions.accuracy = MIN(_observerOptions.accuracy, request.options.accuracy); _observerOptions.accuracy = MIN(_observerOptions.accuracy, request.options.accuracy);
} }
[self beginLocationUpdatesWithDesiredAccuracy:_observerOptions.accuracy distanceFilter:_observerOptions.distanceFilter]; [self beginLocationUpdatesWithDesiredAccuracy:_observerOptions.accuracy
distanceFilter:_observerOptions.distanceFilter
useSignificantChanges:_observerOptions.useSignificantChanges];
_observingLocation = YES; _observingLocation = YES;
} }
@ -206,7 +220,9 @@ RCT_EXPORT_METHOD(stopObserving)
// Stop updating if no pending requests // Stop updating if no pending requests
if (_pendingRequests.count == 0) { if (_pendingRequests.count == 0) {
[_locationManager stopUpdatingLocation]; _usingSignificantChanges ?
[_locationManager stopMonitoringSignificantLocationChanges] :
[_locationManager stopUpdatingLocation];
} }
} }
@ -269,7 +285,9 @@ RCT_EXPORT_METHOD(getCurrentPosition:(RCTLocationOptions)options
if (_locationManager) { if (_locationManager) {
accuracy = MIN(_locationManager.desiredAccuracy, accuracy); accuracy = MIN(_locationManager.desiredAccuracy, accuracy);
} }
[self beginLocationUpdatesWithDesiredAccuracy:accuracy distanceFilter:options.distanceFilter]; [self beginLocationUpdatesWithDesiredAccuracy:accuracy
distanceFilter:options.distanceFilter
useSignificantChanges:options.useSignificantChanges];
} }
#pragma mark - CLLocationManagerDelegate #pragma mark - CLLocationManagerDelegate
@ -306,7 +324,9 @@ RCT_EXPORT_METHOD(getCurrentPosition:(RCTLocationOptions)options
// Stop updating if not observing // Stop updating if not observing
if (!_observingLocation) { if (!_observingLocation) {
[_locationManager stopUpdatingLocation]; _usingSignificantChanges ?
[_locationManager stopMonitoringSignificantLocationChanges] :
[_locationManager stopUpdatingLocation];
} }
// Reset location accuracy if desiredAccuracy is changed. // Reset location accuracy if desiredAccuracy is changed.
@ -356,8 +376,9 @@ static void checkLocationConfig()
{ {
#if RCT_DEV #if RCT_DEV
if (!([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"] || if (!([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"] ||
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"])) { [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"] ||
RCTLogError(@"Either NSLocationWhenInUseUsageDescription or NSLocationAlwaysUsageDescription key must be present in Info.plist to use geolocation."); [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysAndWhenInUseUsageDescription"])) {
RCTLogError(@"Either NSLocationWhenInUseUsageDescription or NSLocationAlwaysUsageDescription or NSLocationAlwaysAndWhenInUseUsageDescription key must be present in Info.plist to use geolocation.");
} }
#endif #endif
} }