Knowing the latitude and longitude of your users can open up all kinds of possibilities in your apps. In an upcoming post, we’ll be discussing how you can use your user’s location to determine their local weather conditions and forecast. But for now, we’re going to focus on Part 1 of this two part tutorial: CoreLocation.
Apple’s done a great job of abstracting GPS, Cellular Triangulation, and Wifi Access Point location lookups into CoreLocation; making it extremely easy to determine the approximate location of your user regardless of their device and network connectivity. Additionally, Apple’s new iPhone Simulator finally supports CoreLocation as well. This significantly eases the process of testing your location code.
The first thing you’ll need to do is add the CoreLocation framework to your project. This is pretty straightforward. Command-Click on ‘Frameworks’ -> Add -> Existing Frameworks
Select ‘CoreLocation.framework’ and click ‘add’.
The class below implements the LocationManager delegate methods required to get the user’s location. You can just as easily implement the LocationManagerDelegate protocol in your AppDelegate or elsewhere in your App. Though I’ve found that having this class makes it super easy to drop in location support into new projects instead of having to cut/paste delegate methods (ugly). Anyway. Take a look:
// // LocationGetter.h // CoreLocationExample // // Created by Matt on 9/3/10. // Copyright 2009 iCodeBlog. All rights reserved. // #import <UIKit/UIKit.h> #import <CoreLocation/CoreLocation.h> @protocol LocationGetterDelegate <NSObject> @required - (void) newPhysicalLocation:(CLLocation *)location; @end @interface LocationGetter : NSObject <CLLocationManagerDelegate> { CLLocationManager *locationManager; id delegate; } - (void)startUpdates; @property (nonatomic, retain) CLLocationManager *locationManager; @property(nonatomic , retain) id delegate; @end
Notice that we’re defining our own protocol with a method that takes the new CLLocation as a parameter. We’ll be implementing that delegate method in a minute. Now for the class
// LocationGetter.m // CoreLocationExample // // Created by Matt on 9/3/10. // Copyright 2009 iCodeBlog. All rights reserved. // #import "LocationGetter.h" #import <CoreLocation/CoreLocation.h> @implementation LocationGetter @synthesize locationManager, delegate; BOOL didUpdate = NO; - (void)startUpdates { NSLog(@"Starting Location Updates"); if (locationManager == nil) locationManager = [[CLLocationManager alloc] init]; locationManager.delegate = self; // locationManager.distanceFilter = 1000; // update is triggered after device travels this far (meters) // Alternatively you can use kCLLocationAccuracyHundredMeters or kCLLocationAccuracyHundredMeters, though higher accuracy takes longer to resolve locationManager.desiredAccuracy = kCLLocationAccuracyKilometer; [locationManager startUpdatingLocation]; } - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"Your location could not be determined." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil]; [alert show]; [alert release]; } // Delegate method from the CLLocationManagerDelegate protocol. - (void)locationManager:(CLLocationManager *)manage didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation { if (didUpdate) return; didUpdate = YES; // Disable future updates to save power. [locationManager stopUpdatingLocation]; // let our delegate know we're done [delegate newPhysicalLocation:newLocation]; } - (void)dealloc { [locationManager release]; [super dealloc]; } @end
CoreLocation gives you a few options for the accuracy of the user’s location. The more accurate the measurement, typically the longer it takes LocationManager to call it’s delegate method didUpdateToLocation. It’s just something to keep in mind when deciding what level of accuracy to use.
Next we need to actually invoke this code and start getting location updates. I usually do this in my AppDelegate’s didFinishLaunchingWithOptions, though you could also do it in viewDidLoad somewhere if you didn’t need to know the user’s location on app startup.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; spinner.center = CGPointMake(self.viewController.view.frame.size.width / 2, self.viewController.view.frame.size.height / 2); [spinner startAnimating]; [viewController.view addSubview:spinner]; // get our physical location LocationGetter *locationGetter = [[LocationGetter alloc] init]; locationGetter.delegate = self; [locationGetter startUpdates]; // Add the view controller's view to the window and display. [window addSubview:viewController.view]; [window makeKeyAndVisible]; return YES; }
Notice that I’ve set locationGetter’s delegate to self. So in your .h, make sure to add LocationGetterDelegate to the interface.
// // CoreLocationExampleAppDelegate.h // CoreLocationExample // // Created by Matt Tuzzolo on 9/3/10. // Copyright iCodeBlog 2010. All rights reserved. // #import <UIKit/UIKit.h> #import "LocationGetter.h" @class CoreLocationExampleViewController; @interface CoreLocationExampleAppDelegate : NSObject <UIApplicationDelegate, LocationGetterDelegate> { UIWindow *window; CoreLocationExampleViewController *viewController; CLLocation *lastKnownLocation; } @property (nonatomic, retain) IBOutlet UIWindow *window; @property (nonatomic, retain) IBOutlet CoreLocationExampleViewController *viewController; @property (nonatomic, retain) CLLocation *lastKnownLocation; @end
I’ve also added a CLLocation *lastKnownLocation that we’ll use in our delegate method (which comes next):
# pragma mark - # pragma mark LocationGetter Delegate Methods - (void)newPhysicalLocation:(CLLocation *)location { // Store for later use self.lastKnownLocation = location; // Remove spinner from view for (UIView *v in [self.viewController.view subviews]) { if ([v class] == [UIActivityIndicatorView class]) { [v removeFromSuperview]; break; } } // Alert user UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Location Found" message:[NSString stringWithFormat:@"Found physical location. %f %f", self.lastKnownLocation.coordinate.latitude, self.lastKnownLocation.coordinate.longitude] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil]; [alert show]; [alert release]; // ... continue with initialization of your app }
This last piece takes care of storing the location, removing the spinner, and firing off an alert. If this was your code, you’d probably want to re-enable any UI elements that you’ve disabled and let the user start using your app.
For those of you who don’t know me yet, my name is Matt Tuzzolo (@matt_tuzzolo). This is my first iCodeBlog post; with many more to come.
Here’s the Example Project for this post. Enjoy!