I'm building an app that uses CoreLocation for two purposes:
First, monitoring beacon regions. Mostly we work with a specific hardware device that acts as a beacon and we track with CoreLocation the status of that beacon connection.
Second, when the app detects a disconnection from the beacon device, we want to start a GPS process to locate where the disconnection happened.
In most cases, the disconnection callback of the beacon device happens while the app is in background. Therefore the app must start the GPS tracking by calling -startUpdatingLocation
while being in background.
And this is the problem. iOS seems to not enable location background updates if the -startUpdatingLocation
call happened in while the app was in background.
Obviously, I have configured correctly background modes, NSLocationAlwaysUsageDescription
, set the property _locationManager.allowsBackgroundLocationUpdates
to YES
.
The amazing thing: If I call -startUpdatingLocation
while being in foreground and then I switch to background, location updates are sent correctly while being in background. However, when calling -startUpdatingLocation
while being in background, then the application receives 2 or 3 location updates and then pauses forever.. until I manually bring back the application to the foreground, when location updates resumes and then keep working even if I switch the app to background again.
Does anybody know or understand what should I do to fix this? or is it a major constraint of iOS? Because of the requirements of my app I really need to start locating updates while the app is in background (inside the callback of a beacon did exit region).
Btw, if you want to reproduce this issue is very easy. Just create a blank project in Xcode and add this code to your default view controller. Remember to enable the background modes add a description to your info.plist key "NSLocationAlwaysUsageDescription".
- (void)viewDidLoad
{
[super viewDidLoad];
_locationManager = [[CLLocationManager alloc] init];
if (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_8_3)
_locationManager.allowsBackgroundLocationUpdates = YES;
_locationManager.delegate = self;
_locationManager.activityType = CLActivityTypeOther;
_locationManager.desiredAccuracy = kCLLocationAccuracyBest;
_locationManager.distanceFilter = kCLDistanceFilterNone;
[_locationManager requestAlwaysAuthorization];
// Creating a background task to delay the start of the startUpdatingLocation
__block UIBackgroundTaskIdentifier taskIdentifier = UIBackgroundTaskInvalid;
taskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:taskIdentifier];
}];
// Delaying the call to startUpdatingLocation 10 seconds, enough time to dismiss the application
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[_locationManager startUpdatingLocation];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[[UIApplication sharedApplication] endBackgroundTask:taskIdentifier];
});
});
}
Then, track the CLLocationManager delegate by doing some NSLogs and see what happens.
Thanks,
it's been 3 months since you posted this question. Have you figured it out on your own already?
I tried out your code. I put some NSLogs
in the AppDelegate
delegate functions, and in the viewDidLoad
. Also, in the AppDelegate
class, I logged out the background time left.
If startUpdatingLocation
is called before going into the background:
2016-08-18 01:22:52.355 test background GPS from StackOF[2267:745042] viewDidLoad
2016-08-18 01:22:52.376 test background GPS from StackOF[2267:745042] _locationManager.allowsBackgroundLocationUpdates = YES;
2016-08-18 01:22:52.382 test background GPS from StackOF[2267:745042] _locationManager requestAlwaysAuthorization
2016-08-18 01:22:52.410 test background GPS from StackOF[2267:745042] applicationDidBecomeActive
2016-08-18 01:22:52.469 test background GPS from StackOF[2267:745042] viewDidAppear
2016-08-18 01:23:03.361 test background GPS from StackOF[2267:745042] [_locationManager startUpdatingLocation];
2016-08-18 01:23:03.362 test background GPS from StackOF[2267:745042] start updating location, _locationManager.allowsBackgroundLocationUpdates == YES
2016-08-18 01:23:03.382 test background GPS from StackOF[2267:745042] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 65.000000
2016-08-18 01:23:03.630 test background GPS from StackOF[2267:745042] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 1743.000000
2016-08-18 01:23:03.709 test background GPS from StackOF[2267:745042] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 1743.000000
2016-08-18 01:23:03.716 test background GPS from StackOF[2267:745042] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 1743.000000
2016-08-18 01:23:03.720 test background GPS from StackOF[2267:745042] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 1743.000000
2016-08-18 01:23:03.814 test background GPS from StackOF[2267:745042] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 65.000000
2016-08-18 01:23:05.004 test background GPS from StackOF[2267:745042] inner block [[UIApplication sharedApplication] endBackgroundTask:taskIdentifier];
2016-08-18 01:23:05.005 test background GPS from StackOF[2267:745042] inner block start updating location, _locationManager.allowsBackgroundLocationUpdates == YES
2016-08-18 01:23:08.287 test background GPS from StackOF[2267:745042] applicationDidEnterBackground. Background time left: 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000
2016-08-18 01:23:10.319 test background GPS from StackOF[2267:745042] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 65.000000
2016-08-18 01:23:19.321 test background GPS from StackOF[2267:745042] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 65.000000
/****
Keep on polling...
****/
If startUpdatingLocation
is called after going into the background:
2016-08-18 01:34:29.023 test background GPS from StackOF[2285:747132] viewDidLoad
2016-08-18 01:34:29.030 test background GPS from StackOF[2285:747132] _locationManager.allowsBackgroundLocationUpdates = YES;
2016-08-18 01:34:29.033 test background GPS from StackOF[2285:747132] _locationManager requestAlwaysAuthorization
2016-08-18 01:34:29.047 test background GPS from StackOF[2285:747132] applicationDidBecomeActive
2016-08-18 01:34:29.104 test background GPS from StackOF[2285:747132] viewDidAppear
2016-08-18 01:34:31.612 test background GPS from StackOF[2285:747132] applicationDidEnterBackground. Background time left: 179.964144
2016-08-18 01:34:40.004 test background GPS from StackOF[2285:747132] [_locationManager startUpdatingLocation];
2016-08-18 01:34:40.006 test background GPS from StackOF[2285:747132] start updating location, _locationManager.allowsBackgroundLocationUpdates == YES
2016-08-18 01:34:40.290 test background GPS from StackOF[2285:747132] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 10.000000
2016-08-18 01:34:40.332 test background GPS from StackOF[2285:747132] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 355.117789
2016-08-18 01:34:40.336 test background GPS from StackOF[2285:747132] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 355.118536
2016-08-18 01:34:40.493 test background GPS from StackOF[2285:747132] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 355.141209
2016-08-18 01:34:40.496 test background GPS from StackOF[2285:747132] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 355.147748
2016-08-18 01:34:40.505 test background GPS from StackOF[2285:747132] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 355.148158
2016-08-18 01:34:40.507 test background GPS from StackOF[2285:747132] Lat/Long: <redacted GPS coordinates>, horizontal accuracy: 65.000000
2016-08-18 01:34:41.640 test background GPS from StackOF[2285:747132] inner block [[UIApplication sharedApplication] endBackgroundTask:taskIdentifier];
2016-08-18 01:34:41.642 test background GPS from StackOF[2285:747132] inner block start updating location, _locationManager.allowsBackgroundLocationUpdates == YES
/****
stops polling...
****/
Take note of the background time left. If the app goes into the background before the CLLocationManager starts updating location, the background time left is 3 minutes. I'm not too sure myself as I too have my own GPS background running issue, but from this brief test it seems like the CLLocationManager must have been called before the app goes into the background.
Here are where I put my NSLogs:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSLog(@"viewDidLoad");
_locationManager = [[CLLocationManager alloc] init];
if (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_8_3)
{
_locationManager.allowsBackgroundLocationUpdates = YES;
NSLog(@"_locationManager.allowsBackgroundLocationUpdates = YES;");
}
_locationManager.delegate = self;
_locationManager.activityType = CLActivityTypeOther;
_locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
_locationManager.distanceFilter = kCLDistanceFilterNone;
[_locationManager requestAlwaysAuthorization];
NSLog(@"_locationManager requestAlwaysAuthorization");
// Creating a background task to delay the start of the startUpdatingLocation
__block UIBackgroundTaskIdentifier taskIdentifier = UIBackgroundTaskInvalid;
taskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:taskIdentifier];
NSLog(@"[[UIApplication sharedApplication] endBackgroundTask:taskIdentifier];");
}];
// Delaying the call to startUpdatingLocation 10 seconds, enough time to dismiss the application
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[_locationManager startUpdatingLocation];
NSLog(@"[_locationManager startUpdatingLocation];");
if(_locationManager.allowsBackgroundLocationUpdates == YES)
{
NSLog(@"start updating location, _locationManager.allowsBackgroundLocationUpdates == YES");
}
else
{
NSLog(@"start updating location, _locationManager.allowsBackgroundLocationUpdates == NO");
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[[UIApplication sharedApplication] endBackgroundTask:taskIdentifier];
NSLog(@"inner block [[UIApplication sharedApplication] endBackgroundTask:taskIdentifier];");
if(_locationManager.allowsBackgroundLocationUpdates == YES)
{
NSLog(@"inner block start updating location, _locationManager.allowsBackgroundLocationUpdates == YES");
}
else
{
NSLog(@"inner block start updating location, _locationManager.allowsBackgroundLocationUpdates == NO");
}
});
});
}
The CLLocationManager
delegate function:
-(void)locationManager:(CLLocationManager *) manager didUpdateLocations:(nonnull NSArray<CLLocation *> *)locations
{
CLLocation *location = [locations firstObject];
NSLog(@"Lat/Long: %f %f, horizontal accuracy: %f",
location.coordinate.latitude,
location.coordinate.longitude,
location.horizontalAccuracy);
}
The AppDelegate:
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(@"applicationDidEnterBackground. Background time left: %f", [[UIApplication sharedApplication] backgroundTimeRemaining]);
}
TL;DR: try calling startUpdatingLocation
right in viewDidLoad
or in the AppDelegate application:didFinishLaunchingWithOptions:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With