I need to keep getting user's location updates when app going to background and, as I found in Apple's documentation, this is one of the long-running background tasks that are allowed to be implemented. I also need to make some processing and network calls to send to server some info when location updates are notified. What I'm trying to do, in broad strokes, is something like this:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
// Background task
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"Background time remaining: %f seconds (%d mins)",
[[UIApplication sharedApplication] backgroundTimeRemaining],
(int)([[UIApplication sharedApplication] backgroundTimeRemaining] / 60));
// Need to get data from database here
// Finished
if (bgTask != UIBackgroundTaskInvalid) {
[application endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}
});
// Start location manager
locationManager = [[CLLocationManager alloc] init];
if ([CLLocationManager locationServicesEnabled]) {
locationManager.delegate = self;
locationManager.distanceFilter = 20;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
}
}
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Want to perform some data processing here, including concurrent tasks
// and several network calls to get and send data to server
[self locationProcessingCompleted];
});
}
- (void)locationProcessingCompleted
{
// Check results. If needed, some more data processing and several network
// calls to send data to server
// Close background task
if (bgTask != UIBackgroundTaskInvalid) {
[[UIApplication sharedApplication] endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}
}
I don`t find any code example of a long-running background task regarding location services, and I have several questions about it:
a) Is it necessary to execute all the background code within a dispatch_async
queue? In other words, has all the code in applicationDidEnterBackground:
to be executed asynchronously, including the location manager settings?
b) [[UIApplication sharedApplication] backgroundTimeRemaining]
is returning a very long value. This time is not supposed to be around 10 minutes?
c) I want to do: perform an initial finite-length task, start location updates, and then perform some other finite-length tasks every time that didUpdateToLocation:
is triggered. I have set the location
value of UIBackgroundModes
in Info.plist
. Is my approach correct? Will I keep locations being updating indefinitely, and then can I perform a finite-length task less than 10 min long at every update received?
Please, I need help with this issues. Thanks in advance
As per your provided code you are allocating LocationManager on applicationDidEnterBackground, I would like to suggest you to allocate LocationManager in didFinishLaunchingWithOptions method and then call startUpdatingLocation method in your applicationDidEnterBackground method.
You may also require to call stopUpdatingLocation in your applicationWillEnterForeground method (In case you don't require to receive location updates when app is in foreground).
Tip: Don't enable GPS with best accuracy for a long time, as it will eat up your battery very quickly (within 3 to 4 hours, as per my experience).
Disclaimer: I work for Cintric
We also wanted to be able to accurately track a users location in background (even after the app had been killed). We spent a long time solving the problem, especially focusing on battery drain.
We posted a great blog post on how we solved it. http://www.blog.cintric.com/?p=121 And also are providing a drag and drop SDK solution for free. With one line of code you can have it constantly track location in the background and use only 1% of battery. Additionally you can set a class to be it's delegate so you can send info to your server (including in the background without having to use background task api).
You can download the .framework file, drag it into your project and initialize with one line of code:
[CintricFind initWithApiKey:@"YOUR_API_KEY_HERE" andSecret:@"YOUR_SECRET_HERE"];
You can get an API key for free. There are docs explaining everythin here: https://www.cintric.com/docs/ios
All you have to do is add "App registers for location updates" under the "Required background modes" you can either edit plist file in a text editor and add following code or can use xcode to add required background modes.
the didUpdateToLocation method will be called (even when your app is in background). You can perform any thing on the bases of this method call...
Snapshot of Xcode