Permalink
Browse files

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 #15062

Differential Revision: D5443331

Pulled By: javache

fbshipit-source-id: 0cf5b6cd831c5a7c8c25a5ddc2e410a9aa989bf4
  • Loading branch information...
jasongaare authored and facebook-github-bot committed Jul 24, 2017
1 parent 64899c0 commit f7043699b04e2fb29a892d14027abc99a8cf5af5
Showing with 37 additions and 15 deletions.
  1. +5 −4 Libraries/Geolocation/Geolocation.js
  2. +32 −11 Libraries/Geolocation/RCTLocationObserver.m
@@ -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) {
@@ -32,6 +32,7 @@ typedef NS_ENUM(NSInteger, RCTPositionErrorCode) {
double maximumAge;
double accuracy;
double distanceFilter;
BOOL useSignificantChanges;
} RCTLocationOptions;
@implementation RCTConvert (RCTLocationOptions)
@@ -47,7 +48,8 @@ + (RCTLocationOptions)RCTLocationOptions:(id)json
.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 @@ @implementation RCTLocationObserver
NSDictionary<NSString *, id> *_lastLocationEvent;
NSMutableArray<RCTLocationRequest *> *_pendingRequests;
BOOL _observingLocation;
BOOL _usingSignificantChanges;
RCTLocationOptions _observerOptions;
}
@@ -117,7 +120,10 @@ @implementation RCTLocationObserver
- (void)dealloc
{
[_locationManager stopUpdatingLocation];
_usingSignificantChanges ?
[_locationManager stopMonitoringSignificantLocationChanges] :
[_locationManager stopUpdatingLocation];
_locationManager.delegate = nil;
}
@@ -133,14 +139,18 @@ - (dispatch_queue_t)methodQueue
#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 @@ - (void)timeout:(NSTimer *)timer
// Stop updating if no pending requests
if (_pendingRequests.count == 0 && !_observingLocation) {
[_locationManager stopUpdatingLocation];
_usingSignificantChanges ?
[_locationManager stopMonitoringSignificantLocationChanges] :
[_locationManager stopUpdatingLocation];
}
}
@@ -195,7 +207,9 @@ - (void)timeout:(NSTimer *)timer
_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 @@ - (void)timeout:(NSTimer *)timer
// Stop updating if no pending requests
if (_pendingRequests.count == 0) {
[_locationManager stopUpdatingLocation];
_usingSignificantChanges ?
[_locationManager stopMonitoringSignificantLocationChanges] :
[_locationManager stopUpdatingLocation];
}
}
@@ -269,7 +285,9 @@ - (void)timeout:(NSTimer *)timer
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 @@ - (void)locationManager:(CLLocationManager *)manager
// 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
}

0 comments on commit f704369

Please sign in to comment.