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:
You have the manager class now responsible for another class’s implementation without a contract.
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