Core Location in iOS: A Tutorial
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:
#import
the correct Core Location header file- Link against the Core Location framework
- Implement two delegate methods to receive information from Core Location
- Tell Core Location to start sending us updates
- 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 |
#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 |
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error |
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; |
@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 thedidUpdateToLocation
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]; } } |
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]; } } |
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]; |
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]; |
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.