Taming Core Location

In today’s mobile development environment using the device’s GPS in some shape, form or fashion is a given. Apple provides a great toolset for developers to access and utilize this functionality, but this is often done carelessly. I am not talking about privacy concerns or the misuse of the data, but many developers, myself included early on, had a poor code design implementation. The characteristics of such are usually:

  • CoreLocation implementation that was always tied directly to a particular UIViewController
  • Set the accuracy to kCLLocationAccuracyBest
  • Set filtering to kCLDistanceFilterNone.
  • Requests way to many location updates

Let’s examine each reason why these are not the best practice

Tight Coupling is Wrong

This is a more of a development practice that developers should always attempt to achieve in their code. There are always examples and uses cases as to why you would have to break this paradigm, but is still goal none the less. What we generally see, and most tutorials on the topic do this, is that the UIViewController sublcass will conform to the CLLocationManagerDelegate protocol, setup the manager and properties and listen for callbacks. I guess that is OK if you have only one use case and user interaction that requires CoreLocation, but in most cases you need to access CoreLocation more than once, thus you have to duplicate the same code. The “C” in MVC has now broken it’s intended contract and you have tightly coupled code.

Less Accuracy is More

This lesson is pretty simple. The more accurate the location you request, the longer it takes to get and the more battery power is drained. The faster the battery is drained the faster the iOS user gets pissed. The simplest example is a weather application. There is NO need to request such a detailed location. By setting your accuracy to kCLLocationAccuracyThreeKilometers you save yourself, and most importantly the user, time and end up with same information. Weather is usually shown based on a region area. i.e. zip code or city.

“For example, setting the desired accuracy for location events to one kilometer gives the location manager the flexibility to turn off GPS hardware and rely solely on the WiFi or cell radios. Turning off GPS hardware can lead to significant power savings.” – CoreLocationManager Reference

Filters are For Your Protection

Unless you are creating a turn-by-turn navigation app set a filter. Getting a location updates every half step or every second doesn’t add any value to the task of the application, but does have the same downsides as mentioned above. Poor battery life and horrible user experience.

You Don’t Need Updates Every Two Feet

This error is really an error in judgement. Apple provides developers with the fine grained control via distanceFilter and desiredAccuracy, but should be used sparingly and with apps that require multiple checks throughout the app session for location updates.

This service is most appropriate for applications that need more fine-grained control over the delivery of location events. Specifically, it takes into account the values in the desiredAccuracy and distanceFilter property to determine when to deliver new events. The precision of the standard location services are needed by navigation applications or any application where high-precision location data or a regular stream of updates is required. However, these services typically require the location-tracking hardware to be enabled for longer periods of time, which can result in higher power usage.

For apps that don’t require a constant stream of updates it is best to use start/stopMonitoringSignificantLocationChanges.

This method is more appropriate for the majority of applications that just need an initial user location fix and need updates only when the user moves a significant distance. This interface delivers new events only when it detects changes to the device’s associated cell towers, resulting in less frequent updates and significantly lower power usage.

Better CoreLocation Pattern Options

The first issue to tackle when implementing CoreLocation features is how to create your own manager that can be used and called throughout the app. The second issue that has to be addressed is how to allow other classe, regardless of type, UIView, UIViewController, NSObject, get notified of updated location information.

Issue One

The basic approach is to create subclass of NSObject that conforms to CLLocationManagerDelegate and implements the appropriate delegate methods. In my particular case I do have to listen for a stream of updates so I will be setting the desiredAccuracy and distanceFilter properties.

Issue Two

In order to tackle issue 2 I go one of three ways: NSNotification, Target/Action or KVO.

NSNotification would solve this problem, however it is seemed too decoupled and I personally feel that NSNotifications should be reserved for OS level notifications not service layer/domain model context changes. Finally, there are performance considerations when it comes to using notifications. No matter if the post to the notification center is done asynchronously or synchronously it is dispatched to the object synchronously. If there are a number of observers or if there is an instance where an observer must do a lot of work then your app could experience a significant delay.

Next on the list is the target/action pattern. While NSNotification was too decoupled from listeners, using target/action has now added a level of coupling that I felt a little unnecessary. In our custom manager class I would have to set a property, either NSMutableArray, NSSet or id that would hold the target instance, in addition, a property for the SEL, and when the location manager’s delegate method received a valid location update I would have to call something like:

`//NSMutableArray or NSSet

[clmListeners makeObjectsPerformSelector:@selector(methodToPerform)];`

`//id target

[target performSelector:@selector(methodToPerform)]`

There are two issues with doing the above proposal:

  1. You have the manager class now responsible for another class’s implementation without a contract.

  2. You are getting really close to having a delegate pattern which brings us closer to a one-to-one coupling between our classes.

The final option that we come to is Key Value Observing…which is the solution that I have gone with. Not only does it keep our manager class independent, but we also are also able to write our location code once while allowing our other classes to handle any UI or model manipulation as they see fit…independent of any other implementation or logic.

Custom Core Location Manager Class

gist.github.com/2421914

View Controller Observing Change to Current Location

gist.github.com/2421928

Using NSNotificationCenter with UINavigation Controllers

When presenting an app to user with a large list of information, most developers will add in the ability to search the list inside of the table view.  To further increase usability it is a good practice to include different filters.  For example, a scope button that allows the user to filter by name, address, phone number, etc.  Implementing these features is pretty straight forward.  However, one of the filter options that was needed for a particular app that I am working on was to filter by department.

The complexity to this problem came in the fact that the search term couldn't be the department name, but the code.  Obviously, most users don't know the corresponding codes for a given department.  So I needed a way to present a list of "friendly" department names to the user to pick from, and once a given department was chosen then the corresponding department code would be populate the search bar field.

The first attempt was to use a UIPickerView.  Unfortunately, this wasn't the best solution mainly because you can't use key/value pairs with the UIPickerView.  Well...not in a very straight forward manner.  My second solution attempt was to use a modalViewController and then pass the selected object back to the parent controller.  Everything worked out just fine, but the parent object property wasn't being updated.

To solve this problem there are a couple of schools of thought.
  1. Set the parent controller as the delegate and setup a new protocol that the child controller implements.
  2. Use the NSNotificationCenter which uses the Observer Pattern to update another object based upon some action/method.

Most of the time I would say that the second option is overkill since it is designed for a one-to-many object broadcasts, but in this use case it is the preferred approach because "you would otherwise have to pass instances around of delegates just to connect objects". - http://alexvollmer.com/index.php/2009/06/24/cocoas-ways-of-talking/

After a user selects the appropriate department, the child controller is dismissed and the search bar text is updated with appropriate department code.