From 6513b9da84d93ece9228e4fb2a9299997cffb5aa Mon Sep 17 00:00:00 2001 From: kumo01GitHub Date: Sun, 24 Nov 2024 17:51:32 +0900 Subject: [PATCH 1/2] refactor: fix Xcode warning --- src/ios/CDVLocation.m | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ios/CDVLocation.m b/src/ios/CDVLocation.m index 84bdf15b..fc3fd61a 100644 --- a/src/ios/CDVLocation.m +++ b/src/ios/CDVLocation.m @@ -82,7 +82,6 @@ - (BOOL)isAuthorized - (BOOL)isLocationServicesEnabled { - BOOL locationServicesEnabledInstancePropertyAvailable = [self.locationManager respondsToSelector:@selector(locationServicesEnabled)]; // iOS 3.x BOOL locationServicesEnabledClassPropertyAvailable = [CLLocationManager respondsToSelector:@selector(locationServicesEnabled)]; // iOS 4.x if (locationServicesEnabledClassPropertyAvailable) { // iOS 4.x @@ -213,7 +212,7 @@ - (void)getLocation:(CDVInvokedUrlCommand*)command } } - if (!__locationStarted || (__highAccuracyEnabled != enableHighAccuracy)) { + if (!self->__locationStarted || (self->__highAccuracyEnabled != enableHighAccuracy)) { // add the callbackId into the array so we can call back when get data @synchronized (self.locationData.locationCallbacks) { if (callbackId != nil) { From ef045076bd6b696a244ece6d1fbf115b436a3f16 Mon Sep 17 00:00:00 2001 From: kumo01GitHub Date: Sat, 4 Jan 2025 15:59:58 +0900 Subject: [PATCH 2/2] fix: #257 --- src/ios/CDVLocation.h | 2 +- src/ios/CDVLocation.m | 219 ++++++++++++++++++++++-------------------- 2 files changed, 117 insertions(+), 104 deletions(-) diff --git a/src/ios/CDVLocation.h b/src/ios/CDVLocation.h index cce2738f..77d31266 100644 --- a/src/ios/CDVLocation.h +++ b/src/ios/CDVLocation.h @@ -66,5 +66,5 @@ typedef NSUInteger CDVLocationStatus; - (void)locationManager:(CLLocationManager*)manager didFailWithError:(NSError*)error; -- (BOOL)isLocationServicesEnabled; +- (void)isLocationServicesEnabled:(void (^)(BOOL))comletionHandler; @end diff --git a/src/ios/CDVLocation.m b/src/ios/CDVLocation.m index fc3fd61a..1dbd6ac1 100644 --- a/src/ios/CDVLocation.m +++ b/src/ios/CDVLocation.m @@ -55,6 +55,8 @@ @implementation CDVLocation - (void)pluginInitialize { + // TODO: The CLLocationManager instance is only safe to use on the thread/dispatch queue it was created in. + // https://github.com/apache/cordova-plugin-geolocation/issues/257#issuecomment-1883740721 self.locationManager = [[CLLocationManager alloc] init]; self.locationManager.delegate = self; // Tells the location manager to send updates to this object __locationStarted = NO; @@ -80,86 +82,93 @@ - (BOOL)isAuthorized return YES; } -- (BOOL)isLocationServicesEnabled +- (void)isLocationServicesEnabled:(void (^)(BOOL))comletionHandler { - BOOL locationServicesEnabledClassPropertyAvailable = [CLLocationManager respondsToSelector:@selector(locationServicesEnabled)]; // iOS 4.x - - if (locationServicesEnabledClassPropertyAvailable) { // iOS 4.x - return [CLLocationManager locationServicesEnabled]; - } else { - return NO; - } + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + BOOL locationServicesEnabledClassPropertyAvailable = [CLLocationManager respondsToSelector:@selector(locationServicesEnabled)]; // iOS 4.x + + if (locationServicesEnabledClassPropertyAvailable) { // iOS 4.x + comletionHandler([CLLocationManager locationServicesEnabled]); + } else { + comletionHandler(NO); + } + }); } - (void)startLocation:(BOOL)enableHighAccuracy { - if (![self isLocationServicesEnabled]) { - [self returnLocationError:PERMISSIONDENIED withMessage:@"Location services are not enabled."]; - return; - } - if (![self isAuthorized]) { - NSString* message = nil; - BOOL authStatusAvailable = [CLLocationManager respondsToSelector:@selector(authorizationStatus)]; // iOS 4.2+ - if (authStatusAvailable) { - NSUInteger code = [CLLocationManager authorizationStatus]; - if (code == kCLAuthorizationStatusNotDetermined) { - // could return POSITION_UNAVAILABLE but need to coordinate with other platforms - message = @"User undecided on application's use of location services."; - } else if (code == kCLAuthorizationStatusRestricted) { - message = @"Application's use of location services is restricted."; + [self isLocationServicesEnabled:^(BOOL enabled) { + if (!enabled) { + [self returnLocationError:PERMISSIONDENIED withMessage:@"Location services are not enabled."]; + return; + } else { + if (![self isAuthorized]) { + NSString* message = nil; + BOOL authStatusAvailable = [CLLocationManager respondsToSelector:@selector(authorizationStatus)]; // iOS 4.2+ + if (authStatusAvailable) { + NSUInteger code = [CLLocationManager authorizationStatus]; + if (code == kCLAuthorizationStatusNotDetermined) { + // could return POSITION_UNAVAILABLE but need to coordinate with other platforms + message = @"User undecided on application's use of location services."; + } else if (code == kCLAuthorizationStatusRestricted) { + message = @"Application's use of location services is restricted."; + } + } + // PERMISSIONDENIED is only PositionError that makes sense when authorization denied + [self returnLocationError:PERMISSIONDENIED withMessage:message]; + + return; } - } - // PERMISSIONDENIED is only PositionError that makes sense when authorization denied - [self returnLocationError:PERMISSIONDENIED withMessage:message]; - - return; - } - + #ifdef __IPHONE_8_0 - NSUInteger code = [CLLocationManager authorizationStatus]; - if (code == kCLAuthorizationStatusNotDetermined && ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)] || [self.locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)])) { //iOS8+ - __highAccuracyEnabled = enableHighAccuracy; - if([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"]){ - [self.locationManager requestWhenInUseAuthorization]; - } else if([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"]) { - [self.locationManager requestAlwaysAuthorization]; - } else { - NSLog(@"[Warning] No NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription key is defined in the Info.plist file."); - } - return; - } + NSUInteger code = [CLLocationManager authorizationStatus]; + if (code == kCLAuthorizationStatusNotDetermined && ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)] || [self.locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)])) { //iOS8+ + self->__highAccuracyEnabled = enableHighAccuracy; + if([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"]){ + [self.locationManager requestWhenInUseAuthorization]; + } else if([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"]) { + [self.locationManager requestAlwaysAuthorization]; + } else { + NSLog(@"[Warning] No NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription key is defined in the Info.plist file."); + } + return; + } #endif - - // Tell the location manager to start notifying us of location updates. We - // first stop, and then start the updating to ensure we get at least one - // update, even if our location did not change. - [self.locationManager stopUpdatingLocation]; - [self.locationManager startUpdatingLocation]; - __locationStarted = YES; - if (enableHighAccuracy) { - __highAccuracyEnabled = YES; - // Set distance filter to 5 for a high accuracy. Setting it to "kCLDistanceFilterNone" could provide a - // higher accuracy, but it's also just spamming the callback with useless reports which drain the battery. - self.locationManager.distanceFilter = 5; - // Set desired accuracy to Best. - self.locationManager.desiredAccuracy = kCLLocationAccuracyBest; - } else { - __highAccuracyEnabled = NO; - self.locationManager.distanceFilter = 10; - self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers; - } + + // Tell the location manager to start notifying us of location updates. We + // first stop, and then start the updating to ensure we get at least one + // update, even if our location did not change. + [self.locationManager stopUpdatingLocation]; + [self.locationManager startUpdatingLocation]; + self->__locationStarted = YES; + if (enableHighAccuracy) { + self->__highAccuracyEnabled = YES; + // Set distance filter to 5 for a high accuracy. Setting it to "kCLDistanceFilterNone" could provide a + // higher accuracy, but it's also just spamming the callback with useless reports which drain the battery. + self.locationManager.distanceFilter = 5; + // Set desired accuracy to Best. + self.locationManager.desiredAccuracy = kCLLocationAccuracyBest; + } else { + self->__highAccuracyEnabled = NO; + self.locationManager.distanceFilter = 10; + self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers; + } + } + }]; } - (void)_stopLocation { if (__locationStarted) { - if (![self isLocationServicesEnabled]) { - return; - } - - [self.locationManager stopUpdatingLocation]; - __locationStarted = NO; - __highAccuracyEnabled = NO; + [self isLocationServicesEnabled:^(BOOL enabled) { + if (!enabled) { + return; + } + + [self.locationManager stopUpdatingLocation]; + self->__locationStarted = NO; + self->__highAccuracyEnabled = NO; + }]; } } @@ -195,36 +204,38 @@ - (void)getLocation:(CDVInvokedUrlCommand*)command NSString* callbackId = command.callbackId; BOOL enableHighAccuracy = [[command argumentAtIndex:0] boolValue]; - if ([self isLocationServicesEnabled] == NO) { - NSMutableDictionary* posError = [NSMutableDictionary dictionaryWithCapacity:2]; - [posError setObject:[NSNumber numberWithInt:PERMISSIONDENIED] forKey:@"code"]; - [posError setObject:@"Location services are disabled." forKey:@"message"]; - CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:posError]; - [self.commandDelegate sendPluginResult:result callbackId:callbackId]; - } else { - if (!self.locationData) { - self.locationData = [[CDVLocationData alloc] init]; - } - CDVLocationData* lData = self.locationData; - @synchronized (self.locationData.locationCallbacks) { - if (!lData.locationCallbacks) { - lData.locationCallbacks = [NSMutableArray arrayWithCapacity:1]; + [self isLocationServicesEnabled:^(BOOL enabled) { + if (!enabled) { + NSMutableDictionary* posError = [NSMutableDictionary dictionaryWithCapacity:2]; + [posError setObject:[NSNumber numberWithInt:PERMISSIONDENIED] forKey:@"code"]; + [posError setObject:@"Location services are disabled." forKey:@"message"]; + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:posError]; + [self.commandDelegate sendPluginResult:result callbackId:callbackId]; + } else { + if (!self.locationData) { + self.locationData = [[CDVLocationData alloc] init]; } - } - - if (!self->__locationStarted || (self->__highAccuracyEnabled != enableHighAccuracy)) { - // add the callbackId into the array so we can call back when get data + CDVLocationData* lData = self.locationData; @synchronized (self.locationData.locationCallbacks) { - if (callbackId != nil) { - [lData.locationCallbacks addObject:callbackId]; + if (!lData.locationCallbacks) { + lData.locationCallbacks = [NSMutableArray arrayWithCapacity:1]; } } - // Tell the location manager to start notifying us of heading updates - [self startLocation:enableHighAccuracy]; - } else { - [self returnLocationInfo:callbackId andKeepCallback:NO]; + + if (!self->__locationStarted || (self->__highAccuracyEnabled != enableHighAccuracy)) { + // add the callbackId into the array so we can call back when get data + @synchronized (self.locationData.locationCallbacks) { + if (callbackId != nil) { + [lData.locationCallbacks addObject:callbackId]; + } + } + // Tell the location manager to start notifying us of heading updates + [self startLocation:enableHighAccuracy]; + } else { + [self returnLocationInfo:callbackId andKeepCallback:NO]; + } } - } + }]; }]; } @@ -246,18 +257,20 @@ - (void)addWatch:(CDVInvokedUrlCommand*)command // add the callbackId into the dictionary so we can call back whenever get data [lData.watchCallbacks setObject:callbackId forKey:timerId]; - if ([self isLocationServicesEnabled] == NO) { - NSMutableDictionary* posError = [NSMutableDictionary dictionaryWithCapacity:2]; - [posError setObject:[NSNumber numberWithInt:PERMISSIONDENIED] forKey:@"code"]; - [posError setObject:@"Location services are disabled." forKey:@"message"]; - CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:posError]; - [self.commandDelegate sendPluginResult:result callbackId:callbackId]; - } else { - if (!__locationStarted || (__highAccuracyEnabled != enableHighAccuracy)) { - // Tell the location manager to start notifying us of location updates - [self startLocation:enableHighAccuracy]; + [self isLocationServicesEnabled:^(BOOL enabled) { + if (!enabled) { + NSMutableDictionary* posError = [NSMutableDictionary dictionaryWithCapacity:2]; + [posError setObject:[NSNumber numberWithInt:PERMISSIONDENIED] forKey:@"code"]; + [posError setObject:@"Location services are disabled." forKey:@"message"]; + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:posError]; + [self.commandDelegate sendPluginResult:result callbackId:callbackId]; + } else { + if (!self->__locationStarted || (self->__highAccuracyEnabled != enableHighAccuracy)) { + // Tell the location manager to start notifying us of location updates + [self startLocation:enableHighAccuracy]; + } } - } + }]; } - (void)clearWatch:(CDVInvokedUrlCommand*)command