Improved Geolocation API

This commit is contained in:
Nick Lockwood 2015-03-09 03:04:44 -07:00
parent c5e6f550ad
commit 705a8e0144
12 changed files with 399 additions and 232 deletions

View File

@ -1,7 +1,7 @@
/**
* Copyright 2004-present Facebook. All Rights Reserved.
*
* @providesModule GeoLocationExample
* @providesModule GeolocationExample
*/
/* eslint no-console: 0 */
'use strict';
@ -15,19 +15,19 @@ var {
} = React;
exports.framework = 'React';
exports.title = 'GeoLocation';
exports.description = 'Examples of using the GeoLocation API.';
exports.title = 'Geolocation';
exports.description = 'Examples of using the Geolocation API.';
exports.examples = [
{
title: 'navigator.geolocation',
render: function() {
return <GeoLocationExample />;
return <GeolocationExample />;
},
}
];
var GeoLocationExample = React.createClass({
var GeolocationExample = React.createClass({
getInitialState: function() {
return {
initialPosition: 'unknown',

View File

@ -11,6 +11,7 @@
134180011AA9153C003F314A /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13417FEF1AA914B8003F314A /* libRCTText.a */; };
134180021AA9153C003F314A /* libReactKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 13417FFF1AA91531003F314A /* libReactKit.a */; };
1341802C1AA9178B003F314A /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1341802B1AA91779003F314A /* libRCTNetwork.a */; };
134A8A2A1AACED7A00945AAE /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 134A8A251AACED6A00945AAE /* libRCTGeolocation.a */; };
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
@ -46,6 +47,13 @@
remoteGlobalIDString = 58B511DB1A9E6C8500147676;
remoteInfo = RCTNetwork;
};
134A8A241AACED6A00945AAE /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 134A8A201AACED6A00945AAE /* RCTGeolocation.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = RCTGeolocation;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
@ -53,6 +61,7 @@
13417FEA1AA914B8003F314A /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = ../../Libraries/Text/RCTText.xcodeproj; sourceTree = "<group>"; };
13417FFA1AA91531003F314A /* ReactKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ReactKit.xcodeproj; path = ../../ReactKit/ReactKit.xcodeproj; sourceTree = "<group>"; };
134180261AA91779003F314A /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = ../../Libraries/Network/RCTNetwork.xcodeproj; sourceTree = "<group>"; };
134A8A201AACED6A00945AAE /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = ../../Libraries/Geolocation/RCTGeolocation.xcodeproj; sourceTree = "<group>"; };
13B07F961A680F5B00A75B9A /* UIExplorer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = UIExplorer.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
@ -67,6 +76,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
134A8A2A1AACED7A00945AAE /* libRCTGeolocation.a in Frameworks */,
1341802C1AA9178B003F314A /* libRCTNetwork.a in Frameworks */,
134180011AA9153C003F314A /* libRCTText.a in Frameworks */,
134180021AA9153C003F314A /* libReactKit.a in Frameworks */,
@ -80,6 +90,7 @@
1316A21D1AA397F400C0188E /* Libraries */ = {
isa = PBXGroup;
children = (
134A8A201AACED6A00945AAE /* RCTGeolocation.xcodeproj */,
13417FFA1AA91531003F314A /* ReactKit.xcodeproj */,
134180261AA91779003F314A /* RCTNetwork.xcodeproj */,
13417FEA1AA914B8003F314A /* RCTText.xcodeproj */,
@ -120,6 +131,14 @@
name = Products;
sourceTree = "<group>";
};
134A8A211AACED6A00945AAE /* Products */ = {
isa = PBXGroup;
children = (
134A8A251AACED6A00945AAE /* libRCTGeolocation.a */,
);
name = Products;
sourceTree = "<group>";
};
13B07FAE1A68108700A75B9A /* UIExplorer */ = {
isa = PBXGroup;
children = (
@ -191,6 +210,10 @@
productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
projectDirPath = "";
projectReferences = (
{
ProductGroup = 134A8A211AACED6A00945AAE /* Products */;
ProjectRef = 134A8A201AACED6A00945AAE /* RCTGeolocation.xcodeproj */;
},
{
ProductGroup = 13417FE41AA91428003F314A /* Products */;
ProjectRef = 13417FE31AA91428003F314A /* RCTImage.xcodeproj */;
@ -244,6 +267,13 @@
remoteRef = 1341802A1AA91779003F314A /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
134A8A251AACED6A00945AAE /* libRCTGeolocation.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libRCTGeolocation.a;
remoteRef = 134A8A241AACED6A00945AAE /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */

View File

@ -32,7 +32,7 @@ var EXAMPLES = [
require('./ActivityIndicatorExample'),
require('./ScrollViewExample'),
require('./DatePickerExample'),
require('./GeoLocationExample'),
require('./GeolocationExample'),
require('./TabBarExample'),
];

View File

@ -1,12 +1,12 @@
/**
* Copyright 2004-present Facebook. All Rights Reserved.
*
* @providesModule GeoLocation
* @providesModule Geolocation
*/
'use strict';
var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
var RCTLocationObserver = require('NativeModules').RKLocationObserver;
var RCTLocationObserver = require('NativeModulesDeprecated').RKLocationObserver;
var invariant = require('invariant');
var logError = require('logError');
@ -16,13 +16,6 @@ var subscriptions = [];
var updatesEnabled = false;
var ensureObserving = function() {
if (!updatesEnabled) {
RCTLocationObserver.startObserving();
updatesEnabled = true;
}
};
/**
* /!\ ATTENTION /!\
* You need to add NSLocationWhenInUseUsageDescription key
@ -30,43 +23,51 @@ var ensureObserving = function() {
* to *fail silently*!
* \!/ \!/
*
* GeoLocation follows the MDN specification:
* Geolocation follows the MDN specification:
* https://developer.mozilla.org/en-US/docs/Web/API/Geolocation
*/
class GeoLocation {
static getCurrentPosition(geo_success, geo_error, geo_options) {
var Geolocation = {
getCurrentPosition: function(geo_success, geo_error, geo_options) {
invariant(
typeof geo_success === 'function',
'Must provide a valid geo_success callback.'
);
if (geo_options) {
warning('geo_options are not yet supported.');
}
ensureObserving();
RCTLocationObserver.getCurrentPosition(
geo_success,
geo_error || logError
geo_error || logError,
geo_options || {}
);
}
static watchPosition(callback) {
ensureObserving();
},
watchPosition: function(success, error, options) {
if (!updatesEnabled) {
RCTLocationObserver.startObserving(options || {});
updatesEnabled = true;
}
var watchID = subscriptions.length;
subscriptions.push(
subscriptions.push([
RCTDeviceEventEmitter.addListener(
'geoLocationDidChange',
callback
)
);
'geolocationDidChange',
success
),
error ? RCTDeviceEventEmitter.addListener(
'geolocationError',
error
) : null,
]);
return watchID;
}
static clearWatch(watchID) {
},
clearWatch: function(watchID) {
var sub = subscriptions[watchID];
if (!sub) {
// Silently exit when the watchID is invalid or already cleared
// This is consistent with timers
return;
}
sub.remove();
sub[0].remove();
sub[1] && sub[1].remove();
subscriptions[watchID] = undefined;
var noWatchers = true;
for (var ii = 0; ii < subscriptions.length; ii++) {
@ -75,10 +76,11 @@ class GeoLocation {
}
}
if (noWatchers) {
GeoLocation.stopObserving();
Geolocation.stopObserving();
}
}
static stopObserving() {
},
stopObserving: function() {
if (updatesEnabled) {
RCTLocationObserver.stopObserving();
updatesEnabled = false;
@ -89,10 +91,8 @@ class GeoLocation {
}
}
subscriptions = [];
} else {
warning('Tried to stop observing when not observing.');
}
}
}
module.exports = GeoLocation;
module.exports = Geolocation;

View File

@ -0,0 +1,319 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#import "RCTLocationObserver.h"
#import <CoreLocation/CLError.h>
#import <CoreLocation/CLLocationManager.h>
#import <CoreLocation/CLLocationManagerDelegate.h>
#import "RCTAssert.h"
#import "RCTBridge.h"
#import "RCTConvert.h"
#import "RCTEventDispatcher.h"
#import "RCTLog.h"
typedef NS_ENUM(NSInteger, RCTPositionErrorCode) {
RCTPositionErrorDenied = 1,
RCTPositionErrorUnavailable,
RCTPositionErrorTimeout,
};
#define RCT_DEFAULT_LOCATION_ACCURACY kCLLocationAccuracyHundredMeters
typedef struct {
NSTimeInterval timeout;
NSTimeInterval maximumAge;
CLLocationAccuracy accuracy;
} RCTLocationOptions;
static RCTLocationOptions RCTLocationOptionsWithJSON(id json)
{
NSDictionary *options = [RCTConvert NSDictionary:json];
return (RCTLocationOptions){
.timeout = [RCTConvert NSTimeInterval:options[@"timeout"]] ?: INFINITY,
.maximumAge = [RCTConvert NSTimeInterval:options[@"maximumAge"]] ?: INFINITY,
.accuracy = [RCTConvert BOOL:options[@"enableHighAccuracy"]] ? kCLLocationAccuracyBest : RCT_DEFAULT_LOCATION_ACCURACY
};
}
static NSDictionary *RCTPositionError(RCTPositionErrorCode code, NSString *msg /* nil for default */)
{
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
{
[_timeoutTimer invalidate];
}
@end
@interface RCTLocationObserver () <CLLocationManagerDelegate>
@end
@implementation RCTLocationObserver
{
CLLocationManager *_locationManager;
NSDictionary *_lastLocationEvent;
NSMutableArray *_pendingRequests;
BOOL _observingLocation;
RCTLocationOptions _observerOptions;
}
@synthesize bridge = _bridge;
#pragma mark - Lifecycle
- (instancetype)init
{
if ((self = [super init])) {
_locationManager = [[CLLocationManager alloc] init];
_locationManager.distanceFilter = RCT_DEFAULT_LOCATION_ACCURACY;
_locationManager.delegate = self;
_pendingRequests = [[NSMutableArray alloc] init];
}
return self;
}
- (void)dealloc
{
[_locationManager stopUpdatingLocation];
}
#pragma mark - Private API
- (void)beginLocationUpdates
{
// Request location access permission
if ([_locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
[_locationManager requestWhenInUseAuthorization];
}
// Start observing location
[_locationManager startUpdatingLocation];
}
#pragma mark - Timeout handler
- (void)timeout:(NSTimer *)timer
{
RCTLocationRequest *request = timer.userInfo;
NSString *message = [NSString stringWithFormat: @"Unable to fetch location within %zds.", (NSInteger)(timer.timeInterval * 1000.0)];
request.errorBlock(@[RCTPositionError(RCTPositionErrorTimeout, message)]);
[_pendingRequests removeObject:request];
// Stop updating if no pending requests
if (_pendingRequests.count == 0 && !_observingLocation) {
[_locationManager stopUpdatingLocation];
}
}
#pragma mark - Public API
- (void)startObserving:(NSDictionary *)optionsJSON
{
RCT_EXPORT();
dispatch_async(dispatch_get_main_queue(), ^{
// Select best options
_observerOptions = RCTLocationOptionsWithJSON(optionsJSON);
for (RCTLocationRequest *request in _pendingRequests) {
_observerOptions.accuracy = MIN(_observerOptions.accuracy, request.options.accuracy);
}
_locationManager.desiredAccuracy = _observerOptions.accuracy;
[self beginLocationUpdates];
_observingLocation = YES;
});
}
- (void)stopObserving
{
RCT_EXPORT();
dispatch_async(dispatch_get_main_queue(), ^{
// Stop observing
_observingLocation = NO;
// Stop updating if no pending requests
if (_pendingRequests.count == 0) {
[_locationManager stopUpdatingLocation];
}
});
}
- (void)getCurrentPosition:(RCTResponseSenderBlock)successBlock
withErrorCallback:(RCTResponseSenderBlock)errorBlock
options:(NSDictionary *)optionsJSON
{
RCT_EXPORT();
if (!successBlock) {
RCTLogError(@"%@.getCurrentPosition called with nil success parameter.", [self class]);
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
if (![CLLocationManager locationServicesEnabled]) {
if (errorBlock) {
errorBlock(@[
RCTPositionError(RCTPositionErrorUnavailable, @"Location services disabled.")
]);
return;
}
}
if (![CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied) {
if (errorBlock) {
errorBlock(@[
RCTPositionError(RCTPositionErrorDenied, nil)
]);
return;
}
}
// Get options
RCTLocationOptions options = RCTLocationOptionsWithJSON(optionsJSON);
// 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) {
// Call success block with most recent known location
successBlock(@[_lastLocationEvent]);
return;
}
// Create request
RCTLocationRequest *request = [[RCTLocationRequest alloc] init];
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];
[_pendingRequests addObject:request];
// Configure location manager and begin updating location
_locationManager.desiredAccuracy = MIN(_locationManager.desiredAccuracy, options.accuracy);
[self beginLocationUpdates];
});
}
#pragma mark - CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
// Create event
CLLocation *location = [locations lastObject];
_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": @(CFAbsoluteTimeGetCurrent() * 1000.0) // in ms
};
// Send event
if (_observingLocation) {
[_bridge.eventDispatcher sendDeviceEventWithName:@"geolocationDidChange"
body:_lastLocationEvent];
}
// Fire all queued callbacks
for (RCTLocationRequest *request in _pendingRequests) {
request.successBlock(@[_lastLocationEvent]);
}
[_pendingRequests removeAllObjects];
// Stop updating if not not observing
if (!_observingLocation) {
[_locationManager stopUpdatingLocation];
}
// Reset location accuracy
_locationManager.desiredAccuracy = RCT_DEFAULT_LOCATION_ACCURACY;
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
// Check error type
NSDictionary *jsError = nil;
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) {
[_bridge.eventDispatcher sendDeviceEventWithName:@"geolocationError"
body:jsError];
}
// Fire all queued error callbacks
for (RCTLocationRequest *request in _pendingRequests) {
request.errorBlock(@[jsError]);
}
[_pendingRequests removeAllObjects];
// Reset location accuracy
_locationManager.desiredAccuracy = RCT_DEFAULT_LOCATION_ACCURACY;
}
@end

View File

@ -140,7 +140,7 @@ function setupXHR() {
function setupGeolocation() {
GLOBAL.navigator = GLOBAL.navigator || {};
GLOBAL.navigator.geolocation = require('GeoLocation');
GLOBAL.navigator.geolocation = require('Geolocation');
}
setupDocumentShim();

View File

@ -19,6 +19,8 @@
+ (float)float:(id)json;
+ (int)int:(id)json;
+ (NSArray *)NSArray:(id)json;
+ (NSDictionary *)NSDictionary:(id)json;
+ (NSString *)NSString:(id)json;
+ (NSNumber *)NSNumber:(id)json;
+ (NSInteger)NSInteger:(id)json;

View File

@ -99,6 +99,8 @@ RCT_CONVERTER(double, double, doubleValue)
RCT_CONVERTER(float, float, floatValue)
RCT_CONVERTER(int, int, intValue)
RCT_CONVERTER_CUSTOM(NSArray *, NSArray, [NSArray arrayWithArray:json])
RCT_CONVERTER_CUSTOM(NSDictionary *, NSDictionary, [NSDictionary dictionaryWithDictionary:json])
RCT_CONVERTER(NSString *, NSString, description)
RCT_CONVERTER_CUSTOM(NSNumber *, NSNumber, @([json doubleValue]))
RCT_CONVERTER(NSInteger, NSInteger, integerValue)

View File

@ -1,182 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#import "RCTLocationObserver.h"
#import <CoreLocation/CLLocationManager.h>
#import <CoreLocation/CLLocationManagerDelegate.h>
#import "RCTAssert.h"
#import "RCTBridge.h"
#import "RCTEventDispatcher.h"
#import "RCTLog.h"
// TODO (#5906496): Shouldn't these be configurable?
const CLLocationAccuracy RCTLocationAccuracy = 500.0; // meters
@interface RCTPendingLocationRequest : NSObject
@property (nonatomic, copy) RCTResponseSenderBlock successBlock;
@property (nonatomic, copy) RCTResponseSenderBlock errorBlock;
@end
@implementation RCTPendingLocationRequest @end
@interface RCTLocationObserver () <CLLocationManagerDelegate>
@end
@implementation RCTLocationObserver
{
CLLocationManager *_locationManager;
NSDictionary *_lastLocationEvent;
NSMutableDictionary *_pendingRequests;
}
@synthesize bridge = _bridge;
#pragma mark - Lifecycle
- (instancetype)init
{
if ((self = [super init])) {
_pendingRequests = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)dealloc
{
[_locationManager stopUpdatingLocation];
}
#pragma mark - Public API
- (void)startObserving
{
RCT_EXPORT();
dispatch_async(dispatch_get_main_queue(), ^{
// Create the location manager if this object does not
// already have one, and it must be created and accessed
// on the main thread
if (nil == _locationManager) {
_locationManager = [[CLLocationManager alloc] init];
}
_locationManager.delegate = self;
_locationManager.desiredAccuracy = RCTLocationAccuracy;
// Set a movement threshold for new events.
_locationManager.distanceFilter = RCTLocationAccuracy; // meters
if([_locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
[_locationManager requestWhenInUseAuthorization];
}
[_locationManager startUpdatingLocation];
});
}
- (void)stopObserving
{
RCT_EXPORT();
dispatch_async(dispatch_get_main_queue(), ^{
[_locationManager stopUpdatingLocation];
_lastLocationEvent = nil;
});
}
#pragma mark - CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
CLLocation *loc = [locations lastObject];
NSDictionary *event = @{
@"coords": @{
@"latitude": @(loc.coordinate.latitude),
@"longitude": @(loc.coordinate.longitude),
@"altitude": @(loc.altitude),
@"accuracy": @(RCTLocationAccuracy),
@"altitudeAccuracy": @(RCTLocationAccuracy),
@"heading": @(loc.course),
@"speed": @(loc.speed),
},
@"timestamp": @(CACurrentMediaTime())
};
[_bridge.eventDispatcher sendDeviceEventWithName:@"geoLocationDidChange" body:event];
NSArray *pendingRequestsCopy;
// TODO (#5906496): is this locking neccessary? If so, use something better than @synchronize
@synchronized(self) {
pendingRequestsCopy = [_pendingRequests allValues];
[_pendingRequests removeAllObjects];
_lastLocationEvent = event;
}
for (RCTPendingLocationRequest *request in pendingRequestsCopy) {
if (request.successBlock) {
request.successBlock(@[event]);
}
}
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSArray *pendingRequestsCopy;
// TODO (#5906496): is this locking neccessary? If so, use something better than @synchronize
@synchronized(self) {
pendingRequestsCopy = [_pendingRequests allValues];
[_pendingRequests removeAllObjects];
}
NSString *errorMsg = @"User denied location service or location service not available.";
for (RCTPendingLocationRequest *request in pendingRequestsCopy) {
if (request.errorBlock) {
request.errorBlock(@[errorMsg]);
}
}
}
- (void)getCurrentPosition:(RCTResponseSenderBlock)geoSuccess withErrorCallback:(RCTResponseSenderBlock)geoError
{
RCT_EXPORT();
NSDictionary *lastLocationCopy;
// TODO (#5906496): is this locking neccessary? If so, use something better than @synchronize
@synchronized(self) {
if (![CLLocationManager locationServicesEnabled] || [CLLocationManager authorizationStatus] == kCLAuthorizationStatusDenied) {
if (geoError) {
NSString *errorMsg = @"User denied location service or location service not available.";
geoError(@[errorMsg]);
return;
}
}
// If a request for the current position comes in before the OS has informed us, we wait for the first
// OS event and then call our callbacks. This obviates the need for handling of the otherwise
// common failure case of requesting the geolocation until it succeeds, assuming we would have
// instead returned an error if it wasn't yet available.
if (!_lastLocationEvent) {
NSInteger requestID = [_pendingRequests count];
RCTPendingLocationRequest *request = [[RCTPendingLocationRequest alloc] init];
request.successBlock = geoSuccess;
request.errorBlock = geoError;
_pendingRequests[@(requestID)] = request;
return;
} else {
lastLocationCopy = [_lastLocationEvent copy];
}
}
if (geoSuccess) {
geoSuccess(@[lastLocationCopy]);
}
}
@end

View File

@ -74,8 +74,9 @@ UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimationType t
_property = [RCTConvert NSString:config[@"property"]];
// TODO: this should be provided in ms, not seconds
_duration = [RCTConvert NSTimeInterval:config[@"duration"]] ?: duration;
_delay = [RCTConvert NSTimeInterval:config[@"delay"]];
// (this will require changing all call sites to ms as well)
_duration = [RCTConvert NSTimeInterval:config[@"duration"]] * 1000.0 ?: duration;
_delay = [RCTConvert NSTimeInterval:config[@"delay"]] * 1000.0;
_animationType = [RCTConvert RCTAnimationType:config[@"type"]];
if (_animationType == RCTAnimationTypeSpring) {
_springDamping = [RCTConvert CGFloat:config[@"springDamping"]];
@ -135,7 +136,8 @@ UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimationType t
if ((self = [super init])) {
// TODO: this should be provided in ms, not seconds
NSTimeInterval duration = [RCTConvert NSTimeInterval:config[@"duration"]];
// (this will require changing all call sites to ms as well)
NSTimeInterval duration = [RCTConvert NSTimeInterval:config[@"duration"]] * 1000.0;
_createAnimation = [[RCTAnimation alloc] initWithDuration:duration dictionary:config[@"create"]];
_updateAnimation = [[RCTAnimation alloc] initWithDuration:duration dictionary:config[@"update"]];

View File

@ -35,7 +35,6 @@
13E067571A70F44B002CDEE1 /* RCTView.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E067501A70F44B002CDEE1 /* RCTView.m */; };
13E067591A70F44B002CDEE1 /* UIView+ReactKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E067541A70F44B002CDEE1 /* UIView+ReactKit.m */; };
58C571C11AA56C1900CDF9C8 /* RCTDatePickerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 58C571BF1AA56C1900CDF9C8 /* RCTDatePickerManager.m */; };
5F5F0D991A9E456B001279FA /* RCTLocationObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F5F0D981A9E456B001279FA /* RCTLocationObserver.m */; };
830A229E1A66C68A008503DA /* RCTRootView.m in Sources */ = {isa = PBXBuildFile; fileRef = 830A229D1A66C68A008503DA /* RCTRootView.m */; };
830BA4551A8E3BDA00D53203 /* RCTCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 830BA4541A8E3BDA00D53203 /* RCTCache.m */; };
832348161A77A5AA00B55238 /* Layout.c in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FC71A68125100A75B9A /* Layout.c */; };
@ -126,8 +125,6 @@
13EFFCCF1A98E6FE002607DC /* RCTJSMethodRegistrar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTJSMethodRegistrar.h; sourceTree = "<group>"; };
58C571BF1AA56C1900CDF9C8 /* RCTDatePickerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDatePickerManager.m; sourceTree = "<group>"; };
58C571C01AA56C1900CDF9C8 /* RCTDatePickerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDatePickerManager.h; sourceTree = "<group>"; };
5F5F0D971A9E456B001279FA /* RCTLocationObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTLocationObserver.h; sourceTree = "<group>"; };
5F5F0D981A9E456B001279FA /* RCTLocationObserver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTLocationObserver.m; sourceTree = "<group>"; };
830213F31A654E0800B993E6 /* RCTBridgeModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTBridgeModule.h; sourceTree = "<group>"; };
830A229C1A66C68A008503DA /* RCTRootView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTRootView.h; sourceTree = "<group>"; };
830A229D1A66C68A008503DA /* RCTRootView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRootView.m; sourceTree = "<group>"; };
@ -190,8 +187,6 @@
13B07FE01A69315300A75B9A /* Modules */ = {
isa = PBXGroup;
children = (
5F5F0D971A9E456B001279FA /* RCTLocationObserver.h */,
5F5F0D981A9E456B001279FA /* RCTLocationObserver.m */,
13B07FE71A69327A00A75B9A /* RCTAlertManager.h */,
13B07FE81A69327A00A75B9A /* RCTAlertManager.m */,
13B07FE91A69327A00A75B9A /* RCTExceptionsManager.h */,
@ -398,7 +393,6 @@
13B0801E1A69489C00A75B9A /* RCTTextField.m in Sources */,
13B07FEF1A69327A00A75B9A /* RCTAlertManager.m in Sources */,
83CBBACC1A6023D300E9B192 /* RCTConvert.m in Sources */,
5F5F0D991A9E456B001279FA /* RCTLocationObserver.m in Sources */,
830A229E1A66C68A008503DA /* RCTRootView.m in Sources */,
13B07FF01A69327A00A75B9A /* RCTExceptionsManager.m in Sources */,
83CBBA5A1A601E9000E9B192 /* RCTRedBox.m in Sources */,