Monday 19 September 2011

iOS Core Location GPS

In this tutorial you will learn how to access the raw Core Location data, including the iPhone’s GPS coordinates and speed of travel. We will be creating a project that incorporates the Core Location framework. We will implement a custom class that handles our Core Location and sets up a custom protocol for other classes to adhere to in order to receive messages with our Core Location data updates. You are expected to have a decent understanding of Objective-C.
First thing we must create a new iOS project. In Xcode, click (File->New Project). Under (iPhone OS->Application) select View-based Application and save it as CoreLocationDemo or something similar.
Right click on the “Frameworks” group and select (Add->Existing Frameworks). Select CoreLocation.framework from the list of available frameworks. We must manually do this in order to access the Core Location methods, Xcode does not automatically know to do use this framework and you will receive many errors when compiling the code.
We need to create a class to act as a Core Location Manager Delegate. This class will receive messages from the Core Location framework containing the raw data and will then pass this to our view controller class later on.
Create a new file (File->New File) and create a new Objective-C class (iPhone OS-> Cocoa Touch Class-> Objective-C class) and make sure it is a subclass of NSObject. Name it something like CoreLocationController and make sure “Also create .h file” is checked.
A lot of things need to happen in this class, but luckily it isn’t much code.
First, we need to setup some things in our .h file. Before anything, we must include our Core Location header file or everything will break on build.
From CoreLocationController.h
#import <CoreLocation/CoreLocation.h>
We will declare a new protocol that will allow anything using our class to adhere to and receive updated Core Location data. For that, we use the following code placed above our interface declaration and below our imports:
From CoreLocationController.h
@protocol CoreLocationControllerDelegate 
@required
- (void)locationUpdate:(CLLocation *)location; // Our location updates are sent here
- (void)locationError:(NSError *)error; // Any errors are sent here
@end
Now we must define our class and make it adhere to the CLLocationManagerDelegate protocol:
From CoreLocationController.h
@interface CoreLocationController : NSObject <CLLocationManagerDelegate> {
	CLLocationManager *locMgr;
	id delegate;
}
 
@property (nonatomic, retain) CLLocationManager *locMgr;
@property (nonatomic, assign) id delegate;
 
@end
We define an instance variable of CLLocationManager type named locMgr.
Apple’s description of CLLocationManager:
The CLLocationManager class defines the interface for configuring the delivery of location- and heading-related events to your application. You use an instance of this class to establish the parameters that determine when location and heading events should be delivered and to start and stop the actual delivery of those events. You can also use a location manager object to retrieve the most recent location and heading data.
We also define “delegate” if type “id” which allows any class implementing our CoreLocationControllerDelegate protocol to register itself as the delegate.
In the .m file of our new class we need to synthesize our instance variables and create three methods.
First the variables:
From CoreLocationController.m
@synthesize locMgr, delegate;
Now the methods, the first being “init” which will create a new instance of our CLLocationManager and set the delegate as “self.” I am not going into how the rest works, you should be up to speed on Objective-C before reading this tutorial.
From CoreLocationController.m
- (id)init {
	self = [super init];
 
	if(self != nil) {
		self.locMgr = [[[CLLocationManager alloc] init] autorelease]; // Create new instance of locMgr
		self.locMgr.delegate = self; // Set the delegate as self.
	}
 
	return self;
}
The next two methods are callbacks from the CLLocationManagerDelegate protocol. They receive updates of Core Location data such as our GPS coords and speed of travel, as well as handle error messages. All we are doing is passing the received data to the whatever class registered itself as the delegate. Nothing more. If no class registered as the delegate, they do nothing. Finally, we have delloc that cleans up and releases locMgr.
From CoreLocationController.m
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
	if([self.delegate conformsToProtocol:@protocol(CoreLocationControllerDelegate)]) {  // Check if the class assigning itself as the delegate conforms to our protocol.  If not, the message will go nowhere.  Not good.
		[self.delegate locationUpdate:newLocation];
	}
}
 
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
	if([self.delegate conformsToProtocol:@protocol(CoreLocationControllerDelegate)]) {  // Check if the class assigning itself as the delegate conforms to our protocol.  If not, the message will go nowhere.  Not good.
		[self.delegate locationError:error];
	}
}
 
- (void)dealloc {
	[self.locMgr release];
	[super dealloc];
}
Open up the view controller .h file and import the core location class .h file. It is important for the view controller to adhere to our newly created protocol. Define an instance variable of CoreLocationController type as well as an IBOutlet for a UILabel to display the data.
CoreLocationDemoViewController.h
#import <UIKit/UIKit.h>
 
#import "CoreLocationController.h"
 
@interface CoreLocationDemoViewController : UIViewController <CoreLocationControllerDelegate> {
	CoreLocationController *CLController;
	IBOutlet UILabel *locLabel;
}
 
@property (nonatomic, retain) CoreLocationController *CLController;
 
@end
Open the view controller nib file in interface builder and create a new UILabel. Ctrl-Click-Drag from File’s Owner to the UILabel and select the IBOutlet we just created. Save and quit interface builder.
Now on to our view controller .m file.
In the viewDidLoad method initialize CLController, set CLController’s delegate as “self,” and call locMgr’s method startUpdatingLocation. The first of the three should be self explanatory, no need to go into why. If you’re confused, you need to brush up on your Objective-C then come back here. The second is to assign our view controller as the delegate in our CoreLocationController class. If you haven’t already figured it out yet, I will explain it. If you remember in our CoreLocationController.m file we used [self.delegate doSomething]. In the init of that file we set the delegate to itself as to prevent a call to nothing, so basically you have a method recursively calling itself until we set the delegate as our view controller. Of course that means we now must implement locationUpdate:newLocation and locationError:error in our view controller .m file. In these two methods we will set the UILabel text to the “description” property of the CLLocation and NSError. We can go and pick out individual details from the CLLocation but for now we will just print out the whole thing as one long semi-garbled string which is what “description” contains.
From CoreLocationDemoViewController.m
- (void)viewDidLoad {
    [super viewDidLoad];
 
	CLController = [[CoreLocationController alloc] init];
	CLController.delegate = self;
	[CLController.locMgr startUpdatingLocation];
}
 
- (void)locationUpdate:(CLLocation *)location {
	locLabel.text = [location description];
}
 
- (void)locationError:(NSError *)error {
	locLabel.text = [error description];
}
 
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}
Of course it wouldn’t be complete with proper memory management:
From CoreLocationDemoViewController.m
- (void)dealloc {
	[CLController release];
    [super dealloc];
}
And that’s it! Compile and run on your iPhone. Try walking down the street, you can see the data update in real time.

No comments:

Post a Comment