iOS Dev Notes

iOS Development From The Frontlines

Core Location in iOS: A Tutorial

with 8 comments

Developing iOS apps that can take advantage of the built-in GPS unit is a great way to add a new dimension to the user experience. You can quickly make your apps location-aware (and able to find the current latitude/longitude coordinates) with minimal effort. This tutorial will walk through the steps involved in finding a device’s current position.

In iOS, you can find the current lat/lng by making a request to the Core Location component to start looking up location information. This works via a series of delegate messages passed to our application when Core Location starts updating. By default, the location hardware in our device is usually turned off (or otherwise in an idle state) so as not to needlessly drain power. In a nutshell we need to:

  1. #import the correct Core Location header file
  2. Link against the Core Location framework
  3. Implement two delegate methods to receive information from Core Location
  4. Tell Core Location to start sending us updates
  5. Properly shut down Core Location when we don’t need it anymore.

Getting Started

Since we’re going to be using Core Location with our iOS app, we need to add the header file (CoreLocation/CoreLocation.h) to our prefix header (usually with the extension .pch). In this prefix header, locate the #ifdef __OBJC__ line:

#ifdef __OBJC__
    #import 
    #import 
    #import  // added for Core Location
#endif

If you have a larger project and are only using Core Location in a few places, you could alternatively just add the #import line to the files where you actually need it. In our simple example, we’ll stick with the prefix file.

Next, we need to add the Core Location framework to our project’s link phase. In Xcode, click on the project’s name at the top of the project navigator view. In the Linked Frameworks and Libraries section, click the “+” icon to add a new framework. Find the Core Location.framework entry, and add it. When you next link your project, the framework will be automatically included.

 

Setting up the Delegate

The begin setting up our class as a delegate, the first step is to locate the class where we want to receive Core Location messages. Then, we need to set that class up as a CLLocationManagerDelegate. You can do this by adding to the @interface definition line.

Next, in the implementation file, we need to add two delegate methods:

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation

This method will be called when Core Location has new location data for us to process. We receive these messages both when we have better accuracy for a current location, or the device has moved. These messages can also come in quickly one after the other, or as a trickle.

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error

If Core Location needs to report a failure of some kind, this method will be called. We need to properly handle these. Note that this method is technically optional, but it’s best to get into the habit of handling these messages.

Next, we also need a way to store where we currently are located. Since there’s no guarantee of when we’ll be told of our location, we need to store this information for the times when we need to reference it. We also need to keep track of our Core Location manager instance. So, be sure to add the following to your class’ @interface definition:

@property (strong, nonatomic) CLLocationManager *locationManager;
@property (strong, nonatomic) CLLocation *currentLocation;

And, in the implementation file, the corresponding synthesize methods:

@synthesize locationManager, currentLocation;

Receiving Messages

Now that the setup is out of the way, it’s on to the fun stuff. First, let’s implement the didUpdateToLocation method. I’ll paste the entirety here and then we’ll step through it:

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
    self.currentLocation = newLocation;
 
    if(newLocation.horizontalAccuracy <= 100.0f) { [locationManager stopUpdatingLocation]; }
}

First, note that we’re saving the currentLocation. Again, since we don’t know when we’re going to be given an update to our location, we need to keep this around for whenever we might need it in the future.

Next, we check the included accuracy against a pre-set expectation. Each time we’re given a newLocation, we’re given an estimation on how accurate it is. This float is in meters, and usually starts out as pretty inaccurate (remember that when we first start requesting location updates, Core Location needs to determine the device’s location, which can take time).

You’ll notice in the Maps app on your iPhone that a blue circle is initially drawn around the point marking your location. That blue circle’s radius is determined by this horizontalAccuracy number. If you’re going to be showing a point on a map for the user’s current location, you may want to consider adding a similar “accuracy circle”.

The reason why we’re checking the provided accuracy against a pre-set expectation is that we don’t want to leave Core Location running for any longer than we absolutely need it to. It drains the battery life of the device when it has to communicate with GPS satellites or power up the radio for location triangulation, so we want to turn it back off again as soon as we have a good-enough position. In the example above, a position +/- 100m is plenty accurate for us. To stop Core Location from sending us updates (and thus allowing it to go back into an idle state), we send our locationManager the stopUpdatingLocation message.

Lastly, we need to implement the didFailWithError method. Here it is:

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
    if(error.code == kCLErrorDenied) {
        [locationManager stopUpdatingLocation];
    } else if(error.code == kCLErrorLocationUnknown) {
        // retry
    } else {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error retrieving location"
                              message:[error description]
                              delegate:nil
                              cancelButtonTitle:@"OK"
                              otherButtonTitles:nil];
        [alert show];
    }
}

When handling errors from Core Location, we need to worry about two special cases. The first is when the user specifically denies our app access to finding its position. If that occurs, we want to immediately stop trying to update by sending the stopUpdatingLocation message.

The second special case is when we’re given a kCLErrorLocationUnknown error. This can occur when Core Location is first starting up and isn’t able to immediately determine its position, or even make a guess as to where it is. If we get this message, its best to ignore it and to keep trying to update our location.

In other cases, we may want to alert the user to a problem with getting a location. According to the Core Location docs, this might occur when the device is near a particularly strong magnetic field. In this example, we’re showing the user a problem but still retrying. You will need to determine for your own app whether you want to stop trying to get location updates at this point or to keep retrying.

Requesting Updates

After the delegate has been implemented, it’s time to actually setup Core Location and request location updates. To setup Core Location, you’ll want to do the following:

locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
[locationManager startUpdatingLocation];

This sets up a new CLLocationManager instance and saves a pointer to it in a class variable. We next set our current class up as the delegate to receive CLLocationManagerDelegate messages. And finally, we tell Core Location to start figuring out our position.

In my example app, I’ve placed all of my Core Location-related code in a single view controller’s class as I only need this information in one section of my app. Thus, I placed the above three lines in my viewDidLoad method, which is called after the view has been loaded, but before it’s being shown to the user. This could occur right when the app opens up (if loaded from the XIB file), but it could also occur at another point in the app, such as after a low-memory condition and the view had been previously unloaded.

Since I put this setup code in the viewDidLoad method, I also need to remember to tell Core Location to stop updating should my view get unloaded. Thus, I placed the following line in my viewDidUnload method:

[locationManager stopUpdatingLocation];

It’s important to remember to tell Core Location to stop sending messages to the delegate before the delegate could be freed, which could cause Core Location to attempt to send messages to a freed/nil object (which would cause a crash).

And that’s all there is to it! Implementing Core Location is an easy thing to implement and can add an interesting new dimension to your iOS apps. Be sure to post in the comments if you found this interesting or have questions.

Reverse Geocoding

One final note. Now that we’ve successfully found the current lat/lng of our device, the next step is to do something interesting with it. Many times you’re looking to turn the coordinates into a street address.

Reverse geocoding is the process by which you find the closest street address given a latitude/longitude coordinate pair. To do this, you must use a reverse geocoder, which is a third-party service (though there is a built-in geocoder in iOS that uses Google). Be sure to check out the article on using SimpleGeo to reverse geocode a pair of lat/lng coordinates into a street address.

Update: Adding an NSTimer

A suggestion was made that we also start an NSTimer to ensure against the case where we never get a “good-enough” location, which would thus leave Core Location running indefinitely. Check out the follow-up post which shows how to implement an NSTimer to stop Core Location after a pre-set time interval.


Looking to add push notifications to your iOS app? Check out PushLayer, a service from the authors of iOSDevNotes.

Bookmark and Share

Subscribe for updates

Fill out the form below to join my newsletter. I'll also send you a bonus copy of my ebook, "5 Steps To Becoming a Better iOS Developer".

Written by rtwomey

October 26th, 2011 at 12:34 pm

8 Responses to 'Core Location in iOS: A Tutorial'

Subscribe to comments with RSS or TrackBack to 'Core Location in iOS: A Tutorial'.

  1. [...] I posted a tutorial on getting started with Core Location. Be sure to check it out if you haven’t [...]

  2. Good catch on locationManager:didUpdateToLocation:fromLocation getting cut off. Should be correct now.

    M

    5 Dec 11 at 3:33 pm

  3. Great tutorial for starters, informative, brief and right to the point.
    One detail I found is that the code snippet didUpdateToLocation right after the “Receiving Messages” title, seems to be incomplete, you explain later and one could assume the code but better if its complete. Looking forward to your other related tutorials. Thanks

    Giovanni

    8 Dec 11 at 8:34 am

  4. Nice tutorial! I’ve also written about how to get longitude and latitude via corelocation service. See it here http://www.altinkonline.nl/tutorials/xcode/corelocation/find-longitude-and-latitude-by-corelocation-xcode/

    Herbert Altink

    12 Jan 12 at 5:43 pm

  5. [...] Core Location in iOS: A Tutorial In other cases, we may want to alert the user to a problem with getting a location. According to the Core Location docs, this might occur when the device is near a particularly strong magnetic field. In this example, we’re showing the user a problem but still retrying. You will need to determine for your own app whether you want to stop trying to get location updates at this point or to keep retrying. Requesting Updates [...]

  6. Were you trying to add anything original to the three references? It just seems like a summary of the three. The example code is quite similar.

    Abizern

    29 Jul 12 at 6:16 am

  7. Oops, sorry – I was trying to leave a comment here: http://blog.paxcel.net/blog/you-are-here-part-i/ which seems to have replicated your example

    Abizern

    29 Jul 12 at 6:17 am

  8. [...] CoreLocation and GPS:iOS CoreLocation Tutorial: http://www.iosdevnotes.com/2011/10/ios-corelocation-tutorial/Building Location-Based Applications for the iPhone [...]

Leave a Reply