Socialize

Feel free to follow me on twitter

Core Location and CLLocationManager

Posted on Oct 20, 2013

The main system framework this app relies on is Core Location. Triply uses it to generate an initial fix on the users location, to calculate the distance between this location and the destination location at regular intervals and to track and monitor a region (the destination) in order to trigger an ‘event’ when  the user cross a pre-determined boundary.

It was clear that a number of view controllers, mainly the Home, Recents and Alarm view controller we’re going to need to rely on this framework and given it’s advisable to only use one instance of the CLLocationManager, I decided to implement a ‘wrapper’ class that hands out a singleton of the CLLocatioManager to each of the classes that needs it.

As part of this I used delegation to inform the ‘interested’ classes of any updates to the users location, including entry of boundaries to trigger the alarm. This is where it get’s tricky.  I need to make sure, at all times that the delegate property of the LocationManager class was set to the right view controller. Normally this would be straight forward as you’d just set it in viewDidLoad, which is fine when the timer get’s presented for changing the delegate to the timer so it receives location updates, but what about when it returns to the Home or Recents VC on deactivation of the alarm, viewDidLoad is never called as the presenting view controller is never deallocated, you can put it in viewDidAppear, however this is called before the modal alarm view has been fully deallocated, meaning for a split second location updates are still coming through, which then get sent to the recent/home VC resulting in another modal alarm view being presented, due to the delegation trigger used in the ‘pull to activate’ design which I talk about in my previous post.

Luckily this is a fairly simple fix  in that when the alarm is deactivated a BOOL ‘shouldPresentTimer’ flag is set to NO in the presenting view controller. Meaning even when a couple of final location update’s are sent to the presenting view controller during dismissal of the alarm view controller, it’s not re-presented.

Then there’s the issues of ensuring you’re only taking notices of accurate, relevant location updates that are fed to you by the location manager. Although you can set the level of accuracy, Apple does not guarantee that every update will be of that accuracy (especially when it’s first fired up you can be up to 1000m out until potentially the third update), not to mention the needs to preserve the users battery life as much as possible. It’s also noted that the first location update you receive will be a cached update that was stored as the last update from the last time the location manager was ran. Given Triply needed to use the first acceptable reading to calculate the initial distance to the destination, from which all remaining distances are calculated it was of upmost importance this cached location was not used. Here’s an extract of the start of my didUpdateToLocation code:

if (locationCount < 5) {
        NSLog(@"Location count passed with %d", locationCount);
        CLLocation* newLocation = [locations lastObject];
        NSLog(@"New location is %f", newLocation.coordinate.longitude);
        NSTimeInterval age = -[newLocation.timestamp timeIntervalSinceNow];

        if (abs(age) > 5.0) return;    // ignore old (cached) updates
        NSLog(@"Passed time test");

        if (newLocation.horizontalAccuracy < 0) return;   // ignore invalid udpates
        NSLog(@"Passed accuracy test");

        self.currentLocation = newLocation;
        NSLog(@"Current location updated to %f and location count updated to %d", newLocation.coordinate.longitude, locationCount);

It does some simple checks to validate the accuracy and time stamp before proceeding to use the location. Triply also uses an algorithm to vary the level of accuracy, the distance filter and frequency of updates based on how far the user is from their destination to ensure the alarm is triggered at the right time whilst preserving as much battery life as possible. I also added some code to so that the manager collects three updates, stops itself, and then chooses the mose accurate one to use.

The location manager wrapper class also handles the monitoring of ‘regions’ that you can specify along with a radius so that when the user crosses the boundary the aptly names didEnterRegion method is called. The great thing about this method is that it’s fired, even if the app is in the background, meaning Triply doesn’t need to keep monitoring the users location and gathering location updates on a background thread whilst the app isn’t active.