@@ -55,6 +55,8 @@ @implementation CDVLocation
5555
5656- (void )pluginInitialize
5757{
58+ // TODO: The CLLocationManager instance is only safe to use on the thread/dispatch queue it was created in.
59+ // https://github.com/apache/cordova-plugin-geolocation/issues/257#issuecomment-1883740721
5860 self.locationManager = [[CLLocationManager alloc ] init ];
5961 self.locationManager .delegate = self; // Tells the location manager to send updates to this object
6062 __locationStarted = NO ;
@@ -80,86 +82,93 @@ - (BOOL)isAuthorized
8082 return YES ;
8183}
8284
83- - (BOOL )isLocationServicesEnabled
85+ - (void )isLocationServicesEnabled : ( void (^)( BOOL )) comletionHandler
8486{
85- BOOL locationServicesEnabledClassPropertyAvailable = [CLLocationManager respondsToSelector: @selector (locationServicesEnabled )]; // iOS 4.x
86-
87- if (locationServicesEnabledClassPropertyAvailable) { // iOS 4.x
88- return [CLLocationManager locationServicesEnabled ];
89- } else {
90- return NO ;
91- }
87+ dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
88+ BOOL locationServicesEnabledClassPropertyAvailable = [CLLocationManager respondsToSelector: @selector (locationServicesEnabled )]; // iOS 4.x
89+
90+ if (locationServicesEnabledClassPropertyAvailable) { // iOS 4.x
91+ comletionHandler ([CLLocationManager locationServicesEnabled ]);
92+ } else {
93+ comletionHandler (NO );
94+ }
95+ });
9296}
9397
9498- (void )startLocation : (BOOL )enableHighAccuracy
9599{
96- if (![self isLocationServicesEnabled ]) {
97- [self returnLocationError: PERMISSIONDENIED withMessage: @" Location services are not enabled." ];
98- return ;
99- }
100- if (![self isAuthorized ]) {
101- NSString * message = nil ;
102- BOOL authStatusAvailable = [CLLocationManager respondsToSelector: @selector (authorizationStatus )]; // iOS 4.2+
103- if (authStatusAvailable) {
104- NSUInteger code = [CLLocationManager authorizationStatus ];
105- if (code == kCLAuthorizationStatusNotDetermined ) {
106- // could return POSITION_UNAVAILABLE but need to coordinate with other platforms
107- message = @" User undecided on application's use of location services." ;
108- } else if (code == kCLAuthorizationStatusRestricted ) {
109- message = @" Application's use of location services is restricted." ;
100+ [self isLocationServicesEnabled: ^(BOOL enabled) {
101+ if (!enabled) {
102+ [self returnLocationError: PERMISSIONDENIED withMessage: @" Location services are not enabled." ];
103+ return ;
104+ } else {
105+ if (![self isAuthorized ]) {
106+ NSString * message = nil ;
107+ BOOL authStatusAvailable = [CLLocationManager respondsToSelector: @selector (authorizationStatus )]; // iOS 4.2+
108+ if (authStatusAvailable) {
109+ NSUInteger code = [CLLocationManager authorizationStatus ];
110+ if (code == kCLAuthorizationStatusNotDetermined ) {
111+ // could return POSITION_UNAVAILABLE but need to coordinate with other platforms
112+ message = @" User undecided on application's use of location services." ;
113+ } else if (code == kCLAuthorizationStatusRestricted ) {
114+ message = @" Application's use of location services is restricted." ;
115+ }
116+ }
117+ // PERMISSIONDENIED is only PositionError that makes sense when authorization denied
118+ [self returnLocationError: PERMISSIONDENIED withMessage: message];
119+
120+ return ;
110121 }
111- }
112- // PERMISSIONDENIED is only PositionError that makes sense when authorization denied
113- [self returnLocationError: PERMISSIONDENIED withMessage: message];
114-
115- return ;
116- }
117-
122+
118123#ifdef __IPHONE_8_0
119- NSUInteger code = [CLLocationManager authorizationStatus ];
120- if (code == kCLAuthorizationStatusNotDetermined && ([self .locationManager respondsToSelector: @selector (requestAlwaysAuthorization )] || [self .locationManager respondsToSelector: @selector (requestWhenInUseAuthorization )])) { // iOS8+
121- __highAccuracyEnabled = enableHighAccuracy;
122- if ([[NSBundle mainBundle ] objectForInfoDictionaryKey: @" NSLocationWhenInUseUsageDescription" ]){
123- [self .locationManager requestWhenInUseAuthorization ];
124- } else if ([[NSBundle mainBundle ] objectForInfoDictionaryKey: @" NSLocationAlwaysUsageDescription" ]) {
125- [self .locationManager requestAlwaysAuthorization ];
126- } else {
127- NSLog (@" [Warning] No NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription key is defined in the Info.plist file." );
128- }
129- return ;
130- }
124+ NSUInteger code = [CLLocationManager authorizationStatus ];
125+ if (code == kCLAuthorizationStatusNotDetermined && ([self .locationManager respondsToSelector: @selector (requestAlwaysAuthorization )] || [self .locationManager respondsToSelector: @selector (requestWhenInUseAuthorization )])) { // iOS8+
126+ self-> __highAccuracyEnabled = enableHighAccuracy;
127+ if ([[NSBundle mainBundle ] objectForInfoDictionaryKey: @" NSLocationWhenInUseUsageDescription" ]){
128+ [self .locationManager requestWhenInUseAuthorization ];
129+ } else if ([[NSBundle mainBundle ] objectForInfoDictionaryKey: @" NSLocationAlwaysUsageDescription" ]) {
130+ [self .locationManager requestAlwaysAuthorization ];
131+ } else {
132+ NSLog (@" [Warning] No NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription key is defined in the Info.plist file." );
133+ }
134+ return ;
135+ }
131136#endif
132-
133- // Tell the location manager to start notifying us of location updates. We
134- // first stop, and then start the updating to ensure we get at least one
135- // update, even if our location did not change.
136- [self .locationManager stopUpdatingLocation ];
137- [self .locationManager startUpdatingLocation ];
138- __locationStarted = YES ;
139- if (enableHighAccuracy) {
140- __highAccuracyEnabled = YES ;
141- // Set distance filter to 5 for a high accuracy. Setting it to "kCLDistanceFilterNone" could provide a
142- // higher accuracy, but it's also just spamming the callback with useless reports which drain the battery.
143- self.locationManager .distanceFilter = 5 ;
144- // Set desired accuracy to Best.
145- self.locationManager .desiredAccuracy = kCLLocationAccuracyBest ;
146- } else {
147- __highAccuracyEnabled = NO ;
148- self.locationManager .distanceFilter = 10 ;
149- self.locationManager .desiredAccuracy = kCLLocationAccuracyThreeKilometers ;
150- }
137+
138+ // Tell the location manager to start notifying us of location updates. We
139+ // first stop, and then start the updating to ensure we get at least one
140+ // update, even if our location did not change.
141+ [self .locationManager stopUpdatingLocation ];
142+ [self .locationManager startUpdatingLocation ];
143+ self->__locationStarted = YES ;
144+ if (enableHighAccuracy) {
145+ self->__highAccuracyEnabled = YES ;
146+ // Set distance filter to 5 for a high accuracy. Setting it to "kCLDistanceFilterNone" could provide a
147+ // higher accuracy, but it's also just spamming the callback with useless reports which drain the battery.
148+ self.locationManager .distanceFilter = 5 ;
149+ // Set desired accuracy to Best.
150+ self.locationManager .desiredAccuracy = kCLLocationAccuracyBest ;
151+ } else {
152+ self->__highAccuracyEnabled = NO ;
153+ self.locationManager .distanceFilter = 10 ;
154+ self.locationManager .desiredAccuracy = kCLLocationAccuracyThreeKilometers ;
155+ }
156+ }
157+ }];
151158}
152159
153160- (void )_stopLocation
154161{
155162 if (__locationStarted) {
156- if (![self isLocationServicesEnabled ]) {
157- return ;
158- }
159-
160- [self .locationManager stopUpdatingLocation ];
161- __locationStarted = NO ;
162- __highAccuracyEnabled = NO ;
163+ [self isLocationServicesEnabled: ^(BOOL enabled) {
164+ if (!enabled) {
165+ return ;
166+ }
167+
168+ [self .locationManager stopUpdatingLocation ];
169+ self->__locationStarted = NO ;
170+ self->__highAccuracyEnabled = NO ;
171+ }];
163172 }
164173}
165174
@@ -195,36 +204,38 @@ - (void)getLocation:(CDVInvokedUrlCommand*)command
195204 NSString * callbackId = command.callbackId ;
196205 BOOL enableHighAccuracy = [[command argumentAtIndex: 0 ] boolValue ];
197206
198- if ([self isLocationServicesEnabled ] == NO ) {
199- NSMutableDictionary * posError = [NSMutableDictionary dictionaryWithCapacity: 2 ];
200- [posError setObject: [NSNumber numberWithInt: PERMISSIONDENIED] forKey: @" code" ];
201- [posError setObject: @" Location services are disabled." forKey: @" message" ];
202- CDVPluginResult* result = [CDVPluginResult resultWithStatus: CDVCommandStatus_ERROR messageAsDictionary: posError];
203- [self .commandDelegate sendPluginResult: result callbackId: callbackId];
204- } else {
205- if (!self.locationData ) {
206- self.locationData = [[CDVLocationData alloc ] init ];
207- }
208- CDVLocationData* lData = self.locationData ;
209- @synchronized (self.locationData .locationCallbacks ) {
210- if (!lData.locationCallbacks ) {
211- lData.locationCallbacks = [NSMutableArray arrayWithCapacity: 1 ];
207+ [self isLocationServicesEnabled: ^(BOOL enabled) {
208+ if (!enabled) {
209+ NSMutableDictionary * posError = [NSMutableDictionary dictionaryWithCapacity: 2 ];
210+ [posError setObject: [NSNumber numberWithInt: PERMISSIONDENIED] forKey: @" code" ];
211+ [posError setObject: @" Location services are disabled." forKey: @" message" ];
212+ CDVPluginResult* result = [CDVPluginResult resultWithStatus: CDVCommandStatus_ERROR messageAsDictionary: posError];
213+ [self .commandDelegate sendPluginResult: result callbackId: callbackId];
214+ } else {
215+ if (!self.locationData ) {
216+ self.locationData = [[CDVLocationData alloc ] init ];
212217 }
213- }
214-
215- if (!self->__locationStarted || (self->__highAccuracyEnabled != enableHighAccuracy)) {
216- // add the callbackId into the array so we can call back when get data
218+ CDVLocationData* lData = self.locationData ;
217219 @synchronized (self.locationData .locationCallbacks ) {
218- if (callbackId != nil ) {
219- [ lData.locationCallbacks addObject: callbackId ];
220+ if (!lData. locationCallbacks ) {
221+ lData.locationCallbacks = [ NSMutableArray arrayWithCapacity: 1 ];
220222 }
221223 }
222- // Tell the location manager to start notifying us of heading updates
223- [self startLocation: enableHighAccuracy];
224- } else {
225- [self returnLocationInfo: callbackId andKeepCallback: NO ];
224+
225+ if (!self->__locationStarted || (self->__highAccuracyEnabled != enableHighAccuracy)) {
226+ // add the callbackId into the array so we can call back when get data
227+ @synchronized (self.locationData .locationCallbacks ) {
228+ if (callbackId != nil ) {
229+ [lData.locationCallbacks addObject: callbackId];
230+ }
231+ }
232+ // Tell the location manager to start notifying us of heading updates
233+ [self startLocation: enableHighAccuracy];
234+ } else {
235+ [self returnLocationInfo: callbackId andKeepCallback: NO ];
236+ }
226237 }
227- }
238+ }];
228239 }];
229240}
230241
@@ -246,18 +257,20 @@ - (void)addWatch:(CDVInvokedUrlCommand*)command
246257 // add the callbackId into the dictionary so we can call back whenever get data
247258 [lData.watchCallbacks setObject: callbackId forKey: timerId];
248259
249- if ([self isLocationServicesEnabled ] == NO ) {
250- NSMutableDictionary * posError = [NSMutableDictionary dictionaryWithCapacity: 2 ];
251- [posError setObject: [NSNumber numberWithInt: PERMISSIONDENIED] forKey: @" code" ];
252- [posError setObject: @" Location services are disabled." forKey: @" message" ];
253- CDVPluginResult* result = [CDVPluginResult resultWithStatus: CDVCommandStatus_ERROR messageAsDictionary: posError];
254- [self .commandDelegate sendPluginResult: result callbackId: callbackId];
255- } else {
256- if (!__locationStarted || (__highAccuracyEnabled != enableHighAccuracy)) {
257- // Tell the location manager to start notifying us of location updates
258- [self startLocation: enableHighAccuracy];
260+ [self isLocationServicesEnabled: ^(BOOL enabled) {
261+ if (!enabled) {
262+ NSMutableDictionary * posError = [NSMutableDictionary dictionaryWithCapacity: 2 ];
263+ [posError setObject: [NSNumber numberWithInt: PERMISSIONDENIED] forKey: @" code" ];
264+ [posError setObject: @" Location services are disabled." forKey: @" message" ];
265+ CDVPluginResult* result = [CDVPluginResult resultWithStatus: CDVCommandStatus_ERROR messageAsDictionary: posError];
266+ [self .commandDelegate sendPluginResult: result callbackId: callbackId];
267+ } else {
268+ if (!self->__locationStarted || (self->__highAccuracyEnabled != enableHighAccuracy)) {
269+ // Tell the location manager to start notifying us of location updates
270+ [self startLocation: enableHighAccuracy];
271+ }
259272 }
260- }
273+ }];
261274}
262275
263276- (void )clearWatch : (CDVInvokedUrlCommand*)command
0 commit comments