cordova-plugin-geolocation icon indicating copy to clipboard operation
cordova-plugin-geolocation copied to clipboard

iOS 14: watchPosition stops working when the App is suspended and reactivated

Open katzlbt opened this issue 4 years ago • 9 comments

In iOS 14 watchPosition stops working as soon as the Application is suspended and reactivated. This is probably due to a new(?) iOS behavior of sending error.code == kCLErrorDenied when the app is suspended and has "in use GPS permissions".

The documentation (https://cordova.apache.org/docs/en/10.x/reference/cordova-plugin-geolocation/#geolocationoptions) states that:

Note that when used in conjunction with geolocation.watchPosition, the geolocationError callback could be called on an interval every timeout milliseconds!

Problem Diagnosis

A few seconds after being suspeded(!) the debugger stops in the function (https://github.com/apache/cordova-plugin-geolocation/blob/master/src/ios/CDVLocation.m#L343-L352):

- (void)locationManager:(CLLocationManager*)manager didFailWithError:(NSError*)error
error.code == kCLErrorDenied and this generates positionError = PERMISSIONDENIED

... and then stops the watch ...

    if (error.code != kCLErrorLocationUnknown) {
        [self.locationManager stopUpdatingLocation];
        __locationStarted = NO;
    }

After reactivation of the app my code gets a GPS Timeout! Code: 3

What is expected to happen?

GPS watchPosition should probably continue working after the app was suspended. The timeout should fire repeatedly as stated in the documentation.

What does actually happen?

watchLocation stops permanently with a TIMEOUT without being cancelled.

Discussion

Apples error number is correct because the user has denied access to location in the suspended app and the code executes after the app is suspended. However the doc concludes that:

If the user denies your app's use of the location service, this method reports a kCLErrorDenied error. Upon receiving such an error, you should stop the location service. (https://developer.apple.com/documentation/corelocation/cllocationmanagerdelegate/1423786-locationmanager?language=objc)

The second statement - stop the location service - is incorrect if the app is just suspended and the user did not give access to background location. The error occurs always and exactly once until the app is suspended.

Also: should kCLErrorHeadingFailure stop the location service? Probably not.

Environment, Platform, Device

iOS 14.4.2, newest XCode

Version information

10.0.0

katzlbt avatar Apr 20 '21 09:04 katzlbt

Also sort of have this issue on iOS 12.3.1. My issues is when a certain time elapses or the user moves 100m from their initial position a list of nearest locations is updated. However, I get the GPS timeout when the app tried to update the list. As with you, it works perfectly on Android. It's frustrating because my work-around is to call getCurrentPosition every 5000ms (using setInterval) but my users are complaining that the location updating is noticeably worse between the previous and current version.

junior-senior avatar Apr 26 '21 09:04 junior-senior

It's frustrating because my work-around is to call getCurrentPosition every 5000ms (using setInterval) but my users are complaining that the location updating is noticeably worse between the previous and current version.

Your workaround shuts down the GPS after every getCurrentPosition() potentially requiring a new warmup. You need to apply my patch and use watchPosition() instead: https://github.com/apache/cordova-plugin-geolocation/pull/225/commits/4d5a5b4e9ceb2cbead06c63572e83b9c82d63349

location updating is noticeably worse between the previous and current version.

Version of what? CDVLocation.m has not changed for years, but iOS has changed more often.

katzlbt avatar Apr 26 '21 10:04 katzlbt

Your workaround shuts down the GPS after every getCurrentPosition() potentially requiring a new warmup. You need to apply my patch and use watchPosition() instead: 4d5a5b4

Great thanks, I'll give that a go and let you know. \

Version of what? CDVLocation.m has not changed for years, but iOS has changed more often.

Sorry I should have been more clear, I meant the different versions of my app. This issue arose for me when I transitioned my app from Ionic 2 to 5.

junior-senior avatar Apr 26 '21 10:04 junior-senior

Did not fix it for me unfortunately, still getting a time out.

junior-senior avatar Apr 26 '21 12:04 junior-senior

Yes, but that should not stop the GPS. You may receive 1 or 2 timeouts and then it should start working (except indoors). Timeouts are generated by geolocation.js of this plugin, not iOS/Android code. Old hardware took minutes to get a lock. NOTE: iOS devices without SIM have no GPS hardware!! ... and position.altitudeAccuracy == -1 What are your geolocationOptions? https://cordova.apache.org/docs/en/10.x/reference/cordova-plugin-geolocation/#geolocationoptions

katzlbt avatar Apr 26 '21 12:04 katzlbt

Tried with a SIM in and it'd the same result. Options are: enableHighAccuracy: true, timeout: 20000. Also tried with enableHighAccuracy: false and no change. I have managed to get the timeouts to stop by changing self.locationManager.distanceFilter from = 5 to = kCLDistanceFilterNone;. Although I'm a little concerned as the comment states that this could lead to increased battery usage.

junior-senior avatar Apr 26 '21 13:04 junior-senior

have managed to get the timeouts to stop by changing self.locationManager.distanceFilter from = 5 to = kCLDistanceFilterNone;

This means you aren't moving more than 5-8 meters (depending on location accuracy in your current area). iOS does battery optimisation by avoiding sending GPS events if there isn't any significant changes in geolocation. This is set to 5 by default in high accuracy mode. kCLDistanceFilterNone constant disables this so that you'll get GPS events regardless if the position has changed or not. It's not recommended by Apple to use this, but depending on your use case it may be necessary.

breautek avatar Apr 26 '21 13:04 breautek

Even while moving in a car the timeouts still happen with this set to 5.

junior-senior avatar Apr 30 '21 09:04 junior-senior

There is nothing wrong with receiving timeouts. I ignore them and have a timeout of 45000, probably should use infinity. You also may get them after the app was suspended. As engineer, you should be able to diagnose the problem and file a separate bug. There are just 600 lines of code in 2 files involved.

katzlbt avatar Apr 30 '21 10:04 katzlbt

Closing as stale.

breautek avatar Jun 05 '23 02:06 breautek