Over the last few posts in our Core Location Framework series of tutorials one of the protocols we have relied heavily on is the CLLocationManagerDelegate. We haven’t gone through this particular protocol in detail though, which is why we are doing just that today.
What is a Protocol?
A protocol in iOS is much like a protocol in other situations in that it is a set of rules defining how a system works. An example would be the HTTP (HyperText Transfer Protocol) which is used to transmit data across the internet. By defining certain rules, HTTP ensures that data can be sent and received in the correct order and displayed on the users screen. Browsers that conform to this “standard” will function correctly when data is sent over HTTP.
In iOS, protocols work in much the same way. They define the rules of how certain things need to be done. The items in the protocol reference are either not marked which means they are “optional” or are marked as “required”. What that means is that if you implement a UITableViewDataSource protocol then there are a couple of required methods in there. If you do not implement those methods and provide the information the protocol requires, the app will get an error and will not compile. For those that are not marked which are defined as optional, it is up to you if you implement them, but if you do you need to implement the methods exactly as required to get the app to function correctly.
The CLLocationManagerDelegate Protocol
Implementing Delegate protocols in iOS is quite simple. There are a few steps you need to take, all of which are quite simple.
Lets take a look at how the CLLocationManagerDelegate can be added to your project. We will begin with setting up a Single View Application as described in this tutorial.
When you have created the Single View Application, add the CoreLocation.framework and import it in to the header of your ViewController.
Importing CoreLocation allows us to access the classes found within. After importing it, lets add the syntax to declare that this class (the ViewController.h/m) is adopting this particular protocol. By default, any other classes that create an instance of this class will also automatically adopt the protocol.
@interface ViewController : UIViewController
The line of code listed above shows the syntax used to show ViewController adopts the CLLocationManagerDelegate protocol. We simply add it between angled brackets <> in the @interface line in the header. If you want to conform to more than 1 protocol then you can separate the different protocols with a comma.
If required methods were found within this protocol we would have seen warnings in the past, but now it is up to you to read the documentation to find the required methods and implement them.
Now that these two lines of code have been added, we are now adopting the CLLocationManagerDelegate protocol.
Lets take a look at what that makes available to us and how we would use it. Let us first look at why we need this delegate though. The overview of the documentation tells us that when we create a CLLocationManager object to handle location related activities, the CLLocationManagerDelegate protocol defines the methods that can be used to get the location and heading updates. In other words, if you want your app to run some code each time a new location is detected, you implement one of the methods found in the delegate and each time an update is received, the method is called and your code executed.
As of iOS 6 there are currently 12 methods available plus 1 deprecated method. These are grouped in to five different categories which are “Responding to Location Events”, “Pausing Location Updates”, “Responding to Heading Events”, “Responding to Region Events”, “Responding to Authorization Changes”.
Responding to Location Events
There are three active methods and 1 deprecated method in this section. These are:
locationManager:didUpdateToLocation:fromLocation: Deprecated in iOS 6.0
The last item in the list has been replaced by the first item. I wrote more about these changes in this post. To briefly summarise the change, the method now passes an NSArray containing CLLocation objects where as the older version just provided a single CLLocation object.
The middle two methods deal with errors and the first is the method you would use to do something if the device location changes.
The full method is – (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations and when implemented it tells the delegate that new location data is available. We have two arguments on this method. The first lets you know which CLLocationManager provided the update and the last provides the CLLocation information which is stored in an NSArray.
One example of when you would use this method would be an app which needs to show your current location and move a map around as you move. Each time you move, this method would be called and you could run some code that repositions the map to keep your current location in the centre. How far you need to move depends on what accuracy you set on the CLLocationManager object.. that could range from the highest accuracy right up to a 3KM accuracy.
Thanks to code completion, these methods are extremely easy to type in. In your implementation file, type a – sign and then start typing locationManager… as you type the code completion will give you several options. Scroll up or down to select the method and hit enter for code completion to fill the rest in. Just add the curly braces on the beginning and assuming your settings are default the end curly brace will automatically be added. For some details on code completion, check the 6th or so paragraph on this article about Xcode documentation.
locationManager:didFailWithError: and locationManager:didFinishDeferredUpdatesWithError:
These next two methods are important to add to your project. The first full method is – (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error which lets you know if the system is unable to get a location value. You should probably implement this so that you can warn the user if there is a technical difficulty figuring out where you are. Implementation is quite simple. You simply add the method to ViewController and if the error occurs this method is run and it tells you the locationManager object along with the error. You can then act as needed to let the user aware.
The second of the error methods is – (void)locationManager:(CLLocationManager *)manager didFinishDeferredUpdatesWithError:(NSError *)error which is also called (if implemented) and lets you know if location updates are deferred. Deferring updates is a method from within the CLLocationManager. This method simply lets you know the “status” so your app can be aware and act accordingly.
Paused and Resumed Location Updates
The next two methods deal with paused and resumed location updates. Although there are no marked methods in the tasks section, the text for these two methods indicates that they are actually both required. This means that both need implementing.
locationManagerDidPauseLocationUpdates: and locationManagerDidResumeLocationUpdates:
The methods mentioned just above are called when location updates are paused or resumed. iOS pauses and resumes updates automatically. If no change in location is detected after a period of time, iOS will pause the update services in order to save battery life. If you are not moving then whats the point of checking?
iOS handles pausing and resuming of location updates and these two required methods are simply in place for your app to be aware of what is happening. You might want to change the colour of the blue dot for example when location updates are paused or you might not want to do anything. Its up to you how you handle these. The full method names are:
– (void)locationManagerDidPauseLocationUpdates:(CLLocationManager *)manager
– (void)locationManagerDidResumeLocationUpdates:(CLLocationManager *)manager
Responding to Heading Events
Heading events are related to the magnetometer. These use the CLHeading class which provides heading details of the device. The two related methods are locationManager:didUpdateHeading: and locationManagerShouldDisplayHeadingCalibration:.
If you are using the magnetometer then any changes to the direction you are facing will call this particular method when implemented. As with the location updates mentioned above, the heading updates also let you know the CLLocationManager. Instead of providing a CLLocation, this method provides a CLHeading called newHeading. When inspecting the properties of this, you can find the heading information you need and update your app accordingly. The full method is – (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading and can be used to update a compass in your app, rotate a map to show which way you are walking or be used for many other ideas.
The method will only work if heading updates are activated in the CLLocationManager.
This method should be implemented when using the magnetometer. It is the second method of the Responding to Heading Events article. If you have used the maps app, compass app or any other app that uses the compass then you might have seen the usual figure of 8 picture telling you there is compass interference. Because the magnetometer requires the earths magnetic field to calculate where North is, interference can cause issues which in turn, brings up this warning. To filter out any unwanted magnetic field you need to move your device in a figure of 8 motion and/or move away from any interference.
Implementation of this method is optional. If you don’t implement it, the CLHeading assumes NO for the return value and your app will not show the compass calibration screen. Accuracy will also be noted in the headingAccuracy property. However, if you implement it you can return a YES or a NO depending on your apps requirements.
The full method name is -(BOOL)locationManagerShouldDisplayHeadingCalibration:(CLLocationManager *)manager.
Responding to Region Events
We covered two of the methods in a tutorial last week for this. These were locationManager:didEnterRegion: and locationManager:didExitRegion:. The CLRegion tutorial can be found here. Simply put, you can define regions on a map and the methods above will run when you either enter or exit a defined region. In your didEnterRegion method you could have some code that logs you in at work on the timesheets and have the didExitRegion log you out on the timesheets. The benefit of these methods is that they can be called even if your app isn’t running which makes them extremely useful.
locationManager:didEnterRegion: and locationManager:didExitRegion:
As mentioned already, these two delegate methods are called when you enter or exit a define region. The full method names are – (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region and – (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region. The CLLocationManager is provided as one argument and the CLRegion that was triggered is the other argument.
Another method from within the Responding to Region Events is the – (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region method. This is simply called each time you start monitoring for a new region. Perhaps one use would be to highlight a pin on the map or simply pass a message to the UIView to inform the user that monitoring is now running for a specific region. The last argument is what lets you know the region being monitored.
The final method is the error one. This is also an optional method but recommended if you use region monitoring. The method is – (void)locationManager:(CLLocationManager *)manager monitoringDidFailForRegion:(CLRegion *)region withError:(NSError *)error and provides the locationManager, the region and the error message if any errors occur.
Responding to Authorisation Changes
The last method of the CLLocationManagerDelegate, if you made it this far, is locationManager:didChangeAuthorizationStatus:. It is quite an important one because any iOS user has control over privacy which includes switching off location updates for certain apps. If the user decides to disable location services for your app then this method is called and can be used to put some code in to let the user know that location has been blocked and provide instructions how to change it back.
The CLLocationManagerDelegate has a number of powerful methods within. I encourage you to implement those you need along with any associated error methods so that your app can appropriately respond if the user denies access or if there are errors with the location services.
As always, documentation is key when programming iOS apps. If you are unsure how to use something, refer to the documentation or take a look here at our growing amount of tutorials.
Any questions, post them below.