@@ -55,6 +55,8 @@ @implementation CDVLocation
55
55
56
56
- (void )pluginInitialize
57
57
{
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
58
60
self.locationManager = [[CLLocationManager alloc ] init ];
59
61
self.locationManager .delegate = self; // Tells the location manager to send updates to this object
60
62
__locationStarted = NO ;
@@ -80,86 +82,93 @@ - (BOOL)isAuthorized
80
82
return YES ;
81
83
}
82
84
83
- - (BOOL )isLocationServicesEnabled
85
+ - (void )isLocationServicesEnabled : ( void (^)( BOOL )) comletionHandler
84
86
{
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
+ });
92
96
}
93
97
94
98
- (void )startLocation : (BOOL )enableHighAccuracy
95
99
{
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 ;
110
121
}
111
- }
112
- // PERMISSIONDENIED is only PositionError that makes sense when authorization denied
113
- [self returnLocationError: PERMISSIONDENIED withMessage: message];
114
-
115
- return ;
116
- }
117
-
122
+
118
123
#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
+ }
131
136
#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
+ }];
151
158
}
152
159
153
160
- (void )_stopLocation
154
161
{
155
162
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
+ }];
163
172
}
164
173
}
165
174
@@ -195,36 +204,38 @@ - (void)getLocation:(CDVInvokedUrlCommand*)command
195
204
NSString * callbackId = command.callbackId ;
196
205
BOOL enableHighAccuracy = [[command argumentAtIndex: 0 ] boolValue ];
197
206
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 ];
212
217
}
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 ;
217
219
@synchronized (self.locationData .locationCallbacks ) {
218
- if (callbackId != nil ) {
219
- [ lData.locationCallbacks addObject: callbackId ];
220
+ if (!lData. locationCallbacks ) {
221
+ lData.locationCallbacks = [ NSMutableArray arrayWithCapacity: 1 ];
220
222
}
221
223
}
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
+ }
226
237
}
227
- }
238
+ }];
228
239
}];
229
240
}
230
241
@@ -246,18 +257,20 @@ - (void)addWatch:(CDVInvokedUrlCommand*)command
246
257
// add the callbackId into the dictionary so we can call back whenever get data
247
258
[lData.watchCallbacks setObject: callbackId forKey: timerId];
248
259
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
+ }
259
272
}
260
- }
273
+ }];
261
274
}
262
275
263
276
- (void )clearWatch : (CDVInvokedUrlCommand*)command
0 commit comments