Archive for 16th January 2013

A better way to launch the Maps app for navigation on iOS 6

Back in the days when Google actually ruled Maps on the iOS platform (5.x and older), you had to push a URL through UIApplication’s sharedApplication object, and the system would see the  Google maps URL and intercept it, sending the data to Maps instead of Safari.

Well this kind of works in iOS 6 as well, substituting maps.apple.com for maps.google.com. But alas, there is a better way.

Here is the code that I am now using to launch Maps, and it works in both iOS 6 and iOS 5.

- (void)launchMaps:(NSString *)serviceAddress
{
    NSString *currentLocation = @"Current+Location";
 
    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"6.0"))
    {
        // use MapKit to handle launching Maps app instead of pushing a URL
        CLGeocoder *geocoder = [[CLGeocoder alloc] init];
        [geocoder geocodeAddressString:serviceAddress completionHandler:^(NSArray *placemarks, NSError *error) {
            if (error)
            {
                NSLog(@"Geocode failed with error: %@", error.localizedDescription);
                return;
            }
 
            if (placemarks && placemarks.count > 0)
            {
                CLPlacemark *pl = placemarks[0];
                CLLocation *loc = pl.location;
                NSLog(@"Geocoded latitude: %f; longitude: %f", loc.coordinate.latitude, loc.coordinate.longitude);
                MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate:loc.coordinate addressDictionary:nil];
                MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:placemark];
                mapItem.name = @"Whatever name you want goes here";
                mapItem.phoneNumber = @"And if you have a phone number, it would go here";
                NSArray *mapItemArray = @[ mapItem ];
                NSDictionary *launchOpt = @{ MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving };
                [MKMapItem openMapsWithItems:mapItemArray launchOptions:launchOpt];
                [mapItem release];
                [placemark release];
            }
        }];
 
        [geocoder release];
        return;
    }
 
    NSString *urlString = [NSString stringWithFormat:@"http://maps.google.com/maps?saddr=%@&daddr=%@", currentLocation, serviceAddress];
    NSString *escaped = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:escaped]];
}

And here are the defines for the OS checking macros that I use. (Yes, I know you should check for specific features instead of checking OS version numbers.)

#define SYSTEM_VERSION_EQUAL_TO(v)                  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v)              ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v)                 ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v)     ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)

The address string passed in would just be the components of your address separated by spaces (such as “1060 W Addison St Chicago IL”), and the Maps app should open up and give you the opportunity to start navigation to the destination.

BTW, Happy Birthday to Frank Zamboni, who is now forever immortalized with a Google Doodle.