159

Previous to iOS 6, opening a URL like this would open the (Google) Maps app:

NSURL *url = [NSURL URLWithString:@"http://maps.google.com/?q=New+York"];
[[UIApplication sharedApplication] openURL:url];

Now with the new Apple Maps implementation, this just opens Mobile Safari to Google Maps. How can I accomplish the same behavior with iOS 6? How do I programmatically open the Maps app and have it point to a specific location/address/search/whatever?

nevan king
  • 112,709
  • 45
  • 203
  • 241
Tom Hamming
  • 10,577
  • 11
  • 71
  • 145

12 Answers12

281

Here's the official Apple way:

// Check for iOS 6
Class mapItemClass = [MKMapItem class];
if (mapItemClass && [mapItemClass respondsToSelector:@selector(openMapsWithItems:launchOptions:)]) 
{
    // Create an MKMapItem to pass to the Maps app
    CLLocationCoordinate2D coordinate = 
                CLLocationCoordinate2DMake(16.775, -3.009);
    MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate:coordinate 
                                            addressDictionary:nil];
    MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:placemark];
    [mapItem setName:@"My Place"];
    // Pass the map item to the Maps app
    [mapItem openInMapsWithLaunchOptions:nil];
}

If you want to get driving or walking instructions to the location, you can include a mapItemForCurrentLocation with the MKMapItem in the array in +openMapsWithItems:launchOptions:, and set the launch options appropriately.

// Check for iOS 6
Class mapItemClass = [MKMapItem class];
if (mapItemClass && [mapItemClass respondsToSelector:@selector(openMapsWithItems:launchOptions:)]) 
{
    // Create an MKMapItem to pass to the Maps app
    CLLocationCoordinate2D coordinate = 
                CLLocationCoordinate2DMake(16.775, -3.009);
    MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate:coordinate 
                                            addressDictionary:nil];
    MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:placemark];
    [mapItem setName:@"My Place"];

    // Set the directions mode to "Walking"
    // Can use MKLaunchOptionsDirectionsModeDriving instead
    NSDictionary *launchOptions = @{MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeWalking};
    // Get the "Current User Location" MKMapItem
    MKMapItem *currentLocationMapItem = [MKMapItem mapItemForCurrentLocation];
    // Pass the current location and destination map items to the Maps app
    // Set the direction mode in the launchOptions dictionary
    [MKMapItem openMapsWithItems:@[currentLocationMapItem, mapItem] 
                    launchOptions:launchOptions];
}

You can preserve your original iOS 5 and lower code in an else statement after that if. Note that if you reverse the order of items in the openMapsWithItems: array, you'll get directions from the coordinate to your current location. You could probably use it to get directions between any two locations by passing a constructed MKMapItem instead of the current location map item. I haven't tried that.

Finally, if you have an address (as a string) that you want directions to, use the geocoder to create an MKPlacemark, by way of CLPlacemark.

// Check for iOS 6
Class mapItemClass = [MKMapItem class];
if (mapItemClass && [mapItemClass respondsToSelector:@selector(openMapsWithItems:launchOptions:)])
{
    CLGeocoder *geocoder = [[CLGeocoder alloc] init];
    [geocoder geocodeAddressString:@"Piccadilly Circus, London, UK" 
        completionHandler:^(NSArray *placemarks, NSError *error) {

        // Convert the CLPlacemark to an MKPlacemark
        // Note: There's no error checking for a failed geocode
        CLPlacemark *geocodedPlacemark = [placemarks objectAtIndex:0];
        MKPlacemark *placemark = [[MKPlacemark alloc]
                                  initWithCoordinate:geocodedPlacemark.location.coordinate
                                  addressDictionary:geocodedPlacemark.addressDictionary];

        // Create a map item for the geocoded address to pass to Maps app
        MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:placemark];
        [mapItem setName:geocodedPlacemark.name];

        // Set the directions mode to "Driving"
        // Can use MKLaunchOptionsDirectionsModeWalking instead
        NSDictionary *launchOptions = @{MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving};

        // Get the "Current User Location" MKMapItem
        MKMapItem *currentLocationMapItem = [MKMapItem mapItemForCurrentLocation];

        // Pass the current location and destination map items to the Maps app
        // Set the direction mode in the launchOptions dictionary
        [MKMapItem openMapsWithItems:@[currentLocationMapItem, mapItem] launchOptions:launchOptions];

    }];
}
nevan king
  • 112,709
  • 45
  • 203
  • 241
  • The code works great for iOS 6. Worth mention though if the route we want is from current user location to a destination, then no need to pass in `currentLocationMapItem`. – Philip007 Nov 27 '12 at 20:25
  • 1
    The fact that you used Tombouctou coordinates just makes the answer even better :) – sachadso Feb 04 '13 at 17:09
  • +1 for mentioning that it's iOS 6 only and that you need a fallback – Henrik Erlandsson Mar 15 '13 at 07:59
  • With this method we can open the map app but how to come back again to my app. – Sawant Apr 06 '13 at 04:01
  • Perhaps things have changed, but I THINK that "[MKMapItem alloc]" will cause a crash when executed pre system-6. I've always done this for newish classes: Class c = [MKMapItem class]; if (c) { mapItem = [[c alloc] initWithCoordinate:coordinate]; ... } You won't see this problem when testing under 6.0! – Christopher Schardt Jun 06 '13 at 17:14
  • @ChristopherSchardt There's something in the docs about `MKMapItem` being used (internally) pre-iOS 6, that's why Apple recommends the special `[mapItemClass respondsToSelector:@selector(openMapsWithItems:launchOptions:)` check. – nevan king Jun 06 '13 at 19:21
  • 2
    how can i navigate to my current application from map apps ? – Apple Aug 12 '13 at 09:36
  • 1
    What so ever location i select when it opens apple maps app it shows alert "directions could not be found between these locations ios 6" and then its doing nothing? Any help – NaXir Sep 01 '13 at 06:18
  • +1 for working and useful code, although if you want to say that something is the 'official Apple way', I think you ought to include a reference that supports that claim (especially since it will make it easier for others to notice if this answer ever *ceases* to be the official Apple way). – Mark Amery Sep 05 '13 at 14:28
  • @MarkAmery I think I meant "official" compared to the accepted answer, which uses a URL scheme. The iOS 6 check is official though, taken from the Apple docs. – nevan king Sep 09 '13 at 11:32
  • Getting crashes in ios 7 with this code. Is there a workaround? – ErikAndren Sep 20 '13 at 15:37
  • @ErikAndren Works for me on iOS 7.0. Have you included the error checking that I left out? Open another question with your error and post the link here. – nevan king Sep 20 '13 at 16:45
  • @nevanking The error was actually somewhere else in the code. Thanks for clarifying that this also works in ios 7 though. – ErikAndren Sep 21 '13 at 07:19
  • Don't forget to #import – Mdlc Jun 11 '15 at 10:20
  • This is a great ObjC answer to this question. Wish it was the accepted answer. – Brandon A Aug 31 '15 at 08:11
80

Found the answer to my own question. Apple documents its maps URL format here. It looks like you can essentially replace maps.google.com with maps.apple.com.

Update: It turns out that the same is true in MobileSafari on iOS 6; tapping a link to http://maps.apple.com/?q=... opens the Maps app with that search, the same way http://maps.google.com/?q=... did on previous versions. This works and is documented in the page linked above.

UPDATE: This answers my question relating to the URL format. But nevan king's answer here (see below) is an excellent summary of the actual Maps API.

Community
  • 1
  • 1
Tom Hamming
  • 10,577
  • 11
  • 71
  • 145
  • 1
    Interesting. If you open a browser to maps.apple.com it redirects to maps.google.com. I wonder how long this will last? – pir800 Sep 26 '12 at 23:50
  • @pir800 - I was actually just wondering about what would happen if you tap a link to maps.apple.com in Safari on iOS 6. I tried it, and it goes to Maps the same way it did on previous iOS versions when you tapped a link to maps.google.com. I think it's a good bet that they're redirecting to Google maps so that website authors can just point map links to maps.apple.com and have it work on iOS in Maps but work normally on all other clients. But I'd want to verify that somehow before changing all my map links to point to maps.apple.com! – Tom Hamming Sep 26 '12 at 23:59
  • @pir800 - for me, opening http://maps.apple.com in a browser takes me to http://www.apple.com/ios/maps/. Maybe my previous comment is wishful thinking. – Tom Hamming Sep 27 '12 at 00:02
  • I meant if you try to query an address or coordinate. Try maps.apple.com/?q=los angeles, ca. You can open it on your desktop machine and it will forward to maps.google.com – pir800 Sep 27 '12 at 14:18
  • Is there a way to add the transit mode to that query? (walking/ driving). couldn't find any reference to it on the web. – Lirik Aug 06 '13 at 14:07
41

The best way to do it is to call new iOS 6 method on MKMapItem openInMapsWithLaunchOptions:launchOptions

Example:

CLLocationCoordinate2D endingCoord = CLLocationCoordinate2DMake(40.446947, -102.047607);
MKPlacemark *endLocation = [[MKPlacemark alloc] initWithCoordinate:endingCoord addressDictionary:nil];
MKMapItem *endingItem = [[MKMapItem alloc] initWithPlacemark:endLocation];

NSMutableDictionary *launchOptions = [[NSMutableDictionary alloc] init];
[launchOptions setObject:MKLaunchOptionsDirectionsModeDriving forKey:MKLaunchOptionsDirectionsModeKey];

[endingItem openInMapsWithLaunchOptions:launchOptions];

This will start the navigation for driving from the current location.

zvonicek
  • 1,503
  • 16
  • 26
  • 1
    OK, so what is the easiest way to get an instance of `MKMapItem` when all I have is an address? This API seems a bit complicated for that simple use case. – Tom Hamming Sep 24 '12 at 14:54
  • MKPlacemark *endLocation = [[MKPlacemark alloc] initWithCoordinate:nil addressDictionary:yourAdressDictHere]; you can hand a adress Dictonary in – mariusLAN Oct 12 '12 at 12:46
7

I see you found the maps.apple.com url "scheme". It's a good choice because it will automatically redirect older devices to maps.google.com. But for iOS 6 there is a new class you might want to take advantage of: MKMapItem.

Two methods that are of interest to you:

  1. -openInMapsWithLaunchOptions: - call it on an MKMapItem instance to open it in Maps.app
  2. +openMapsWithItems:launchOptions: - call it on MKMapItem class to open an array of MKMapItem instances.
Filip Radelic
  • 26,607
  • 8
  • 71
  • 97
  • That looks useful. But it also seems a little more complicated to use; you have to `init` `MKMapItem` with an `MKPlacemark`, which you get by providing a latitude/longitude pair and an address dictionary. Seems simpler to just open `http://maps.apple.com/?q=1+infinite+loop+cupertino+ca`. Is there an advantage to using `MKMapItem` when all you want to do is show an address in Maps? – Tom Hamming Sep 20 '12 at 15:43
  • You don't have to initialize an `MKMapItem` if you don't already have a reference to one that you'd like to use. Just use the class method, `+openMapsWithItems:launchOptions:` on `MKMapItem` to do the same thing. – Mark Adams Sep 22 '12 at 15:25
  • @MarkAdams that class method takes an array of class' instances, so yeah he needs to have at least one initialized. – Filip Radelic Sep 22 '12 at 16:11
  • So in a situation when I have GPS coordinates to open, but no address, I use the `MKMapItem` API and it drops a point on the map labeled "Unknown Location". Is there a way to get it to display a name for the location? I don't see any keys for this in the options for the address dictionary... – Tom Hamming Oct 09 '12 at 21:02
  • 5
    @Mr.Jefferson Set the name property of MKMapItem – matt Dec 05 '12 at 00:52
5

Here is a class using nevan king's solution completed in Swift:

class func openMapWithCoordinates(theLon:String, theLat:String){

            var coordinate = CLLocationCoordinate2DMake(CLLocationDegrees(theLon), CLLocationDegrees(theLat))

            var placemark:MKPlacemark = MKPlacemark(coordinate: coordinate, addressDictionary:nil)

            var mapItem:MKMapItem = MKMapItem(placemark: placemark)

            mapItem.name = "Target location"

            let launchOptions:NSDictionary = NSDictionary(object: MKLaunchOptionsDirectionsModeDriving, forKey: MKLaunchOptionsDirectionsModeKey)

            var currentLocationMapItem:MKMapItem = MKMapItem.mapItemForCurrentLocation()

            MKMapItem.openMapsWithItems([currentLocationMapItem, mapItem], launchOptions: launchOptions)
}
PJeremyMalouf
  • 613
  • 6
  • 15
4

I found it annoying that using the http://maps.apple.com?q=... link setup opens the safari browser first at older devices.

So for an iOS 5 device opening up your app with a reference to maps.apple.com the steps look like:

  1. you click something in the app and it refers to the maps.apple.com url
  2. safari opens up the link
  3. the maps.apple.com server redirects to the maps.google.com url
  4. the maps.google.com url gets interpreted and opens the google Maps app.

I think that the (very obvious and confusing) steps 2 and 3 are annoying to users. Therefore i check the os version and either run maps.google.com or maps.apple.com on the device (for resp. ios 5 or ios 6 OS versions).

EeKay
  • 6,494
  • 3
  • 24
  • 24
3

My research on this issue lead me to the following conclusions:

  1. If you use maps.google.com then it will open the map in safari for every ios.
  2. If you use maps.apple.com then it will open the map in map application of ios 6 and also work greate with ios 5 and in ios 5 it open the map as normal in safari.
Tiago Almeida
  • 14,081
  • 3
  • 67
  • 82
Honey Jain
  • 61
  • 3
3

If you want to open Google Maps instead (or offer as a secondary option), you can use the comgooglemaps:// and comgooglemaps-x-callback:// URL schemes documented here.

Johan Kool
  • 15,637
  • 8
  • 64
  • 81
3

Before launching url, remove any special character from the url and replace spaces by +. This will save you some headaches:

    NSString *mapURLStr = [NSString stringWithFormat: @"http://maps.apple.com/?q=%@",@"Limmattalstrasse 170, 8049 Zürich"];

    mapURLStr = [mapURLStr stringByReplacingOccurrencesOfString:@" " withString:@"+"];
    NSURL *url = [NSURL URLWithString:[mapURLStr stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];
    if ([[UIApplication sharedApplication] canOpenURL:url]){
            [[UIApplication sharedApplication] openURL:url];
        }
2
NSString *address = [NSString stringWithFormat:@"%@ %@ %@ %@"
                             ,[dataDictionary objectForKey:@"practice_address"]
                             ,[dataDictionary objectForKey:@"practice_city"]
                             ,[dataDictionary objectForKey:@"practice_state"]
                             ,[dataDictionary objectForKey:@"practice_zipcode"]];


        NSString *mapAddress = [@"http://maps.apple.com/?q=" stringByAppendingString:[address stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];

        NSLog(@"Map Address %@",mapAddress);

        [objSpineCustomProtocol setUserDefaults:mapAddress :@"webSiteToLoad"];

        [self performSegueWithIdentifier: @"provider_to_web_loader_segue" sender: self];

//VKJ

Vinod Joshi
  • 7,696
  • 1
  • 50
  • 51
2

Not using maps, just programmatically using a UiButton action, this worked great for me.

// Button triggers the map to be presented.

@IBAction func toMapButton(sender: AnyObject) {

//Empty container for the value

var addressToLinkTo = ""

//Fill the container with an address

self.addressToLinkTo = "http://maps.apple.com/?q=111 Some place drive, Oak Ridge TN 37830"

self.addressToLinkTo = self.addressToLinkTo.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!

let url = NSURL(string: self.addressToLinkTo)
UIApplication.sharedApplication().openURL(url!)

                }

You could spread some of this code out a bit. For example, I put the variable as a class level variable, had another function fill it, and then when pressed the button simply took what was in the variable and scrubbed it to be used in a URL.

Christopher Wade Cantley
  • 7,122
  • 5
  • 35
  • 48
2

Updated to Swift 4 based on @PJeremyMalouf's answer:

private func navigateUsingAppleMaps(to coords:CLLocation, locationName: String? = nil) {
    let placemark = MKPlacemark(coordinate: coords.coordinate, addressDictionary:nil)
    let mapItem = MKMapItem(placemark: placemark)
    mapItem.name = locationName
    let launchOptions = [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving]
    let currentLocationMapItem = MKMapItem.forCurrentLocation()

    MKMapItem.openMaps(with: [currentLocationMapItem, mapItem], launchOptions: launchOptions)
}
Chris Prince
  • 7,288
  • 2
  • 48
  • 66