mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-06-16 21:59:52 +08:00
Location (iOS): Call back on requests only if they pass age and accuracy filters
Summary: iOS pretty much always immediately updates location with the last cached location. This leads to the getCurrentPosition() API often returning this stale location. This change adds filtering to keep waiting until CLLocationManager provides a location fix that passes the requirements for each pending request. This is potentially breaking in that clients that rely upon getCurrentPosition being extremely fast may find that location fixes take longer than before. However in such cases they should relax their requirements for maximumAge and/or accuracy. Reviewed By: mmmulani Differential Revision: D13889626 fbshipit-source-id: f566314ed5968151dad0839b99e0d3c9a562af13
This commit is contained in:
committed by
Facebook Github Bot
parent
6f9e47839d
commit
24b9889fb2
@@ -151,12 +151,14 @@ RCT_EXPORT_MODULE()
|
||||
|
||||
#pragma mark - Private API
|
||||
|
||||
- (void)beginLocationUpdatesWithDesiredAccuracy:(CLLocationAccuracy)desiredAccuracy distanceFilter:(CLLocationDistance)distanceFilter useSignificantChanges:(BOOL)useSignificantChanges
|
||||
- (void)_beginLocationUpdatesWithDesiredAccuracy:(CLLocationAccuracy)desiredAccuracy
|
||||
distanceFilter:(CLLocationDistance)distanceFilter
|
||||
useSignificantChanges:(BOOL)useSignificantChanges
|
||||
{
|
||||
if (!_locationConfiguration.skipPermissionRequests) {
|
||||
[self requestAuthorization];
|
||||
}
|
||||
|
||||
|
||||
if (!_locationManager) {
|
||||
_locationManager = [CLLocationManager new];
|
||||
_locationManager.delegate = self;
|
||||
@@ -172,6 +174,32 @@ RCT_EXPORT_MODULE()
|
||||
[_locationManager startUpdatingLocation];
|
||||
}
|
||||
|
||||
- (void)_stopUpdatingIfIdle {
|
||||
if (_pendingRequests.count == 0 && !_observingLocation) {
|
||||
_usingSignificantChanges ?
|
||||
[_locationManager stopMonitoringSignificantLocationChanges] :
|
||||
[_locationManager stopUpdatingLocation];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Static Helpers
|
||||
|
||||
static BOOL locationEventValid(NSDictionary<NSString *, id> *event, RCTLocationOptions options) {
|
||||
return [NSDate date].timeIntervalSince1970 - [RCTConvert NSTimeInterval:event[@"timestamp"]] < options.maximumAge &&
|
||||
[event[@"coords"][@"accuracy"] doubleValue] <= options.accuracy;
|
||||
}
|
||||
|
||||
static void checkLocationConfig()
|
||||
{
|
||||
#if RCT_DEV
|
||||
if (!([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"] ||
|
||||
[[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
|
||||
}
|
||||
|
||||
#pragma mark - Timeout handler
|
||||
|
||||
- (void)timeout:(NSTimer *)timer
|
||||
@@ -181,12 +209,8 @@ RCT_EXPORT_MODULE()
|
||||
request.errorBlock(@[RCTPositionError(RCTPositionErrorTimeout, message)]);
|
||||
[_pendingRequests removeObject:request];
|
||||
|
||||
// Stop updating if no pending requests
|
||||
if (_pendingRequests.count == 0 && !_observingLocation) {
|
||||
_usingSignificantChanges ?
|
||||
[_locationManager stopMonitoringSignificantLocationChanges] :
|
||||
[_locationManager stopUpdatingLocation];
|
||||
}
|
||||
// Stop updating if not observing and no pending requests
|
||||
[self _stopUpdatingIfIdle];
|
||||
}
|
||||
|
||||
#pragma mark - Public API
|
||||
@@ -229,9 +253,9 @@ RCT_EXPORT_METHOD(startObserving:(RCTLocationOptions)options)
|
||||
_observerOptions.accuracy = MIN(_observerOptions.accuracy, request.options.accuracy);
|
||||
}
|
||||
|
||||
[self beginLocationUpdatesWithDesiredAccuracy:_observerOptions.accuracy
|
||||
distanceFilter:_observerOptions.distanceFilter
|
||||
useSignificantChanges:_observerOptions.useSignificantChanges];
|
||||
[self _beginLocationUpdatesWithDesiredAccuracy:_observerOptions.accuracy
|
||||
distanceFilter:_observerOptions.distanceFilter
|
||||
useSignificantChanges:_observerOptions.useSignificantChanges];
|
||||
_observingLocation = YES;
|
||||
}
|
||||
|
||||
@@ -241,11 +265,8 @@ RCT_EXPORT_METHOD(stopObserving)
|
||||
_observingLocation = NO;
|
||||
|
||||
// Stop updating if no pending requests
|
||||
if (_pendingRequests.count == 0) {
|
||||
_usingSignificantChanges ?
|
||||
[_locationManager stopMonitoringSignificantLocationChanges] :
|
||||
[_locationManager stopUpdatingLocation];
|
||||
}
|
||||
[self _stopUpdatingIfIdle];
|
||||
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getCurrentPosition:(RCTLocationOptions)options
|
||||
@@ -278,9 +299,7 @@ RCT_EXPORT_METHOD(getCurrentPosition:(RCTLocationOptions)options
|
||||
}
|
||||
|
||||
// Check if previous recorded location exists and is good enough
|
||||
if (_lastLocationEvent &&
|
||||
[NSDate date].timeIntervalSince1970 - [RCTConvert NSTimeInterval:_lastLocationEvent[@"timestamp"]] < options.maximumAge &&
|
||||
[_lastLocationEvent[@"coords"][@"accuracy"] doubleValue] <= options.accuracy) {
|
||||
if (_lastLocationEvent && locationEventValid(_lastLocationEvent, options)) {
|
||||
|
||||
// Call success block with most recent known location
|
||||
successBlock(@[_lastLocationEvent]);
|
||||
@@ -307,9 +326,9 @@ RCT_EXPORT_METHOD(getCurrentPosition:(RCTLocationOptions)options
|
||||
if (_locationManager) {
|
||||
accuracy = MIN(_locationManager.desiredAccuracy, accuracy);
|
||||
}
|
||||
[self beginLocationUpdatesWithDesiredAccuracy:accuracy
|
||||
distanceFilter:options.distanceFilter
|
||||
useSignificantChanges:options.useSignificantChanges];
|
||||
[self _beginLocationUpdatesWithDesiredAccuracy:accuracy
|
||||
distanceFilter:options.distanceFilter
|
||||
useSignificantChanges:options.useSignificantChanges];
|
||||
}
|
||||
|
||||
#pragma mark - CLLocationManagerDelegate
|
||||
@@ -337,20 +356,17 @@ RCT_EXPORT_METHOD(getCurrentPosition:(RCTLocationOptions)options
|
||||
[self sendEventWithName:@"geolocationDidChange" body:_lastLocationEvent];
|
||||
}
|
||||
|
||||
// Fire all queued callbacks
|
||||
for (RCTLocationRequest *request in _pendingRequests) {
|
||||
request.successBlock(@[_lastLocationEvent]);
|
||||
[request.timeoutTimer invalidate];
|
||||
}
|
||||
[_pendingRequests removeAllObjects];
|
||||
|
||||
// Stop updating if not observing
|
||||
if (!_observingLocation) {
|
||||
_usingSignificantChanges ?
|
||||
[_locationManager stopMonitoringSignificantLocationChanges] :
|
||||
[_locationManager stopUpdatingLocation];
|
||||
// Fire off queued callbacks that pass maximumAge and accuracy filters
|
||||
for (RCTLocationRequest *request in [_pendingRequests copy]) {
|
||||
if (locationEventValid(_lastLocationEvent, request.options)) {
|
||||
request.successBlock(@[_lastLocationEvent]);
|
||||
[request.timeoutTimer invalidate];
|
||||
[_pendingRequests removeObject:request];
|
||||
}
|
||||
}
|
||||
|
||||
// Stop updating if not observing and no pending requests
|
||||
[self _stopUpdatingIfIdle];
|
||||
}
|
||||
|
||||
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
|
||||
@@ -384,15 +400,4 @@ RCT_EXPORT_METHOD(getCurrentPosition:(RCTLocationOptions)options
|
||||
|
||||
}
|
||||
|
||||
static void checkLocationConfig()
|
||||
{
|
||||
#if RCT_DEV
|
||||
if (!([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"] ||
|
||||
[[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
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user