Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Geolocation] Add permissionTypeIOS option #1506

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Libraries/Geolocation/Geolocation.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ var invariant = require('invariant');
var logError = require('logError');
var warning = require('warning');

var PERMISSION_TYPES = ['always', 'inUse'];

var subscriptions = [];

var updatesEnabled = false;
Expand All @@ -26,6 +28,7 @@ type GeoOptions = {
timeout: number;
maximumAge: number;
enableHighAccuracy: bool;
permissionTypeIOS: string;
}

/**
Expand Down Expand Up @@ -55,6 +58,10 @@ var Geolocation = {
geo_error?: Function,
geo_options?: GeoOptions
) {
invariant(
!geo_options || !geo_options.permissionTypeIOS || PERMISSION_TYPES.indexOf(geo_options.permissionTypeIOS) > -1,
'Invalid permissionTypeIOS option value.'
);
invariant(
typeof geo_success === 'function',
'Must provide a valid geo_success callback.'
Expand All @@ -71,6 +78,11 @@ var Geolocation = {
* options: timeout (ms), maximumAge (ms), enableHighAccuracy (bool)
*/
watchPosition: function(success: Function, error?: Function, options?: GeoOptions): number {
invariant(
!options || !options.permissionTypeIOS || PERMISSION_TYPES.indexOf(options.permissionTypeIOS) > -1,
'Invalid permissionTypeIOS option value.'
);

if (!updatesEnabled) {
RCTLocationObserver.startObserving(options || {});
updatesEnabled = true;
Expand Down
49 changes: 29 additions & 20 deletions Libraries/Geolocation/RCTLocationObserver.m
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ typedef NS_ENUM(NSInteger, RCTPositionErrorCode) {
RCTPositionErrorTimeout,
};

static NSString *const RCTPermissionTypeAlways = @"always";
static NSString *const RCTPermissionTypeInUse = @"inUse";
static NSString *const RCTPermissionKey = @"permissionTypeIOS";

#define RCT_DEFAULT_LOCATION_ACCURACY kCLLocationAccuracyHundredMeters

typedef struct {
Expand Down Expand Up @@ -124,20 +128,20 @@ - (dispatch_queue_t)methodQueue

#pragma mark - Private API

- (void)beginLocationUpdates
- (void)beginLocationUpdates:(NSString*)permissionType
{
if (!_locationManager) {
_locationManager = [CLLocationManager new];
_locationManager.distanceFilter = RCT_DEFAULT_LOCATION_ACCURACY;
_locationManager.delegate = self;
}

// Request location access permission
if ([_locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
if ([permissionType isEqualToString:RCTPermissionTypeAlways] && [_locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) {
[_locationManager requestAlwaysAuthorization];
}
// Defaults to minimal permission: `requestWhenInUseAuthorization`
else if ([_locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
[_locationManager requestWhenInUseAuthorization];
}

// Start observing location
[_locationManager startUpdatingLocation];
}

Expand All @@ -158,18 +162,18 @@ - (void)timeout:(NSTimer *)timer

#pragma mark - Public API

RCT_EXPORT_METHOD(startObserving:(RCTLocationOptions)options)
RCT_EXPORT_METHOD(startObserving:(NSDictionary*)options)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just add to RCTLocationOptions?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RCTLocationOptions is a C Struct, which to my knowledge cannot hold NSString values. I thought about assigning integer values to the permission types, or making RCTLocationOptions a class but this seemed like unnecessary complexity & change.

{
[self checkLocationConfig];
[self checkLocationConfig:options];
_observerOptions = [RCTConvert RCTLocationOptions:options];

// Select best options
_observerOptions = options;
for (RCTLocationRequest *request in _pendingRequests) {
_observerOptions.accuracy = MIN(_observerOptions.accuracy, request.options.accuracy);
}

_locationManager.desiredAccuracy = _observerOptions.accuracy;
[self beginLocationUpdates];
[self beginLocationUpdates:options[@"permissionTypeIOS"]];
_observingLocation = YES;
}

Expand All @@ -184,11 +188,12 @@ - (void)timeout:(NSTimer *)timer
}
}

RCT_EXPORT_METHOD(getCurrentPosition:(RCTLocationOptions)options
RCT_EXPORT_METHOD(getCurrentPosition:(NSDictionary*)options
withSuccessCallback:(RCTResponseSenderBlock)successBlock
errorCallback:(RCTResponseSenderBlock)errorBlock)
{
[self checkLocationConfig];
[self checkLocationConfig:options];
RCTLocationOptions locationOptions = [RCTConvert RCTLocationOptions:options];

if (!successBlock) {
RCTLogError(@"%@.getCurrentPosition called with nil success parameter.", [self class]);
Expand All @@ -215,8 +220,8 @@ - (void)timeout:(NSTimer *)timer

// Check if previous recorded location exists and is good enough
if (_lastLocationEvent &&
CFAbsoluteTimeGetCurrent() - [RCTConvert NSTimeInterval:_lastLocationEvent[@"timestamp"]] < options.maximumAge &&
[_lastLocationEvent[@"coords"][@"accuracy"] doubleValue] >= options.accuracy) {
CFAbsoluteTimeGetCurrent() - [RCTConvert NSTimeInterval:_lastLocationEvent[@"timestamp"]] < locationOptions.maximumAge &&
[_lastLocationEvent[@"coords"][@"accuracy"] doubleValue] >= locationOptions.accuracy) {

// Call success block with most recent known location
successBlock(@[_lastLocationEvent]);
Expand All @@ -227,8 +232,8 @@ - (void)timeout:(NSTimer *)timer
RCTLocationRequest *request = [RCTLocationRequest new];
request.successBlock = successBlock;
request.errorBlock = errorBlock ?: ^(NSArray *args){};
request.options = options;
request.timeoutTimer = [NSTimer scheduledTimerWithTimeInterval:options.timeout
request.options = locationOptions;
request.timeoutTimer = [NSTimer scheduledTimerWithTimeInterval:locationOptions.timeout
target:self
selector:@selector(timeout:)
userInfo:request
Expand All @@ -239,8 +244,8 @@ - (void)timeout:(NSTimer *)timer
[_pendingRequests addObject:request];

// Configure location manager and begin updating location
_locationManager.desiredAccuracy = MIN(_locationManager.desiredAccuracy, options.accuracy);
[self beginLocationUpdates];
_locationManager.desiredAccuracy = MIN(_locationManager.desiredAccuracy, locationOptions.accuracy);
[self beginLocationUpdates:options[RCTPermissionKey]];
}

#pragma mark - CLLocationManagerDelegate
Expand Down Expand Up @@ -325,9 +330,13 @@ - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *
}
}

- (void)checkLocationConfig
- (void)checkLocationConfig:(NSDictionary*)options
{
if (![[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"]) {
if([options[RCTPermissionKey] isEqualToString:RCTPermissionTypeAlways] && ![[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"]) {
//@TODO we should also check that the location background capability exists, and we should cache the results
RCTLogError(@"NSLocationAlwaysUsageDescription key must be present in Info.plist for 'always' authorization.");
}
else if (![[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"]) {
RCTLogError(@"NSLocationWhenInUseUsageDescription key must be present in Info.plist to use geolocation.");
}
}
Expand Down