Hello There: A CoreLocation Tutorial
There are plenty of different places to get a mobile application designed. The problem is that they’re quite expensive. You might be able to figure out how to create your own, but it will probably look very basic. Instead, a good mobile application development software can make it even easier, so that you can build great looking apps all by yourself.
The Mobile design Starter Kit includes all the themes and scenarios you need to build whatever app you want. There are customizable and standard files that allow you to sell or offer anything you want through the kit. Everything is there, so once you spend the money, you can create as many mobile apps as you want – and even sell the apps if this is your thing.
This tutorial provides a step-by-step howto for using CoreLocation in an iPhone app.
When you’ve finished the tutorial you’ll have created a simple, single-view app with a label that will be updated to show the location. Essentially, you’ll create a simplified version of Apple’s LocateMe sample app, which was the basis for this code.
This tutorial, combined with our earlier Apple Approved Inter-Process Communication post, accounts for all of the inner workings of Alocola, our Safari location helper.
Source/Github
The code for this tutorial is available in GitHub. If you want to follow along in the code, clone the repository:
- Open a terminal and change to the directory where you want the code
- Clone the repo by typing git clone git://github.com/dcgrigsby/hellothere.git
I’ve made two separate commits to the repository to match the progress of the tutorial. If you’re following along step-by-step you’ll want to revert to the earlier commit:
- Type git checkout 655a84c654e25a1ff38492ab61f5e89bd73032bb
Creating The Project
Launch Xcode and create a new View-Based iPhone application called HelloThere:
- Create a new project using File > New Project… from Xcode’s menu
- Select View-Based Application from the iPhone OS > Application section, click Choose…
- Name the project as HelloThere and click Save
Core Location Fundamentals
Core Location is event based. The phone triggers an update event when Core Location is running and a new location becomes available. To use Core Location, you configure your application to receive these update events.
Applications configure where update events are sent by creating an instance of the CLLocationManager class, assigning a CLLocationManagerDelegate — a class instance that will receive the location update events — to it, and enabling location updates.
Creating A Core Location Controller Class
We’ll start by adding the CoreLocation framework to the project:
- In the Groups & Files panel of the project, expand the HelloThere project branch
- Control-click or right-click the Frameworks folder
- Choose Add > Existing Frameworks…
- Expand the Frameworks folder
- Choose CoreLocation.framework and click Add
- Click Add once more
Next, import the CoreLocation headers into the project in HelloThere_Prefix.pch:
- In the Groups & Files panel of the project, expand the HelloThere project branch
- Expand the Other Sources folder
- Click HelloThere_Prefix.pch
- Update the file to include a CoreLocation import:
#ifdef __OBJC__ #import <Foundation/Foundation.h> #import <UIKit/UIKit.h> #import <CoreLocation/CoreLocation.h> #endif
Now, we’ll create a class to handle location events. Later, we’ll wire the location data provided by this class into our view.
Add a new class called MyCLController to the project:
- In the Groups & Files panel of the project, expand the HelloThere project branch
- Control-click or right-click the Classes folder
- Choose Add > New File…
- Select NSObject subclass from the iPhone OS &bt; Cocoa Touch Classes section, click Next…
- Name the class MyCLController.m and click Finish
Use this code for MyCLController.h:
@interface MyCLController : NSObject <CLLocationManagerDelegate> { CLLocationManager *locationManager; } @property (nonatomic, retain) CLLocationManager *locationManager; - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation; - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error; @end
This .h file: (1) labels our class as adhering to CLLocationManagerDelegate protocol, which enables it to receive location event call backs; (2) contains an instance of the CLLocationManager class — this will be used to register itself as a delegate for events and to activate location updates.
Finally, we’ll finish up the class by adding its implementation:
#import "MyCLController.h" @implementation MyCLController @synthesize locationManager; - (id) init { self = [super init]; if (self != nil) { self.locationManager = [[[CLLocationManager alloc] init] autorelease]; self.locationManager.delegate = self; // send loc updates to myself } return self; } - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation { NSLog(@"Location: %@", [newLocation description]); } - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error { NSLog(@"Error: %@", [error description]); } - (void)dealloc { [self.locationManager release]; [super dealloc]; } @end
The init
method: (1) initializes the location manager and (2) tells the location manager to send updates to this class.
locationManager:didUpdateToLocation:fromLocation
and locationManager:didFailWithError
are the callback methods that will be triggered by location events. For now, they just add output to the console/log.
Using The Core Location Controller Class
Now that we’ve got a class that should be able to receive updates we ought to see if it works. We’ll add an instance of it to our View Controller and activate it after the view launches.
Use this code for HelloThereViewController.h:
#import <UIKit/UIKit.h> #import "MyCLController.h" @interface HelloThereViewController : UIViewController { MyCLController *locationController; } @end
Use this code for HelloThereViewController.m:
#import "HelloThereViewController.h" @implementation HelloThereViewController - (void)viewDidLoad { locationController = [[MyCLController alloc] init]; [locationController.locationManager startUpdatingLocation]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } - (void)dealloc { [locationController release]; [super dealloc]; } @end
The viewDidLoad
method (1) allocates and initializes the location controller instance and (2) actives the location updates by sending the startUpdatingLocation
message to the location manager.
Run the application and watch the console for location messages:
- Run the project using Build > Build and Run from Xcode’s menu
- Show the console using Run > Console from Xcode’s menu
The app will log location events to the console. For best results, run this on an iPhone and not the sim; the sim doesn’t provide real location data and, as far as I’ve ever been able to tell, only ever has one update event.
Before continuing: if you’re following along in the source, and you rolled back to the earlier version, you’ll want to roll forward to the final version:
- From the terminal, type git checkout 785b46b337d3b0ecbc5d55b770fe6adf4e536d07
Making The Core Location Controller Class Useful
At this point, our core location controller class isn’t of much use. Outputting the updates to the console was kind of a punt to get us started. Let’s fix that now by updating MyCLController to push location change events back to the class that instantiated it. We’ll ultimately configure our view controller to receive these updates to update the app’s display.
To accomplish this, we’ll define a delegate protocol. It will work like this: a class (e.g., our view) can implement the protocol and register itself as the delegate. When location events occur, the MyCLController instance will trigger methods in the delegate (e.g., our view).
Update the MyCLController.h file. Additions are shown in bold:
@protocol MyCLControllerDelegate @required - (void)locationUpdate:(CLLocation *)location; - (void)locationError:(NSError *)error; @end @interface MyCLController : NSObject { CLLocationManager *locationManager; id delegate; } @property (nonatomic, retain) CLLocationManager *locationManager; @property (nonatomic, assign) id delegate; - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation; - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error; @end
The code sets up our delegate protocol. It has two required methods: one for a location update event and another for an error. For our view controller to receive location updates it will have to implement those two methods. But we’re getting ahead of ourselves.
The code also ads a delegate
member. As we’ll see in a bit, this will allow our view controller to register itself to receive location updates.
Update MyCLController.m; changes are bold:
#import "MyCLController.h" @implementation MyCLController @synthesize locationManager; @synthesize delegate; - (id) init { self = [super init]; if (self != nil) { self.locationManager = [[[CLLocationManager alloc] init] autorelease]; self.locationManager.delegate = self; // send loc updates to myself } return self; } - (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation { [self.delegate locationUpdate:newLocation]; } - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error { [self.delegate locationError:error]; } - (void)dealloc { [self.locationManager release]; [super dealloc]; } @end
Displaying Location Updates
We’ll conclude this tutorial by modifying our view controller to support the MyCLControllerDelegate prototocol and then use the location callbacks up update a label that we’ll add to the view.
First, let’s add the label that will show the location to the view. Begin by updating HelloThereViewController.h. Additions shown in bold:
#import <UIKit/UIKit.h> #import "MyCLController.h" @interface HelloThereViewController : UIViewController { IBOutlet UILabel *locationLabel; MyCLController *locationController; } @end
Adding the line above provides us with a mechanism for hooking into the label element we’ll add next in Interface Builder. IBOutlet
is a macro that tells the compiler that it needs to wire up this variable to a corresponding UILabel
element added WYSIWYG in Interface Builder. In the next step, we’ll add that element and connect the two pieces.
Edit the HelloThereViewController.xib file in Interface Builder:
- In the Groups & Files panel of the project, expand the HelloThere project branch
- Control-click or right-click the Resources folder
- Double-click the HelloThereViewController.xib file
Make sure that the Library, Inspector and View windows are open/visible. If they are not:
- Show the Library window using Tools > Library from the menu
- Show the Inspector window using Tools > Inspector from the menu
- Show the View by clicking the View icon on the HelloThereViewController.xib window
Add the label:
- Locate the Label component in the Library window and drag it onto the view
- In the View window use the label’s handles to enlarge it to about half the size of the view
- In the Inspector window under View Attributes (the left-most tab), set the label’s number of lines to 0.
Setting the label’s number of lines to zero configures the label dynamically size the text to fit within its bounds.
Connect the Interface Builder label the code’s locationLabel
. While still in Interface Builder:
- Control-click on File’s Owner icon in the HelloThereViewController.xib window
- In the resulting pop-up menu, click-and-hold (i.e., don’t unclick) on the right-justified circle in the locationLabel row of the Outlets section
- Drag the mouse to the Label in the View. A blue line will connect the two.
Confirm that the two have been connected. The pop-up menu should look like the image on the right. If everything looks right, save the changes and close Interface Builder.
Finally, we’ll need to update the HelloThereViewController to implement the MyCLControllerDelegate protocol and set the label to the location. Update the HelloThereController.h file. Changes are shown in bold:
#import <UIKit/UIKit.h> #import "MyCLController.h" @interface HelloThereViewController : UIViewController <MyCLControllerDelegate> { IBOutlet UILabel *locationLabel; MyCLController *locationController; } - (void)locationUpdate:(CLLocation *)location; - (void)locationError:(NSError *)error; @end
Update the HelloThereController.m file. Changes are shown in bold:
#import "HelloThereViewController.h" @implementation HelloThereViewController - (void)viewDidLoad { locationController = [[MyCLController alloc] init]; locationController.delegate = self; [locationController.locationManager startUpdatingLocation]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } - (void)dealloc { [locationController release]; [super dealloc]; } - (void)locationUpdate:(CLLocation *)location { locationLabel.text = [location description]; } - (void)locationError:(NSError *)error { locationLabel.text = [error description]; } @end
By implementing the MyCLControllerDelegate protocol and registering itself as the delegate, our view controller sets itself up to receive location updates. When its locationUpdate
method is fired, it updates the label in our view to show the current location.
Conclusion
Build and run the project and enjoy!
I’m trying your walkthrough but do not see a CoreLocation.framework.
I see: CoreAudio.framework, CoreAudioKit.framework, CoreData…, CoreFoundation…, CoreMIDI…, CoreMidiServer…, CoreServices…, CoreVideo…
I have Xcode 3.1.2 that I downloaded last weekend.
I installed the 2.2.1 SDK and CoreLocation.framework now shows up under “Add Existing Framework”
Thanks for your blog….
Its very nice tutorial. I got the basic behind the Location API. For start up its good enough.
Thx
Thank you VERY much for this fantastic tutorial!!
Merci beaucoup pour ce tutorial: il m’a fait gagner beaucoup de temps!
(Thanks a lot for this tutorial: it saved me a lot of time!)
Dan!
Thank you! Thank you! Thank you! I was deep into deving my latest game (Jetman Extreme Velocity) when, for the first time, I needed a Lat/Long, and REALLY didn’t want to spend the time in a diversion from my focus (the game!) to wrap my head around CoreLocation. You saved me! This tutorial is PERFECT.
(Hey! Did I mention I was grateful?)
Until it’s released, you can see the demo of Jetman on
Thx again,
BF
Thanks! This is the best tutorial on using CoreLocation I have read so far. Thanks!
good tutorial, but i have a question have somebody noticed that oldLocation is the same as newLocation at least 2 times in a raw, so it looks like the location provided is not very accurate althought set to the Best accuracy?
Thank you for the great tutorial. I’m curious whether it would be possible to write a simple console application (e.g. on a Jailbroken iPhone) so that it would use the CoreLocation service and print out the coordinate in the terminal as opposed to the UI. Thank you.
Hi All!
After build all i have encountered this exception:
/Users/bongio/Documents/Sviluppo Iphone/HelloThere/Classes/MyCLController.m:35: error: synthesized property ‘delegate’ must either be named the same as a compatible ivar or must explicitly name an ivar
help me!
Please
Hi,
Its a nice tutorial,
I have one question
while creating locationManager instance variable you are doing autorelease
than again in dealloc you are doing
[self.locationManager release];
will it not throw double free exception.
?????
Hi,
Thanks for the nice tutorial.
Ramesh.
Hi, thanks for the tutorial….one question. I am using the iphone simulator and do not get any updates…not even one… ?????
The best tutorial on the Core Location FW by far. Period. Thanks a bunch 🙂
Thank you for this usefull tutorial.
To Lee : the simulator doesn’t get a GPS, so no update 🙂
Hello Sir,
I wish to say you very much thanks for this best tutorial.
sir i embedded your code in my existing project but i am facing one problem i wish to store that description in a variable so that i can access into my whole project wherever i need .i m making its object instead of viewdidload on button click of my project. And i wish to access that particular description in a global string variable.
Please help me.
thanks for this good tuto!
Thanks, tutorial rocks…Keep it good work..
Nice one man …. keep posting many examples like this !!!!
ROCK ON!!!!!
Thanks!! This is a great tutorial!
For everyone…:
What do you thing would happen if you change the MyCLController’s delegate somewhere in the timeline?
(For example, set the delegate, then for some reason 5 seconds later the deleage is changed.)
Well, since position (latitude and longitude) is not changed (suppose you are not moving or not significantly moving) the second delegate method locationUpdate: will never be called!
Hence you will not have a location if you change the delegate.
I suggest to “fetch” last location from MyCLController when delegate is being set. This will ensure you have some valid data in you Location
Thanks again
AWESOME!!! Thank you very much! =D
Hi, thank you so much for this stupend thread!!…
i have add this line in the init method:
self.locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
i know(think) there are several kCLLocationAccuracy constant to define when the locationUpdate is call, like(kCLLocationAccuracyHundredMeters, kCLLocationAccuracyBest, etc..)..
Does anyone know how many times(in second) is called the locationUpdate function?
P.S: sorry for my bad english!!
Hi, thanks for the tutorial. I noticed something in the code or actually when testing it in Xcode. I got a warning saying that the MyCLController didn’t implement the CLLocationManagerDelegate.
‘MyCLController’ does not implement the ‘CLLocationManagerDelegate’ protocol
So I think the correct header for the interface should be:
@interface MyCLController : NSObject
can anybody help me?
this is a perfect tutorial, but i need a programm which sends now the location to a webserver. i’am a absolutely newbie.
thanks for an answer.
lg
Hi I was just wondering how would I get the iPhone to send this data to my server like every 10 seconds?
@Russell.. if you have an answer for your problem please let me know, because i also have to know this.
greets church
Great tutorial! I really enjoyed it and found the explanations good and clear and easy to follow. Thanks!
Great tutorial
Thank you so much for this. It’s exactly what I was looking for.
Hello,
thank you so much for this tutorial.
but, i have an error, on “MyCLController.m” :s
".objc_class_name_CLLocationManager", referenced from:
literal-pointer@__OBJC@__cls_refs@CLLocationManager in MyCLController.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
I have found my problem.
I have add “CoreLocation.framework” on my project
THANK YOU for the tutorial ^^
Great tutorial! I love how you isolated the functionality in the delegate
I’m new to iPhone coding and objective-C and have been banging my head against the wall trying to figure out CL for HOURS and in 5 minutes you cleared everything up for me! My code now works perfectly! THANKS!
Thanks for this tutorial. I just have a question. Is there any way to use location data from terminal? I mean i want to build a command-line tool that returns the position without having to use any UI. Thanks
@church, @russell
You could POST the data something like
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation;
[NSMutableURLRequest* urlRequest = [NSMutableURLRequest requestWithURL:@"https://www.yoursite.com/yourfile.php"];
[urlRequest setHTTPMethod:@"POST"];
[urlRequest setHTTPBody:@"lat=%f&lng=%f",newlocation.latitude, newlocation.longitude];
[[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
well, I mean:
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation{
[NSMutableURLRequest* urlRequest = [NSMutableURLRequest requestWithURL:@"https://www.yoursite.com/yourfile.php"];
[urlRequest setHTTPMethod:@"POST"];
[urlRequest setHTTPBody:@"lat=%f&lng=%f",newlocation.latitude, newlocation.longitude];
[[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];}
Thank you VERY much for this fantastic tutorial!!