How to use OpenStreetMap on iOS 7 in 7 lines of code

Posted on January 31, 2014 by Lukas under iOS development | 22 comments

OpenStreetMap logoIn today’s programming tutorial, I’ll show you how easy it is to use OpenStreetMap maps in your app instead of default Apple maps. There are many reasons why you might want to do this: for example, the OpenStreetMap project is being built by a huge community of contributors and therefore the maps are really detailed; or, you might just want to give your users the option to use whatever maps they prefer.

Prior to iOS 7, it was also possible to use different map tiles than those that MapKit provides, but it required fairly large amount of code and work. Luckily, the map kit team introduced a really simple way to do this in iOS 7: new classes MKTileOverlay and MKTileOverlayRenderer. If you are a registered iOS developer, I highly suggest you watch the session 304 of WWDC ’13 here (requires ADC login). The introduction of it starts at 22:09.
You probably came here to see the code, so I’ll skip the explanation for now.
And… here it is!

The code


// Place this into your -viewDidLoad method
NSString *template = @"http://tile.openstreetmap.org/{z}/{x}/{y}.png";         // (1)
MKTileOverlay *overlay = [[MKTileOverlay alloc] initWithURLTemplate:template]; // (2)
overlay.canReplaceMapContent = YES;                        // (3)
[self.mapView addOverlay:overlay level:MKOverlayLevelAboveLabels];         // (4)

// And this somewhere in your class that’s mapView’s delegate (most likely a view controller).
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id)overlay {
    if ([overlay isKindOfClass:[MKTileOverlay class]]) {
        return [[MKTileOverlayRenderer alloc] initWithTileOverlay:overlay];
    }
}

The code is pretty self-explanatory, but just in case, I’ll briefly explain what it does.

Line 1: Here is where you specify what map tiles url you want to use. The {x}, {y} and {z} are placeholders that will be substituted by integers based on what part of the planet and at what zoom needs to be loaded (x is horizontal position, y is vertical and z is zoom, starting at 0 for the most zoomed-out map).
Line 2: Creating new MKTileOverlay, which will do all the magic of loading the tiles as needed.
Line 3: Will keep map view from loading default Apple map tiles.
Line 4: Adding the overlay above labels (streets, cities etc.), because OpenStreetMap has its own.
Line 7-11: The delegate method: simply checks what class the overlay is, and creates and returns new MKTileOverlayRenderer in case it’s MKTileOverlay.

The MKTileOverlay coordinate system

MKTileOverlay uses spherical mercator projection (EPSG:3857). I don’t know much about projections (or map stuff in general), but the good news is that this seems to be the most common type of projection, popularized by Google and now used by OpenStreetMap, among others, so you’ll likely be able to use it with the maps of your choice. You can learn more about it on Wikipedia – Mercator projection.

A note for using OpenStreetMap tile servers

OpenStreetMap tile servers are run solely on donated resources, so if you have a very popular app that would create heavy load on those servers, you should go with a commercial solution. For more info, take a look at Tile usage policy – OpenStreetMap wiki.

Other OpenStreetMap-based maps

There are several other maps based on the OpenStreetMap data, most notably OpenCycleMap and MapQuest. You can use them by changing the template url when creating the MKTileOverlay
• OpenCycleMap: http://b.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png
• MapQuest: http://otile3.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.jpg
Take a look at the official OSM wiki page Slippy map filenames – Tile servers to learn more.

Comparison of maps

Standard Apple mapsStandard Apple maps
OpenStreetMapOpenStreetMap
OpenCycleMapOpenCycleMap
MapQuest mapsMapQuest maps

Where to go from here

Give it a spin, and let me know in the comments if you have any questions. In case you are wondering what UI to build to let users switch between the maps, I have good news: I figured out and implemented a very nice solution. And it even includes a user-tracking button, so it can be used as a drop-in replacement of MKMapView to instantly improve your app’s map experience. I am planning to release the code on GitHub very soon, but if you are curious, you can get Routie and try it out there. For now, here is a screenshot to whet your appetite:

GSMapViewBox screenshotGSMapViewBox preview (comming soon to GitHub).
  • max

    Please all source print =)

  • Lukas

    what do you mean? This is the whole source code, given you have an ivar MKMapView *mapView inside a view controller. What’s not clear about this?

    • Poonam

      Hi Lukas,
      your code is working but it is showing map in tile format

  • sdfsf

    you could leave the full source code for the tutorial

  • http://intransitione.com M

    That was really useful, thanks.
    Just a very minor issue with the code you posted, the rendererForOverlay method in your example requires a return statement after the if.

  • Lukas

    Hi M,
    thanks for your comment, I am glad to hear that the post was useful to you.

    You are right that normally, you’d need to handle the case when there is a different renderer to return. I should have probably just inserted ‘return nil;’ there.

  • Fab

    Sorry but i had copied the code as you written but the app show standard map Apple.
    Seems that the overlay don’t work correctly.
    What could be wrong?
    Thanks.

    • Lukas

      Make sure that you set to mapView.delegate to self (the view controller in which you are doing this) + also that you conformed to , and that your mapView outlet is properly wired in the Storyboard (if you are using it).
      Was that helpful? Please let me know.
      Lukas

      • Fab

        Right! I forgot to set the delegate to self, now i see the correct OpenStreet Map.
        Thank you very much for your help.

        • Lukas

          You are welcome! I am glad it is working now.

  • http://efciphone.blogspot.com.es Eduardo Fulgencio Comendeiro

    Muchas gracias por la explicación.

  • Balram Tiwari

    Hello Lukas ,
    The tutorial is short & sweet & it working as expected. For the initial state I was struggling to get the overlay images due to Firewall problem. AS soon as I switched to 3G mobile / Data connection, it worked like a charm.

    Thanks a Lot.

  • Christina

    Hello,

    Is there any way to store those maps offline? If user wants to use them without an internet connection.

  • Lyubomir Marinov

    The delegate function has an error. If the overlay is not MKTileOverlay , there is nothing to return. So I just placed 8th line of code, that just allocs and inits the MKTileOverlayRenderer :) Quick and dirty, but avoids the compiler error.

  • Alex

    At the most beginning of loading my ViewController , the Apple map apears for the moment until first openstreet tiles load. How can I turn off loading of apple map? Thanx

    • Lukas

      Hi Alex, I am sorry but I really don’t know. Did you take a look around stackoverflow? Someone might have dealt with this issue.

  • servitola

    Thank you very much. Works well with Xamarin after translation to C#.

    • Lukas

      You are welcome! :).

    • Christoffer

      Any example of this. I have extended the map view but the rendererForOverlay is newer called..

      • Lukas

        Hi Christopher,
        don’t subclass the MKMapView, just instantiate it, add it as subview to the view controller and set the delegate to self. You have to also specify that your class conforms to this delegate by placing next to the interface.
        But if you are just starting out, I would recommend learning using this book: http://www.apress.com/9781430260226 . I started with it myself (back then it was iOS 3), and it was great. Also, you can check iTunes U Stanford course about developing iOS apps (this year, it’s updated for Swift).

    • Christoffer
  • Anthony Dunk

    It seems that iOS 9 broke the above tutorial. You now need to allow insecure HTTP access by adding info to your plist file. See http://stackoverflow.com/questions/31901566/ios-9-mktileoverlay-not-working