diff --git a/Libraries/Geolocation/RCTLocationObserver.m b/Libraries/Geolocation/RCTLocationObserver.m index d5d2c2f21aa..f3b2209a39a 100644 --- a/Libraries/Geolocation/RCTLocationObserver.m +++ b/Libraries/Geolocation/RCTLocationObserver.m @@ -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 *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