mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-04-28 20:25:33 +08:00
[ReactNative] Migrate navigator.geolocation to open source
This commit is contained in:
7
ReactKit/Modules/RCTLocationObserver.h
Normal file
7
ReactKit/Modules/RCTLocationObserver.h
Normal file
@@ -0,0 +1,7 @@
|
||||
// Copyright 2004-present Facebook. All Rights Reserved.
|
||||
|
||||
#import "RCTBridgeModule.h"
|
||||
|
||||
@interface RCTLocationObserver : NSObject<RCTBridgeModule>
|
||||
|
||||
@end
|
||||
182
ReactKit/Modules/RCTLocationObserver.m
Normal file
182
ReactKit/Modules/RCTLocationObserver.m
Normal file
@@ -0,0 +1,182 @@
|
||||
// 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;
|
||||
RCTEventDispatcher *_eventDispatcher;
|
||||
NSDictionary *_lastLocationEvent;
|
||||
NSMutableDictionary *_pendingRequests;
|
||||
}
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
{
|
||||
if (self = [super init]) {
|
||||
_eventDispatcher = bridge.eventDispatcher;
|
||||
_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())
|
||||
};
|
||||
[_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
|
||||
@@ -32,6 +32,7 @@
|
||||
13E067561A70F44B002CDEE1 /* RCTViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E0674E1A70F44B002CDEE1 /* RCTViewManager.m */; };
|
||||
13E067571A70F44B002CDEE1 /* RCTView.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E067501A70F44B002CDEE1 /* RCTView.m */; };
|
||||
13E067591A70F44B002CDEE1 /* UIView+ReactKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 13E067541A70F44B002CDEE1 /* UIView+ReactKit.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 */; };
|
||||
@@ -113,6 +114,8 @@
|
||||
13E067541A70F44B002CDEE1 /* UIView+ReactKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+ReactKit.m"; sourceTree = "<group>"; };
|
||||
13ED13891A80C9D40050A8F9 /* RCTPointerEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPointerEvents.h; sourceTree = "<group>"; };
|
||||
13EFFCCF1A98E6FE002607DC /* RCTJSMethodRegistrar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTJSMethodRegistrar.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>"; };
|
||||
@@ -177,6 +180,8 @@
|
||||
13B07FE01A69315300A75B9A /* Modules */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5F5F0D971A9E456B001279FA /* RCTLocationObserver.h */,
|
||||
5F5F0D981A9E456B001279FA /* RCTLocationObserver.m */,
|
||||
13B07FE71A69327A00A75B9A /* RCTAlertManager.h */,
|
||||
13B07FE81A69327A00A75B9A /* RCTAlertManager.m */,
|
||||
13B07FE91A69327A00A75B9A /* RCTExceptionsManager.h */,
|
||||
@@ -376,6 +381,7 @@
|
||||
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 */,
|
||||
1302F0FD1A78550100EBEF02 /* RCTStaticImage.m in Sources */,
|
||||
13B07FF01A69327A00A75B9A /* RCTExceptionsManager.m in Sources */,
|
||||
|
||||
Reference in New Issue
Block a user