Upon working on location product, one of the little challenges along the way has been how to report background location updates back to our servers.
First, some basics.
We're going to be using the Significant Location Changes feature as this is the recommended way of tracking the approximate device location in a low power way.
Note the really key features here;
a) If the application is suspended when an update occurs, the system wakes it up in the background to handle the update.
b) If the application starts this service and is then terminated, the system relaunches the application automatically when a new location becomes available.
That's just perfect, so what we can now do is turn on significant location updates when the user hits the home key and we can let the system wake us up when needed.
-(void) applicationDidEnterBackground:(UIApplication *) application
{
// You will also want to check if the user would like background location
// tracking and check that you are on a device that supports this feature.
// Also you will want to see if location services are enabled at all.
// All this code is stripped back to the bare bones to show the structure
// of what is needed.
[locationManager startMonitoringSignificantLocationChanges];
}
Then to perhaps switch to higher accuracy when the application is started up, use;
-(void) applicationDidBecomeActive:(UIApplication *) application
{
[locationManager stopMonitoringSignificantLocationChanges];
[locationManager startUpdatingLocation];
}
Next we'll likely want to change your location manager delegate to handle background location updates.
-(void) locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
BOOL isInBackground = NO;
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground)
{
isInBackground = YES;
}
// Handle location updates as normal, code omitted for brevity.
// The omitted code should determine whether to reject the location update for being too
// old, too close to the previous one, too inaccurate and so forth according to your own
// application design.
if (isInBackground)
{
[self sendBackgroundLocationToServer:newLocation];
}
else
{
// ...
}
}
If we are running in the background, we can't just use the network as we would normally. In background mode the iOS controls very strictly what is allowed, and for how long it is allowed, so if we were just to send the location to our server as normal, we will find this will be highly unreliable. It may work sometimes, it may not, and you will have no control over what is going on. We can however TELL the operating system in advance that we are doing a background task that should be allowed to run to completion. By doing this, we can ensure that our network activity is given enough time to complete and so the remote server will get the location updates OK.
-(void) sendBackgroundLocationToServer:(CLLocation *)location
{
// REMEMBER. We are running in the background if this is being executed.
// We can't assume normal network access.
// bgTask is defined as an instance variable of type UIBackgroundTaskIdentifier
// Note that the expiration handler block simply ends the task. It is important that we always
// end tasks that we have started.
bgTask = [[UIApplication sharedApplication]
beginBackgroundTaskWithExpirationHandler:
^{
[[UIApplication sharedApplication} endBackgroundTask:bgTask];
}];
// ANY CODE WE PUT HERE IS OUR BACKGROUND TASK
// For example, I can do a series of SYNCHRONOUS network methods (we're in the background, there is
// no UI to block so synchronous is the correct approach here).
// ...
// AFTER ALL THE UPDATES, close the task
if (bgTask != UIBackgroundTaskInvalid)
{
[[UIApplication sharedApplication} endBackgroundTask:bgTask];
bgTask = UIBackgroundTaskInvalid;
}
}
The key to this whole process is the use of background tasks to ensure that our synchronous network activity is given enough time to complete. This lets us update a couch database for example where we might need to make 1 network call to get the current document revision and then a second network call to actually put the new data.
0 comments:
Post a Comment