Archive for February, 2010


Fun with CoreLocation

The other day I was just getting into using CoreLocation on iPhone OS, and I found a great tutorial involving making a driver class to add a level of abstraction between CLLocationManager and your code. CLLocationManager is kind of complex to a first time user, and my class simplifies it a bit. You simply implement the protocol with two callbacks (location updated, error reporting), set your interface as a delegate for my driver, and let the events start flowing. N.B: Requires your app to reference the CoreLocation framework.

WLLocationDriver.h:

//
//  WLLocationDriver.h
//
//  Created by William LaFrance on 2/23/10.
//  Public Domain
//

/**
 * Protocol for any interface being used as a delegate to WLLocationDriver.
 * This greatly simplifies using a CLLocationManager.
 */
@protocol WLLocationDriverDelegate
    @required
    - (void)locationUpdate:(CLLocation *)location;
    - (void)locationError:(NSError *)error;
@end

/**
 * A simple location driver. Excecutes two simple events: one for location
 * changes, and one for errors.
 *
 * Usage:
    locationDriver = [[WLLocationDriver alloc] init];
    locationDriver.delegate = self;
 */
@interface WLLocationDriver : NSObject  {
    CLLocationManager *locationManager;
    id delegate;
}

/**
 * The CLLocationManager we're getting events from
 */
@property (nonatomic, retain) CLLocationManager *locationManager;

/**
 * The delegate we're sending our own events to
 */
@property (nonatomic, assign) id delegate;

/**
 * Called by the location manager every time a location update occurs (ie, a new
 * GPS fix).
 */
- (void)locationManager:(CLLocationManager *)manager
        didUpdateToLocation:(CLLocation *)newLocation
        fromLocation:(CLLocation *)oldLocation;

/**
 * Called by the location manager every time a location update error occurs,
 * such as not being able to get a GPS fix.
 */
- (void)locationManager:(CLLocationManager *)manager
        didFailWithError:(NSError *)error;

/**
 * Easy access to the devices current location
 */
- (CLLocation *)getCurrentLocation;

@end

WLLocationDriver.m:

//
//  WLLocationDriver.m
//
//  Created by William LaFrance on 2/23/10.
//  Public Domain
//

#import "WLLocationDriver.h"

/**
 * A simple location driver. Excecutes two simple events: one for location
 * changes, and one for errors.
 *
 * Usage:
    locationDriver = [[WLLocationDriver alloc] init];
    locationDriver.delegate = self;
 */
@implementation WLLocationDriver

@synthesize locationManager;
@synthesize delegate;

/**
 * Initializes the location driver, constructs the location manager, and begins
 * to get event updates.
 */
- (id) init {
    self = [super init];

    if (self != nil) {
        self.locationManager = [[[CLLocationManager alloc] init] autorelease];
        self.locationManager.delegate = self;
        [self.locationManager startUpdatingLocation];
    }

    return self;
}

/**
 * Called by the location manager every time a location update occurs (ie, a new
 * GPS fix).
 */
- (void)locationManager:(CLLocationManager *)manager
        didUpdateToLocation:(CLLocation *)newLocation
        fromLocation:(CLLocation *)oldLocation
{
    if (delegate != nil)
        [self.delegate locationUpdate:newLocation];
    else
        NSLog(@"%@", @"Failed to report location -- missing delegate.");
}

/**
 * Called by the location manager every time a location update error occurs,
 * such as not being able to get a GPS fix.
 */
- (void)locationManager:(CLLocationManager *)manager
        didFailWithError:(NSError *)error
{
    if (delegate != nil)
        [self.delegate locationError:error];
    else
        NSLog(@"%@", @"Failed to report error -- missing delegate.");
}

/**
 * Easy access to the devices current location
 */
- (CLLocation *)getCurrentLocation {
    return [locationManager location];
}

/**
 * Clean up the mess we made
 */
- (void)dealloc {
    [self.locationManager release];
    [super dealloc];
}

@end

To use this, create a new iPhone app, add a WLLocationDriver to your classes interface, and stick these methods in your app delegate.

- (void)viewDidLoad {
    [super viewDidLoad];

    locationDriver = [[WLLocationDriver alloc] init];
    locationDriver.delegate = self;
}

- (void)locationUpdate:(CLLocation *)location {
    NSLog(@"%@", [location description]);
}

- (void)locationError:(NSError *)error {
    NSLog(@"%@", [error description]);
}

Enjoy!

Advertisements

I recently started playing with Core Location programming on my iPod Touch (which is way cool, by the way — GPS location via Wifi), and wanted to plot my location on a map using MapKit. Centering on your position is easy.. just construct a MKMapView object and call it’s setCenterCoordinate method every time the location driver tells me I’m at a new location.

However, when it came to adding a pin (called a map annotation) on your exact coordinates was more difficult. The addAnnotation method requires an <MKAnnotation> to be passed to it, which is not an object but a protocol. In my hour long search, I couldn’t find a bare bones implementation of that protocol anywhere in MapKit, so I decided to reinvent the wheel. Hopefully Google catches this before any other developers have to do the same. I present to you, WLSimpleMapAnnotation! N.B.: Requires your project to reference the MapKit framework.

WLSimpleMapAnnotation.h:

//
//  WLSimpleMapAnnotation.h
//
//  Created by William LaFrance on 2/23/10.
//  Public Domain
//

#import <Foundation/Foundation.h>

@interface WLSimpleMapAnnotation : NSObject <MKAnnotation< {
    CLLocationCoordinate2D _coordinate;
    NSString * _title;
    NSString * _subtitle;
}

+ (id) initWithCoordinate:(CLLocationCoordinate2D)coordinate;
+ (id) initWithCoordinate:(CLLocationCoordinate2D)coordinate andTitle:(NSString *) title;
+ (id) initWithCoordinate:(CLLocationCoordinate2D)coordinate andTitle:(NSString *) title andSubtitle:(NSString *)subtitle;

- (NSString *)title;
- (NSString *)subtitle;

@end

WLSimpleMapAnnotation.m:

//
//  WLSimpleMapAnnotation.m
//
//  Created by William LaFrance on 2/23/10.
//  Public Domain
//

#import "WLSimpleMapAnnotation.h"

@implementation WLSimpleMapAnnotation

@synthesize coordinate = _coordinate;

+ (id) initWithCoordinate:(CLLocationCoordinate2D)coordinate {
    self = [super alloc];
    _coordinate = coordinate;
    return self;
}

+ (id) initWithCoordinate:(CLLocationCoordinate2D)coordinate andTitle:(NSString*) title {
    self = [super alloc];
    _coordinate = coordinate;
    _title = [title retain];
    return self;
}

+ (id) initWithCoordinate:(CLLocationCoordinate2D)coordinate andTitle:(NSString*) title andSubtitle:(NSString*) subtitle {
    self = [super alloc];
    _coordinate = coordinate;
    _title = [title retain];
    _subtitle = [subtitle retain];
    return self;
}

- (NSString *)title {
    return _title;
}

- (NSString *)subtitle {
    return _subtitle;
}

-(void) dealloc {
    [_title release];
    [_subtitle release];
    [super dealloc];
}

@end

Unfortunately, this code suffers a few compiler warnings in the constructors, but otherwise works completely fine. I’ve decided to make this code public domain (do whatever you want with it, but please don’t use it to control nuclear weapons), because it’s stupidly simple, but just tedious. Enjoy!

EDIT: Fixed the header to extend <MKAnnotation>.

Hello. My name is William LaFrance. I’m an information technology student at Madison Area Technical College in Madison, Wisconsin. This blog will be rather technical, involving posts revolving around new technologies, and my adventures through current ones. Enjoy!