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;
type GeoOptions = {
timeout: number,
maximumAge: number,
enableHighAccuracy: bool,
timeout?: number,
maximumAge?: number,
enableHighAccuracy?: bool,
distanceFilter: number,
useSignificantChanges?: bool,
}
/**
@ -124,7 +125,7 @@ var Geolocation = {
/*
* 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 {
if (!updatesEnabled) {

View File

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