react-native/Libraries/Geolocation/RCTLocationObserver.m

411 lines
13 KiB
Mathematica
Raw Normal View History

/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
2015-03-09 10:04:44 +00:00
#import "RCTLocationObserver.h"
#import <CoreLocation/CLError.h>
#import <CoreLocation/CLLocationManager.h>
#import <CoreLocation/CLLocationManagerDelegate.h>
#import <React/RCTAssert.h>
#import <React/RCTBridge.h>
#import <React/RCTConvert.h>
#import <React/RCTEventDispatcher.h>
#import <React/RCTLog.h>
2015-03-09 10:04:44 +00:00
typedef NS_ENUM(NSInteger, RCTPositionErrorCode) {
RCTPositionErrorDenied = 1,
RCTPositionErrorUnavailable,
RCTPositionErrorTimeout,
};
#define RCT_DEFAULT_LOCATION_ACCURACY kCLLocationAccuracyHundredMeters
iOS: Geolocation: Allow skipping of permission prompts Summary: This change enables developers to tell the Geolocation module to skip its permissions requests so the app can manage the permissions requests on its own. React Native Android's Geolocation module already requires apps to request permissions on their own. Currently, the Geolocation module automatically makes permissions requests for you based on what you've specified in your property list file. However, the property list file doesn't always represent what permissions the app wants right now. For example, suppose an app sometimes wants location when "in use" and sometimes wants location "always" depending on what app features the user has engaged with. The Geolocation API will request the "always" location permission even if that's not what the app wants for the current scenario. This change enables developers to tell the Geolocation module to skip permission requests so that the app can explicitly ask for the appropriate permissions. By default, Geolocation will still ask for permissions so this is not a breaking change. This change adds a method to Geolocation that is not supported by the web spec for geolocation (`setRNConfiguration`). This method includes `RN` in the name to minimize the chances that the web spec will introduce an API with the same name. **Test plan (required)** Verified that Geolocation requests permissions by default and when `skipPermissionRequests` is `false`. Verified the permission requests are skipped when `skipPermissionRequests` is `true`. Also, my team is using this change in our app. Adam Comella Microsoft Corp. Closes https://github.com/facebook/react-native/pull/15096 Differential Revision: D5725563 Pulled By: javache fbshipit-source-id: fd23fedb7e57408fa0f0d0568e0f1ef2aea1cdd4
2017-08-29 11:00:41 +00:00
typedef struct {
BOOL skipPermissionRequests;
} RCTLocationConfiguration;
2015-03-09 10:04:44 +00:00
typedef struct {
double timeout;
double maximumAge;
double accuracy;
double distanceFilter;
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
2017-07-24 18:15:27 +00:00
BOOL useSignificantChanges;
2015-03-09 10:04:44 +00:00
} RCTLocationOptions;
@implementation RCTConvert (RCTLocationOptions)
iOS: Geolocation: Allow skipping of permission prompts Summary: This change enables developers to tell the Geolocation module to skip its permissions requests so the app can manage the permissions requests on its own. React Native Android's Geolocation module already requires apps to request permissions on their own. Currently, the Geolocation module automatically makes permissions requests for you based on what you've specified in your property list file. However, the property list file doesn't always represent what permissions the app wants right now. For example, suppose an app sometimes wants location when "in use" and sometimes wants location "always" depending on what app features the user has engaged with. The Geolocation API will request the "always" location permission even if that's not what the app wants for the current scenario. This change enables developers to tell the Geolocation module to skip permission requests so that the app can explicitly ask for the appropriate permissions. By default, Geolocation will still ask for permissions so this is not a breaking change. This change adds a method to Geolocation that is not supported by the web spec for geolocation (`setRNConfiguration`). This method includes `RN` in the name to minimize the chances that the web spec will introduce an API with the same name. **Test plan (required)** Verified that Geolocation requests permissions by default and when `skipPermissionRequests` is `false`. Verified the permission requests are skipped when `skipPermissionRequests` is `true`. Also, my team is using this change in our app. Adam Comella Microsoft Corp. Closes https://github.com/facebook/react-native/pull/15096 Differential Revision: D5725563 Pulled By: javache fbshipit-source-id: fd23fedb7e57408fa0f0d0568e0f1ef2aea1cdd4
2017-08-29 11:00:41 +00:00
+ (RCTLocationConfiguration)RCTLocationConfiguration:(id)json
{
NSDictionary<NSString *, id> *options = [RCTConvert NSDictionary:json];
return (RCTLocationConfiguration) {
.skipPermissionRequests = [RCTConvert BOOL:options[@"skipPermissionRequests"]]
};
}
+ (RCTLocationOptions)RCTLocationOptions:(id)json
2015-03-09 10:04:44 +00:00
{
NSDictionary<NSString *, id> *options = [RCTConvert NSDictionary:json];
double distanceFilter = options[@"distanceFilter"] == NULL ? RCT_DEFAULT_LOCATION_ACCURACY
: [RCTConvert double:options[@"distanceFilter"]] ?: kCLDistanceFilterNone;
2015-03-09 10:04:44 +00:00
return (RCTLocationOptions){
.timeout = [RCTConvert NSTimeInterval:options[@"timeout"]] ?: INFINITY,
.maximumAge = [RCTConvert NSTimeInterval:options[@"maximumAge"]] ?: INFINITY,
.accuracy = [RCTConvert BOOL:options[@"enableHighAccuracy"]] ? kCLLocationAccuracyBest : RCT_DEFAULT_LOCATION_ACCURACY,
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
2017-07-24 18:15:27 +00:00
.distanceFilter = distanceFilter,
.useSignificantChanges = [RCTConvert BOOL:options[@"useSignificantChanges"]] ?: NO,
2015-03-09 10:04:44 +00:00
};
}
@end
static NSDictionary<NSString *, id> *RCTPositionError(RCTPositionErrorCode code, NSString *msg /* nil for default */)
2015-03-09 10:04:44 +00:00
{
if (!msg) {
switch (code) {
case RCTPositionErrorDenied:
msg = @"User denied access to location services.";
break;
case RCTPositionErrorUnavailable:
msg = @"Unable to retrieve location.";
break;
case RCTPositionErrorTimeout:
msg = @"The location request timed out.";
break;
}
}
return @{
@"code": @(code),
@"message": msg,
@"PERMISSION_DENIED": @(RCTPositionErrorDenied),
@"POSITION_UNAVAILABLE": @(RCTPositionErrorUnavailable),
@"TIMEOUT": @(RCTPositionErrorTimeout)
};
}
@interface RCTLocationRequest : NSObject
@property (nonatomic, copy) RCTResponseSenderBlock successBlock;
@property (nonatomic, copy) RCTResponseSenderBlock errorBlock;
@property (nonatomic, assign) RCTLocationOptions options;
@property (nonatomic, strong) NSTimer *timeoutTimer;
@end
@implementation RCTLocationRequest
- (void)dealloc
{
if (_timeoutTimer.valid) {
[_timeoutTimer invalidate];
}
2015-03-09 10:04:44 +00:00
}
@end
@interface RCTLocationObserver () <CLLocationManagerDelegate>
@end
@implementation RCTLocationObserver
{
CLLocationManager *_locationManager;
NSDictionary<NSString *, id> *_lastLocationEvent;
NSMutableArray<RCTLocationRequest *> *_pendingRequests;
2015-03-09 10:04:44 +00:00
BOOL _observingLocation;
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
2017-07-24 18:15:27 +00:00
BOOL _usingSignificantChanges;
iOS: Geolocation: Allow skipping of permission prompts Summary: This change enables developers to tell the Geolocation module to skip its permissions requests so the app can manage the permissions requests on its own. React Native Android's Geolocation module already requires apps to request permissions on their own. Currently, the Geolocation module automatically makes permissions requests for you based on what you've specified in your property list file. However, the property list file doesn't always represent what permissions the app wants right now. For example, suppose an app sometimes wants location when "in use" and sometimes wants location "always" depending on what app features the user has engaged with. The Geolocation API will request the "always" location permission even if that's not what the app wants for the current scenario. This change enables developers to tell the Geolocation module to skip permission requests so that the app can explicitly ask for the appropriate permissions. By default, Geolocation will still ask for permissions so this is not a breaking change. This change adds a method to Geolocation that is not supported by the web spec for geolocation (`setRNConfiguration`). This method includes `RN` in the name to minimize the chances that the web spec will introduce an API with the same name. **Test plan (required)** Verified that Geolocation requests permissions by default and when `skipPermissionRequests` is `false`. Verified the permission requests are skipped when `skipPermissionRequests` is `true`. Also, my team is using this change in our app. Adam Comella Microsoft Corp. Closes https://github.com/facebook/react-native/pull/15096 Differential Revision: D5725563 Pulled By: javache fbshipit-source-id: fd23fedb7e57408fa0f0d0568e0f1ef2aea1cdd4
2017-08-29 11:00:41 +00:00
RCTLocationConfiguration _locationConfiguration;
2015-03-09 10:04:44 +00:00
RCTLocationOptions _observerOptions;
}
RCT_EXPORT_MODULE()
2015-03-09 10:04:44 +00:00
#pragma mark - Lifecycle
- (void)dealloc
{
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
2017-07-24 18:15:27 +00:00
_usingSignificantChanges ?
[_locationManager stopMonitoringSignificantLocationChanges] :
[_locationManager stopUpdatingLocation];
_locationManager.delegate = nil;
2015-03-09 10:04:44 +00:00
}
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
- (NSArray<NSString *> *)supportedEvents
{
return @[@"geolocationDidChange", @"geolocationError"];
}
#pragma mark - Private API
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
2017-07-24 18:15:27 +00:00
- (void)beginLocationUpdatesWithDesiredAccuracy:(CLLocationAccuracy)desiredAccuracy distanceFilter:(CLLocationDistance)distanceFilter useSignificantChanges:(BOOL)useSignificantChanges
2015-03-09 10:04:44 +00:00
{
iOS: Geolocation: Allow skipping of permission prompts Summary: This change enables developers to tell the Geolocation module to skip its permissions requests so the app can manage the permissions requests on its own. React Native Android's Geolocation module already requires apps to request permissions on their own. Currently, the Geolocation module automatically makes permissions requests for you based on what you've specified in your property list file. However, the property list file doesn't always represent what permissions the app wants right now. For example, suppose an app sometimes wants location when "in use" and sometimes wants location "always" depending on what app features the user has engaged with. The Geolocation API will request the "always" location permission even if that's not what the app wants for the current scenario. This change enables developers to tell the Geolocation module to skip permission requests so that the app can explicitly ask for the appropriate permissions. By default, Geolocation will still ask for permissions so this is not a breaking change. This change adds a method to Geolocation that is not supported by the web spec for geolocation (`setRNConfiguration`). This method includes `RN` in the name to minimize the chances that the web spec will introduce an API with the same name. **Test plan (required)** Verified that Geolocation requests permissions by default and when `skipPermissionRequests` is `false`. Verified the permission requests are skipped when `skipPermissionRequests` is `true`. Also, my team is using this change in our app. Adam Comella Microsoft Corp. Closes https://github.com/facebook/react-native/pull/15096 Differential Revision: D5725563 Pulled By: javache fbshipit-source-id: fd23fedb7e57408fa0f0d0568e0f1ef2aea1cdd4
2017-08-29 11:00:41 +00:00
if (!_locationConfiguration.skipPermissionRequests) {
[self requestAuthorization];
}
re #17486 - set _locationManager if auth skipped Summary: This fixes #17486 make sure locationManager is being set before continuing. Used if !locationManager vs else on previous statement as we should NEVER enter this code without _locationManager set. Also the else version might experience issues if someone touches the auth code and doesn't check this case, so seems more "long term stable". <!-- Thank you for sending the PR! We appreciate you spending the time to work on these changes. Help us understand your motivation by explaining why you decided to make this change. You can learn more about contributing to React Native here: http://facebook.github.io/react-native/docs/contributing.html Happy contributing! --> This fixes #17486 1) Have a working geolocation demo 2) Add navigator.geolocation.setRNConfiguration({'skipPermissionRequests':true}); to your code. I added it to the constructor or componentWillMount for the app. 3) Observe that geolocation no longer works (times out) 4) Apply patch 5) Observe that geolocation works again re #15096 <!-- Help reviewers and the release process by writing your own release notes **INTERNAL and MINOR tagged notes will not be included in the next version's final release notes.** CATEGORY [----------] TYPE [ CLI ] [-------------] LOCATION [ DOCS ] [ BREAKING ] [-------------] [ GENERAL ] [ BUGFIX ] [-{Component}-] [ INTERNAL ] [ ENHANCEMENT ] [ {File} ] [ IOS ] [ FEATURE ] [ {Directory} ] |-----------| [ ANDROID ] [ MINOR ] [ {Framework} ] - | {Message} | [----------] [-------------] [-------------] |-----------| [CATEGORY] [TYPE] [LOCATION] - MESSAGE EXAMPLES: [IOS] [BREAKING] [FlatList] - Change a thing that breaks other things [ANDROID] [BUGFIX] [TextInput] - Did a thing to TextInput [CLI] [FEATURE] [local-cli/info/info.js] - CLI easier to do things with [DOCS] [BUGFIX] [GettingStarted.md] - Accidentally a thing/word [GENERAL] [ENHANCEMENT] [Yoga] - Added new yoga thing/position [INTERNAL] [FEATURE] [./scripts] - Added thing to script that nobody will see --> [IOS] [BUGFIX] [GeoLocation] - Fix skipPermissionRequests by setting _locationManager Closes https://github.com/facebook/react-native/pull/17487 Differential Revision: D6718389 Pulled By: hramos fbshipit-source-id: 08c1c9306b4d87cc40acdaa1550bb6df8345db02
2018-01-13 02:48:10 +00:00
if (!_locationManager) {
_locationManager = [CLLocationManager new];
_locationManager.delegate = self;
}
2015-03-09 10:04:44 +00:00
_locationManager.distanceFilter = distanceFilter;
_locationManager.desiredAccuracy = desiredAccuracy;
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
2017-07-24 18:15:27 +00:00
_usingSignificantChanges = useSignificantChanges;
2015-03-09 10:04:44 +00:00
// Start observing location
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
2017-07-24 18:15:27 +00:00
_usingSignificantChanges ?
[_locationManager startMonitoringSignificantLocationChanges] :
[_locationManager startUpdatingLocation];
2015-03-09 10:04:44 +00:00
}
#pragma mark - Timeout handler
- (void)timeout:(NSTimer *)timer
{
RCTLocationRequest *request = timer.userInfo;
NSString *message = [NSString stringWithFormat: @"Unable to fetch location within %.1fs.", request.options.timeout];
2015-03-09 10:04:44 +00:00
request.errorBlock(@[RCTPositionError(RCTPositionErrorTimeout, message)]);
[_pendingRequests removeObject:request];
// Stop updating if no pending requests
if (_pendingRequests.count == 0 && !_observingLocation) {
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
2017-07-24 18:15:27 +00:00
_usingSignificantChanges ?
[_locationManager stopMonitoringSignificantLocationChanges] :
[_locationManager stopUpdatingLocation];
2015-03-09 10:04:44 +00:00
}
}
#pragma mark - Public API
iOS: Geolocation: Allow skipping of permission prompts Summary: This change enables developers to tell the Geolocation module to skip its permissions requests so the app can manage the permissions requests on its own. React Native Android's Geolocation module already requires apps to request permissions on their own. Currently, the Geolocation module automatically makes permissions requests for you based on what you've specified in your property list file. However, the property list file doesn't always represent what permissions the app wants right now. For example, suppose an app sometimes wants location when "in use" and sometimes wants location "always" depending on what app features the user has engaged with. The Geolocation API will request the "always" location permission even if that's not what the app wants for the current scenario. This change enables developers to tell the Geolocation module to skip permission requests so that the app can explicitly ask for the appropriate permissions. By default, Geolocation will still ask for permissions so this is not a breaking change. This change adds a method to Geolocation that is not supported by the web spec for geolocation (`setRNConfiguration`). This method includes `RN` in the name to minimize the chances that the web spec will introduce an API with the same name. **Test plan (required)** Verified that Geolocation requests permissions by default and when `skipPermissionRequests` is `false`. Verified the permission requests are skipped when `skipPermissionRequests` is `true`. Also, my team is using this change in our app. Adam Comella Microsoft Corp. Closes https://github.com/facebook/react-native/pull/15096 Differential Revision: D5725563 Pulled By: javache fbshipit-source-id: fd23fedb7e57408fa0f0d0568e0f1ef2aea1cdd4
2017-08-29 11:00:41 +00:00
RCT_EXPORT_METHOD(setConfiguration:(RCTLocationConfiguration)config)
{
_locationConfiguration = config;
}
RCT_EXPORT_METHOD(requestAuthorization)
{
if (!_locationManager) {
_locationManager = [CLLocationManager new];
_locationManager.delegate = self;
}
// Request location access permission
if ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"] &&
[_locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) {
[_locationManager requestAlwaysAuthorization];
// On iOS 9+ we also need to enable background updates
NSArray *backgroundModes = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIBackgroundModes"];
if (backgroundModes && [backgroundModes containsObject:@"location"]) {
if ([_locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)]) {
[_locationManager setAllowsBackgroundLocationUpdates:YES];
}
}
} else if ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"] &&
[_locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
[_locationManager requestWhenInUseAuthorization];
}
}
RCT_EXPORT_METHOD(startObserving:(RCTLocationOptions)options)
2015-03-09 10:04:44 +00:00
{
checkLocationConfig();
// Select best options
_observerOptions = options;
for (RCTLocationRequest *request in _pendingRequests) {
_observerOptions.accuracy = MIN(_observerOptions.accuracy, request.options.accuracy);
}
2015-03-09 10:04:44 +00:00
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
2017-07-24 18:15:27 +00:00
[self beginLocationUpdatesWithDesiredAccuracy:_observerOptions.accuracy
distanceFilter:_observerOptions.distanceFilter
useSignificantChanges:_observerOptions.useSignificantChanges];
_observingLocation = YES;
2015-03-09 10:04:44 +00:00
}
2015-04-08 15:52:48 +00:00
RCT_EXPORT_METHOD(stopObserving)
2015-03-09 10:04:44 +00:00
{
// Stop observing
_observingLocation = NO;
2015-03-09 10:04:44 +00:00
// Stop updating if no pending requests
if (_pendingRequests.count == 0) {
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
2017-07-24 18:15:27 +00:00
_usingSignificantChanges ?
[_locationManager stopMonitoringSignificantLocationChanges] :
[_locationManager stopUpdatingLocation];
}
2015-03-09 10:04:44 +00:00
}
RCT_EXPORT_METHOD(getCurrentPosition:(RCTLocationOptions)options
2015-04-08 15:52:48 +00:00
withSuccessCallback:(RCTResponseSenderBlock)successBlock
errorCallback:(RCTResponseSenderBlock)errorBlock)
2015-03-09 10:04:44 +00:00
{
checkLocationConfig();
2015-03-09 10:04:44 +00:00
if (!successBlock) {
RCTLogError(@"%@.getCurrentPosition called with nil success parameter.", [self class]);
return;
}
if (![CLLocationManager locationServicesEnabled]) {
if (errorBlock) {
errorBlock(@[
RCTPositionError(RCTPositionErrorUnavailable, @"Location services disabled.")
]);
return;
2015-03-09 10:04:44 +00:00
}
}
2015-03-09 10:04:44 +00:00
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied) {
if (errorBlock) {
errorBlock(@[
RCTPositionError(RCTPositionErrorDenied, nil)
]);
return;
2015-03-09 10:04:44 +00:00
}
}
2015-03-09 10:04:44 +00:00
// 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) {
2015-03-09 10:04:44 +00:00
// Call success block with most recent known location
successBlock(@[_lastLocationEvent]);
return;
}
2015-03-09 10:04:44 +00:00
// Create request
RCTLocationRequest *request = [RCTLocationRequest new];
request.successBlock = successBlock;
request.errorBlock = errorBlock ?: ^(NSArray *args){};
request.options = options;
request.timeoutTimer = [NSTimer scheduledTimerWithTimeInterval:options.timeout
target:self
selector:@selector(timeout:)
userInfo:request
repeats:NO];
if (!_pendingRequests) {
_pendingRequests = [NSMutableArray new];
}
[_pendingRequests addObject:request];
// Configure location manager and begin updating location
CLLocationAccuracy accuracy = options.accuracy;
if (_locationManager) {
accuracy = MIN(_locationManager.desiredAccuracy, accuracy);
}
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
2017-07-24 18:15:27 +00:00
[self beginLocationUpdatesWithDesiredAccuracy:accuracy
distanceFilter:options.distanceFilter
useSignificantChanges:options.useSignificantChanges];
2015-03-09 10:04:44 +00:00
}
#pragma mark - CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray<CLLocation *> *)locations
2015-03-09 10:04:44 +00:00
{
// Create event
CLLocation *location = locations.lastObject;
2015-03-09 10:04:44 +00:00
_lastLocationEvent = @{
@"coords": @{
@"latitude": @(location.coordinate.latitude),
@"longitude": @(location.coordinate.longitude),
@"altitude": @(location.altitude),
@"accuracy": @(location.horizontalAccuracy),
@"altitudeAccuracy": @(location.verticalAccuracy),
@"heading": @(location.course),
@"speed": @(location.speed),
},
@"timestamp": @([location.timestamp timeIntervalSince1970] * 1000) // in ms
2015-03-09 10:04:44 +00:00
};
// Send event
if (_observingLocation) {
[self sendEventWithName:@"geolocationDidChange" body:_lastLocationEvent];
2015-03-09 10:04:44 +00:00
}
// Fire all queued callbacks
for (RCTLocationRequest *request in _pendingRequests) {
request.successBlock(@[_lastLocationEvent]);
[request.timeoutTimer invalidate];
2015-03-09 10:04:44 +00:00
}
[_pendingRequests removeAllObjects];
// Stop updating if not observing
2015-03-09 10:04:44 +00:00
if (!_observingLocation) {
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
2017-07-24 18:15:27 +00:00
_usingSignificantChanges ?
[_locationManager stopMonitoringSignificantLocationChanges] :
[_locationManager stopUpdatingLocation];
2015-03-09 10:04:44 +00:00
}
// Reset location accuracy if desiredAccuracy is changed.
// Otherwise update accuracy will force triggering didUpdateLocations, watchPosition would keeping receiving location updates, even there's no location changes.
if (ABS(_locationManager.desiredAccuracy - RCT_DEFAULT_LOCATION_ACCURACY) > 0.000001) {
_locationManager.desiredAccuracy = RCT_DEFAULT_LOCATION_ACCURACY;
}
2015-03-09 10:04:44 +00:00
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
// Check error type
NSDictionary<NSString *, id> *jsError = nil;
2015-03-09 10:04:44 +00:00
switch (error.code) {
case kCLErrorDenied:
jsError = RCTPositionError(RCTPositionErrorDenied, nil);
break;
case kCLErrorNetwork:
jsError = RCTPositionError(RCTPositionErrorUnavailable, @"Unable to retrieve location due to a network failure");
break;
case kCLErrorLocationUnknown:
default:
jsError = RCTPositionError(RCTPositionErrorUnavailable, nil);
break;
}
// Send event
if (_observingLocation) {
[self sendEventWithName:@"geolocationError" body:jsError];
2015-03-09 10:04:44 +00:00
}
// Fire all queued error callbacks
for (RCTLocationRequest *request in _pendingRequests) {
request.errorBlock(@[jsError]);
[request.timeoutTimer invalidate];
2015-03-09 10:04:44 +00:00
}
[_pendingRequests removeAllObjects];
// Reset location accuracy if desiredAccuracy is changed.
// Otherwise update accuracy will force triggering didUpdateLocations, watchPosition would keeping receiving location updates, even there's no location changes.
if (ABS(_locationManager.desiredAccuracy - RCT_DEFAULT_LOCATION_ACCURACY) > 0.000001) {
_locationManager.desiredAccuracy = RCT_DEFAULT_LOCATION_ACCURACY;
}
2015-03-09 10:04:44 +00:00
}
static void checkLocationConfig()
{
#if RCT_DEV
if (!([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"] ||
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
2017-07-24 18:15:27 +00:00
[[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
}
2015-03-09 10:04:44 +00:00
@end