I'm writing an application that requires background location updates with high accuracy and low frequency. The solution seems to be a background NSTimer task that starts the location manager's updates, which then immediately shuts down. This question has been asked before:
How do I get a background location update every n minutes in my iOS application?
Getting user location every n minutes after app goes to background
iOS Not the typical background location tracking timer issue
iOS long-running background timer with "location" background mode
iOS full-time background-service based on location tracking
but I have yet to get a minimum example working. After trying every permutation of the above accepted answers, I put together a starting point. Entering background:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
self.bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
NSLog(@"ending background task");
[[UIApplication sharedApplication] endBackgroundTask:self.bgTask];
self.bgTask = UIBackgroundTaskInvalid;
}];
self.timer = [NSTimer scheduledTimerWithTimeInterval:60
target:self.locationManager
selector:@selector(startUpdatingLocation)
userInfo:nil
repeats:YES];
}
and the delegate method:
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation {
NSLog(@"%@", newLocation);
NSLog(@"background time: %f", [UIApplication sharedApplication].backgroundTimeRemaining);
[self.locationManager stopUpdatingLocation];
}
The current behavior is that the backgroundTimeRemaining
decrements from 180 seconds to zero (while logging location), and then the expiration handler executes and no further location updates are generated. How do I modify the above code in order to receive periodic location updates in the background indefinitely?
Update: I'm targeting iOS 7 and there appears to be some evidence that background tasks behave differently:
If you have the
UIBackgroundModes
in your plist withlocation
key then you don't need to usebeginBackgroundTaskWithExpirationHandler
method. That's redundant. Also you're using it incorrectly (see here) but that's moot since your plist is set.With
UIBackgroundModes location
in the plist the app will continue to run in the background indefinitely only as long asCLLocationManger
is running. If you callstopUpdatingLocation
while in the background then the app will stop and won't start again.Maybe you could call
beginBackgroundTaskWithExpirationHandler
just before callingstopUpdatingLocation
and then after callingstartUpdatingLocation
you could call theendBackgroundTask
to keep it backgrounded while the GPS is stopped, but I've never tried that - it's just an idea.Another option (which I haven't tried) is to keep the location manager running while in the background but once you get an accurate location change the
desiredAccuracy
property to 1000m or higher to allow the GPS chip to get turned off (to save battery). Then 10 minutes later when you need another location update, change thedesiredAccuracy
back to 100m to turn on the GPS until you get an accurate location, repeat.When you call
startUpdatingLocation
on the location manager you must give it time to get a position. You should not immediately callstopUpdatingLocation
. We let it run for a maximum of 10 seconds or until we get a non-cached high accuracy location.You need to filter out cached locations and check the accuracy of the location you get to make sure it meets your minimum required accuracy (see here). The first update you get may be 10 mins or 10 days old. The first accuracy you get may be 3000m.
Consider using the significant location change APIs instead. Once you get the significant change notification, you could start
CLLocationManager
for a few seconds to get a high accuracy position. I'm not certain, I've never used the significant location change services.How about giving it a try with
startMonitoringSignificantLocationChanges:
API? It definitely fires with less frequency and the accuracy is reasonably good. Additionally it has lot more advantages than using otherlocationManager
API's.Lot more regarding this API has already been discussed on this link
After iOS 8 their are several changes in CoreLocation framework related background fetch and updations made by apple.
1) Apple introduce the AlwaysAuthorization request for fetching the location update in the application.
2) Apple introduce backgroundLocationupdates for fetching location from background in iOS.
For fetching location in background you need to enable Location Update in Capabilities of Xcode.
When it is time to start location service and stop background task, background task should be stopped with a delay (1 second should be enough). Otherwise location service wont start. Also Location Service should be left ON for a couple of seconds (e.g. 3 seconds).
There is a cocoapod APScheduledLocationManager that allows to get background location updates every n seconds with desired location accuracy.
The repository also contains an example app written in Swift 3.
This Project will work to manage location in background.
LOCATION BACKGROUND UPDATER
will manage automatically if app enters background and need to send network call to update location.
I did write an app using Location services, app must send location every 10s. And it worked very well.
Just use the "allowDeferredLocationUpdatesUntilTraveled:timeout" method, following Apple's doc.
Steps are as follows:
Required: Register background mode for update Location.
1. Create LocationManger and startUpdatingLocation, with accuracy and filteredDistance as whatever you want:
2. To keep app run forever using "allowDeferredLocationUpdatesUntilTraveled:timeout" method in background, you must restart updatingLocation with new parameter when app moves to background, like this:
3. App gets updatedLocations as normal with "locationManager:didUpdateLocations:" callback:
4. But you should handle the data in then "locationManager:didFinishDeferredUpdatesWithError:" callback for your purpose
5. NOTE: I think we should reset parameters of LocationManager each time app switches between background/forground mode.