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:
parent
64899c08f3
commit
f7043699b0
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue