Introduction to MapKit in Swift Tutorial

Introduction to MapKit in Swift Tutorial
Plot Honolulu public artwork data using MapKit!

Plot Honolulu public artwork data using MapKit!

Update note: Audrey Tam updated this tutorial to Swift and iOS 8. Original post by Ray Wenderlich. Ray’s original tutorial queried and retrieved data from the Baltimore web service but the code broke when the web service made minor changes, so this update uses a static JSON file downloaded from the Honolulu data portal.

MapKit is a really neat API available on iOS devices that makes it easy to display maps, jump to coordinates, plot locations, and even draw routes and other shapes on top.

This update uses public artworks data from Honolulu, where I was born and raised. It’s no longer my hometown but the names and places bring back memories. If you’re not lucky enough to live there, I hope you’ll enjoy imagining yourself being there!

In this tutorial, you’re going to make an app that zooms into a location in Honolulu, and you’ll plot one of the artworks on the map. Just for fun, you’ll implement the pin’s callout detail button to launch the Maps app, with driving/walking directions to the artwork. Your app will then parse a JSON file from a Honolulu data portal, find the public artworks around that area, and plot them on the map.

In the process, you’ll learn how to add a MapKit map to your app, zoom to a particular location, parse government data that uses the Socrata Framework, create custom map annotations, and more!

This tutorial assumes some familiarity with Swift and iOS programming. If you are a complete beginner, you may wish to check out some of the other tutorials on this site.

Without further ado, let’s get mapping!

Getting Started

In Xcode, go to File\New\New Project, select iOS\Application\Single View Application, and click Next. Then type HonoluluArt as the project name. Set Language to Swift and select iPhone for the Devices option. Make sure that Use Core Data is unchecked, then click Next, and choose a directory to save your project in to finish.

Open Main.storyboard and, from the Object library, drag a Toolbar to the bottom of the screen, and a MapKit View above the Toolbar. In the Attributes Inspector, change the Toolbar Item‘s Identifier to Refresh, as shown in the screenshot below – the Item will become a Refresh button.


When you select the Toolbar Item, be careful not to select its Title by double-clicking on it, or you’ll only change its Title and not its Identifier.

Note: It’s important to add the Toolbar first and then the MapKit View, because if you do it that way ’round you’ll notice that the MapKit View automagically takes up the remaining space. It’s as if Xcode is reading your mind!

To set the constraints to pin all the edges to the superview, so that your layout works for all the different screen sizes, click on the view controller icon then select Resolve Autolayout Issues\Reset to Suggested Contstraints from the lower All Views in View Controller section:


Before you can run your code, you need to create an outlet for the map view in ViewController.swift, or else it will crash on startup!

The outlet’s type will be MKMapView, so you first need to add the MapKit framework to your project.

Open the Assistant editor: it should display ViewController.swift. Just below the import UIKit statement, add this line:

import MapKit

To create the outlet, click the Map View in Main.storyboard and control-drag from it into the space just inside the ViewController class definition: Xcode should prompt you to Insert Outlet or Outlet Collection. Release the drag and, in the pop-up window, name the outlet mapView:


Xcode adds a mapView property to the ViewController class – you’ll use this to control what the map view displays.

While you’re here, delete the didReceiveMemoryWarning method; you won’t be needing it.

Build and run your project, and you’ll have a fully zoomable and pannable map showing the continent of your current location, using Apple Maps!


So far so good, eh? But you don’t want the map to start looking at the entire world – you want to take a look at a particular area!

Setting Visible Area

In ViewController.swift, add the following property to the class:

let searchRadius: CLLocationDistance = 1000

This simply creates a constant for the search radius that you’ll be using later: 1000 meters (1 kilometer), which is a little more than half a mile.

Next, find viewDidLoad and add the following to the end of the method:

// set initial location in Honolulu
let initialLocation = CLLocation(latitude: 21.282778, longitude: -157.829444)

This will set the the starting coordinates of the map view to a point in Honolulu.

When you are trying to tell the map what to display, you can’t just give a latitude and longitude. That’s enough to center the map, but you need to specify the rectangular region to display to get a correct zoom level too.

Add the following helper method to the class:

func centerMapOnLocation(location: CLLocation) {
  let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate, 
    searchRadius * 2.0, searchRadius * 2.0)
  mapView.setRegion(coordinateRegion, animated: true)

The location argument is the center point. The region will be have north-south and east-west spans based on a distance of searchRadius. You use searchRadius * 2.0 here, because that works well for plotting the public artwork data in the JSON file.

setRegion tells the mapView to display the region. The map view automatically transitions the current view to the desired region with a neat zoom animation, with no extra code required!

Back in viewDidLoad, add the following line to the end of the method:


This will call the helper method to zoom into initialLocation on startup.

Build and run the app, and now it should zoom in to the heart of Waikiki :]


Obtaining Public Artworks Data

The next step is to plot some interesting data around the current location. But where in the heck can we get such stuff?

Well, it depends on your current location. Honolulu, like many cities, has an Open Data Portal to improve public access to government data. Like many cities, Honolulu’s data portal is “Powered by Socrata“, an open data framework which provides a rich set of developer tools for accessing Socrata-based data. After you finish this tutorial, maybe look around to see if a nearby city has an alternate dataset you can use?

For this tutorial, you’ll be using the Honolulu Public Art dataset. To keep things simple, I have already downloaded this data from the portal for you – download the file here. Unzip it to find two files: PublicArt.json and JSON.swift. You’ll use JSON.swift later in this tutorial, to parse PublicArt.json.

When you have the PublicArt.json file, drag it into your HonoluluArt\Supporting Files group, make sure Destination: Copy items if needed and Add to targets: HonoluluArt are selected, and click Finish.

To get a feeling for the items in this dataset, open PublicArt.json in the Xcode editor and scroll down to line 1180 (or use ⌘ + L for Jump to Line), which begins with "data" followed by an array of arrays – one array for each artwork. For this tutorial, you’ll use only a few properties from each array: the artwork’s location name, discipline, title, latitude and longitude. For example, for the first data item:

  • location name: Lester McCoy Pavilion
  • discipline: Mural
  • title: The Makahiki Festival – The Makai Mural
  • latitude: 21.290824
  • longitude: -157.85131

Later in this tutorial, you’ll parse this dataset to create an array of Artworks but first, to jump straight into the MapKit fun, you’ll just plot one of the artworks on the map.

Showing an Artwork on the Map

In PublicArt.json, scroll down further to item 55 at line 1233 (or use ⌘ + L for Jump to Line) – it’s a bronze statue of King David Kalakaua in Waikiki Gateway Park – ah, can you hear the tradewinds sighing through the palm trees?


Photo of King David Kalakaua statue, by Wally Gobetz

The properties for this item are:

  • location name: Waikiki Gateway Park
  • discipline: Sculpture
  • title: King David Kalakaua
  • latitude: 21.283921
  • longitude: -157.831661

To show this on the map view, you must create a map annotation. In the context of MapKit, annotations are small pieces of information tied to a particular location and are most often represented as the little pins that show up in the Maps app.

Creating your own annotations is easy. All you need is a class that conforms to MKAnnotation, add the annotation to the map, and inform the map how the annotation should be displayed.

Begin with step 1: create an Artwork class in a new Swift file. To specify the new file’s position in the Project Navigator, select ViewController.swift, so the new file will appear below this. Next, go to File\New\New File, choose iOS\Source\Swift File, and click Next. Set the Save As field to Artwork.swift and click Create.

Open Artwork.swift in the editor and add the following, below import Foundation:

import MapKit
class Artwork: NSObject, MKAnnotation {
  let title: String
  let locationName: String
  let discipline: String
  let coordinate: CLLocationCoordinate2D
  init(title: String, locationName: String, discipline: String, coordinate: CLLocationCoordinate2D) {
    self.title = title
    self.locationName = locationName
    self.discipline = discipline
    self.coordinate = coordinate
  var subtitle: String {
    return locationName

This is a plain old NSObject with a special initializer. Note it marks itself as implementing the MKAnnotation protocol. This means that the coordinate property is required. If you want the annotation view to display a title and subtitle when the user selects a pin, the class also needs properties named title and subtitle.

It’s perfectly sensible for the Artwork class to have stored properties named title and coordinate but none of the PublicArt.json properties maps naturally to the idea of “subtitle”. To conform to the MKAnnotation protocol, subtitle is a computed property that returns locationName.

OK, so the title, locationName and coordinate properties will be used for the MKAnnotation object, but what’s the discipline property for? You’ll find out later in this tutorial ;]

On to step 2 – add an instance of the Artwork class for every artwork you want to plot. You’re adding only one artwork to the map so add the following lines to the end of viewDidLoad in ViewController.swift:

// show artwork on map
let artwork = Artwork(title: "King David Kalakaua",
  locationName: "Waikiki Gateway Park", 
  discipline: "Sculpture",
  coordinate: CLLocationCoordinate2D(latitude: 21.283921, longitude: -157.831661))

This creates a new Artwork object and adds it as an annotation to the map view. The MKMapView class also has an addAnnotations method, which you’ll use later in this tutorial, when you have an array of annotations to add to the map view.

OK, on to the third and final step: providing the map with the information it needs to display the annotation! To do so, the map view will call its viewForAnnotation delegate method. Your job in this delegate method is to return an instance of MKPinAnnotationView to present as a visual indicator of the annotation.

In this case, your ViewController will be the delegate for the map view. To avoid clutter and improve readability, you’ll create an extension of ViewController in a separate file.

Create a new Swift file: go to File\New\New File, choose iOS\Source\Swift File, and click Next. Set the Save As field to VCMapView.swift and click Create. Then add the following, below import Foundation:

import MapKit
extension ViewController: MKMapViewDelegate {
  // 1
  func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
    if let annotation = annotation as? Artwork {
      let identifier = "pin"
      var view: MKPinAnnotationView
      if let dequeuedView = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier)
        as? MKPinAnnotationView { // 2
        dequeuedView.annotation = annotation
        view = dequeuedView
      } else {
        // 3
        view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
        view.canShowCallout = true
        view.calloutOffset = CGPoint(x: -5, y: 5)
        view.rightCalloutAccessoryView = UIButton.buttonWithType(.DetailDisclosure) as UIView
      return view
    return nil

This code isn’t too complicated, but here’s a quick breakdown:

  1. mapView(_:viewForAnnotation:) is the method that gets called for every annotation you add to the map (kind of like tableView(_:cellForRowAtIndexPath:) when working with table views), to return the view for each annotation.
  2. Also similarly to tableView(_:cellForRowAtIndexPath:), map views are set up to reuse annotation views when some are no longer visible. So the code first checks to see if a reusable annotation view is available before creating a new one.
  3. Here you use the plain vanilla MKAnnotationView class if an annotation view could not be dequeued. It uses the title and subtitle properties of your Artwork class to determine what to show in the callout – the little bubble that pops up when the user taps on the pin.

Note: One extra thing to point out about this, suggested by Kalgar, when you dequeue a reusable annotation, you give it an identifier. If you have multiple styles of annotations, be sure to have a unique identifier for each one, otherwise you might mistakenly dequeue an identifier of a different type, and have unexpected behavior in your app. It’s basically the same idea behind a cell identifier in tableView(_:cellForRowAtIndexPath:).

All that’s left is setting ViewController as the delegate of the map view. You can do this in Main.storyboard, but I prefer to do it in code, where it’s more visible. In ViewController.swift, add this line to viewDidLoad, before the statement that creates artwork:

mapView.delegate = self

And that’s it! Build and run your project, and now you should see where King David Kalakaua’s statue is, at the gateway to Waikiki!


mapView(_:viewForAnnotation:) configures the callout to include a detail disclosure info button on the right side but tapping that button doesn’t do anything yet. You could implement it to show an alert with more info, or to open a detail view controller. In Swift By Tutorials, the TreasureHunt app in Chapter 3 shows an alert, and the CafeHunter app in Chapter 8 opens a detail view controller.

Here’s a neat third option: when the user taps the info button, your app will launch the Maps app, complete with driving/walking directions to get from the simulated user location to the artwork!

Launching the Maps App

To provide this great user experience, open Artwork.swift and add this import statement, below the other two import statements:

import AddressBook

This adds the AddressBook framework. What does the address book framework have to do with maps, you might ask? Well, it contains some dictionary key constants such as kABPersonAddressStreetKey for when you need to set the address or city or state fields of a location.

Next, add the following helper method to the class:

// annotation callout info button opens this mapItem in Maps app
func mapItem() -> MKMapItem {
  let addressDictionary = [String(kABPersonAddressStreetKey): subtitle]
  let placemark = MKPlacemark(coordinate: coordinate, addressDictionary: addressDictionary)
  let mapItem = MKMapItem(placemark: placemark) = title
  return mapItem

Here you create an MKMapItem from an MKPlacemark. The Maps app is able to read this MKMapItem and display the right thing.

Next, you have to tell MapKit what to do when the callout button is tapped. To do so, open VCMapView.swift and add this method to the MKMapViewDelegate extension:

func mapView(mapView: MKMapView!, annotationView view: MKAnnotationView!, 
    calloutAccessoryControlTapped control: UIControl!) {
  let location = view.annotation as Artwork
  let launchOptions = [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving]

When the user taps a map annotation pin, the callout shows an info button. If the user taps this info button, the mapView(_:annotationView:calloutAccessoryControlTapped:) method is called.

In this method, you grab the Artwork object that this tap refers to and then launch the Maps app by creating an associated MKMapItem and calling openInMapsWithLaunchOptions on the map item.

Notice you’re passing a dictionary to this method. This allows you to specify a few different options; here the DirectionModeKeys is set to Driving. This will make the Maps app try to show driving directions from the user’s current location to this pin. Neat!

I suggest you take a look at the various different options you can pass in the launch options dictionary. Also take a look at the MKMapItem class method that allows you to pass multiple MKMapItem objects at the same time.

Before you build and run, it is a good idea to simulate your location to Honolulu. In Xcode, go to Product\Scheme\Edit Scheme… and select Run from the left menu, then select the Options tab. Check Core Location: Allow Location Simulation and select Honolulu, HI, USA as the Default Location. Then click the Close button:


Build and run the app and you’ll see the map zoom in on Waikiki:


Note: The glowing user location dot might not appear until you stop and run your app again, or you might need to give it a nudge by selecting Apple from the simulator‘s Debug\Location menu, then stopping and running your app again.

Now tap on the pin, then tap the info button in the callout and watch it launch the Maps app to show the statue’s location, with driving directions to it. Click on the service station icon just to the left of your pin: Aloha!


This calls for a celebration – treat yourself to your favorite tropical drink!

Parsing JSON Data into Artwork Objects

Now that you know how to show one artwork on the map and how to launch the Maps app from the pin’s callout info button, it’s time to parse the dataset into an array of Artwork objects. Then you can search this array for objects that are in the current map region, and show them on the map.

First, find the JSON.swift file that was in the resources zip file along with PublicArt.json. JSON.swift contains a wonderful enumeration that makes JSON parsing a piece of cake. You can read all about it in Chapter 6 of Swift By Tutorials but basically, it provides a case for each type of JSON value, and a library of computed properties to extract the various values.

Add JSON.swift to the HonoluluArt group in the Project Navigator, then add this method to Artwork.swift, below the initializer:

class func fromJSON(json: [JSONValue]) -> Artwork? {
  // 1
  var title: String
  if let titleOrNil = json[16].string {
    title = titleOrNil
  } else {
    title = ""
  let locationName = json[12].string
  let discipline = json[15].string
  // 2
  let latitude = (json[18].string! as NSString).doubleValue
  let longitude = (json[19].string! as NSString).doubleValue
  let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
  // 3
  return Artwork(title: title, locationName: locationName!, discipline: discipline!, coordinate: coordinate)

Just a few notes about this code:

  1. fromJSON‘s json argument will be one of the arrays that represent an artwork – an array of JSONValue objects. If you count through an array’s elements, you’ll see that the title, locationName etc. are at the indexes specified in this method. The title for some of the artworks is null so you test for this when setting the title value.
  2. This converts the string latitude and longitudes to NSString objects so you can then use the handy doubleValue to convert them to doubles.
  3. The computed string property from JSON.swift returns an optional string for locationName and discipline, which must be implicitly unwrapped when passing them to the Artwork initializer

In other words, the fromJSON method converts an array like this:

[ 55, "8492E480-43E9-4683-927F-0E82F3E1A024", 55, 1340413921, "436621", 1340413921, "436621", "{\n}", "Sean Browne",
"Gift of the Oahu Kanyaku Imin Centennial Committee", "1989", "Large than life-size bronze figure of King David Kalakaua
mounted on a granite pedestal. Located at Waikiki Gateway Park.", "Waikiki Gateway Park", 
"", "1991.03", "Sculpture", "King David 
Kalakaua", "Full", "21.283921", "-157.831661", [ null, "21.283921", "-157.831661", null, false ], null ]

into an Artwork object like the one you created before:

  • locationName: “Waikiki Gateway Park”
  • discipline: “Sculpture”
  • title: “King David Kalakaua”
  • coordinate with latitude: 21.283921 longitude: -157.831661

To use the fromJSON method, open ViewController.swift and add the following properties to the class:

var artworks = [Artwork]()
var visibleArtworks = [Artwork]()

This will hold the Artwork objects from the JSON, as well as just the visible ones.

Next, add the following helper method to the class:

func loadInitialData() {
  // 1
  let fileName = NSBundle.mainBundle().pathForResource("PublicArt", ofType: "json");
  var readError : NSError?
  var data: NSData = NSData(contentsOfFile: fileName!, options: NSDataReadingOptions(0),
    error: &readError)!
  // 2
  var error: NSError?
  let jsonObject: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, 
    options: NSJSONReadingOptions(0), error: &error)
  // 3
  if let jsonObject = jsonObject as? [String: AnyObject] {
    if error == nil {
      // 4
      if let jsonData = JSONValue.fromObject(jsonObject)?["data"]?.array {
        for artworkJSON in jsonData {
          if let artworkJSON = artworkJSON.array {
            // 5
            if let artwork = Artwork.fromJSON(artworkJSON) {

Here’s a run-down of what you’re doing in this code:

  1. Read the PublicArt.json file into an NSData object
  2. Use NSJSONSerialization to obtain a JSON object
  3. Check that the JSON object is a dictionary where the keys are Strings and the values can be AnyObject
  4. You’re only interested in the JSON object whose key is "data" and you loop through that array of arrays, checking that each element is an array
  5. Pass each artwork’s array to the fromJSON method that you just added to the Artwork class. If it returns a valid Artwork object, you append it to the artworks array.

Plotting the Visible Artworks

You now have an array of all the public artworks in the dataset but not all of them are located within searchRadius meters of the map view’s center location. You’ll add another helper method next to filter the full list of artworks to just the ones that are within the visible part of the map. In ViewController.swift, add the following method:

func fetchArtAroundLocation(location: CLLocation) {
  // 1
  var artworksHere = [Artwork]()
  // 2
  for artwork in artworks {
    let artworkLocation = CLLocation(latitude: artwork.coordinate.latitude, 
      longitude: artwork.coordinate.longitude)
    if  location.distanceFromLocation(artworkLocation) <= searchRadius {
  // 3
  visibleArtworks = artworksHere

Let’s look at what you’re doing here:

  1. Create an initially empty working array artworksHere to keep track of artworks located within the search radius. At the end of this method, artworksHere is copied to visibleArtworks.
  2. Loop through the artworks array, looking for those that are located within the search radius and appending them to the working array artworksHere
  3. Remove the current annotations in visibleArtworks from the map view, copy the working array artworks to visibleArtworks, and add these annotations to the map view.

Soon, you’ll implement the Refresh button but for now, you’ll make a few changes so you can see fetchArtAroundLocation in action.

Under the lines in viewDidLoad that create initialLocation and center the map view there, add calls to loadInitialData and fetchArtAroundLocation:


Comment out or delete the lines that create the single “King David Kalakaua” map annotation – you don’t need them, now that loadInitialData creates the artworks array:

//    let artwork = Artwork(title: "King David Kalakaua", locationName: "Waikiki Gateway Park",
//      discipline: "Sculpture", coordinate: CLLocationCoordinate2D(latitude: 21.283921, longitude: -157.831661))
//    mapView.addAnnotation(artwork)

Build and run your app and check out all the pins!


Tap a pin to open its callout bubble, then tap its info button to launch the Maps app – yes, everything you did with the King Kalakaua statue works with all these new artworks!

The User Moves the Map…

With your app running, move the map around – there aren’t any pins outside of the initial map region, but I can assure you that there are artworks there. What you need is a way to tell the app to recalculate the visible artworks array and update the map view – that’s what the Refresh button is for.

You need to set up the Refresh button on the toolbar to call a method, so you know when it’s tapped and can search for the artworks around the new current location.

To do this, open Main.storyboard and the Assistant editor, select the Refresh button, and control drag from the button to ViewController.swift, to the line right after the fetchArtAroundLocation method – Xcode should prompt you to Insert Outlet, Action, or Outlet Collection.

Release the drag, change the Connection type to Action, the Name to refreshTapped, keep the Type as AnyObject, and click Connect. Xcode will automatically create the method for you:

@IBAction func refreshTapped(sender: AnyObject) {

All you’re going to do in refreshTapped is call centerMapOnLocation and fetchArtAroundLocation, just as you did with the initialLocation – but … what’s the new location?

MKMapViewDelegate to the rescue! First, create a new optional property in the ViewController class to store the currentLocation – just below the searchRadius declaration is a good place to put it:

var currentLocation: CLLocation?

It’s declared as an optional property because you won’t set its initial value until viewDidLoad to – what else? – initialLocation. Do this anywhere in viewDidLoad, after you’ve created initialLocation:

currentLocation = initialLocation

Now add this delegate method to the MKMapViewDelegate extension in VCMapView.swift:

func mapView(mapView: MKMapView!, regionDidChangeAnimated animated: Bool) {
  currentLocation = CLLocation(latitude:, longitude:

Note that mapView(_:regionDidChangeAnimated:) is called continuously when the user is moving the map – it needs to be as lightweight as possible so it doesn’t slow down the map scrolling.

When the user moves the map view, it wil notify its delegate via this method, so you implement it to update currentLocation. Back in ViewController.swift, implement refreshTapped with the following code:

@IBAction func refreshTapped(sender: AnyObject) {
if let location = currentLocation {
  } else {
    let alert = UIAlertController(title: "Error", message: "No location yet!", preferredStyle: .Alert)
    alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
    presentViewController(alert, animated: true, completion: nil)

In refreshTapped, you check that currentLocation has a value: if it does, then you center the map and fetch the artworks; if it doesn’t, you pop up an alert.

Build and run your app. Move the map away from where all the pins are, then tap the Refresh button – new pins appear!


And that’s it! You’ve built an app that parses a JSON file into an array of artworks, calculates which ones are visible in the current map region, displays them as annotation pins, with a callout info button that launches the Maps app – celebrate with a hula dance around your desk :]

But wait, there’s one little bit of bling that I saved for last…

Color-Coded Pins

Remember the discipline property in the Artwork class? Its values are things like “Sculpture” and “Mural” – in fact, the most numerous disciplines are Sculpture, Plaque, Mural and Monument. It’s easy to color-code the pins so that Sculptures and Plaques have red pins and Murals and Monuments have purple pins, with green pins for all the other disciplines.

In Artwork.swift, add this method:

// pinColor for disciplines: Sculpture, Plaque, Mural, Monument, other
func pinColor() -> MKPinAnnotationColor  {
  switch discipline {
  case "Sculpture", "Plaque":
    return .Red
  case "Mural", "Monument":
    return .Purple
    return .Green

Then, in VCMapView.swift, specify the pinColor in mapView(_:viewForAnnotation:) by adding this line after the else closure:

view.pinColor = annotation.pinColor()

The color needs to be reset every time the view is being reused, since the pin view could previously have been of a different discipline.

Build and run your app to see the different colored pins:


You’re restricted to three pin colors because MKPinAnnotationColor only has those ones defined. Another option for customization is to use images instead of colors. You could replace the pinColor method with a pinImage method in Artwork.swift and set view.image instead of view.pinColor in ViewController.swift.

Bonus Topic: User Location Authorization

This app doesn’t need to ask the user for authorization to access their location but it’s something you might want to include in your other MapKit-based apps. Apple’s phrase is “to use location services” – as of iOS 8, this requires some extra steps, beyond checking a checkbox in the map view’s Attributes pane.

In ViewController.swift, add the following lines:

// MARK: - location manager to authorize user location for Maps app
var locationManager = CLLocationManager()
func checkLocationAuthorizationStatus() {
  if CLLocationManager.authorizationStatus() == .AuthorizedWhenInUse {
    mapView.showsUserLocation = true
  } else {
override func viewDidAppear(animated: Bool) {

This code creates a CLLocationManager object, which keeps track of your app’s authorization status for accessing the user’s location. The checkLocationAuthorizationStatus method checks your app’s status: if your app is authorized, then it effectively checks the map view’s Shows-User-Location checkbox; otherwise, it gets the locationManager to request authorization from the user.

Note: The locationManager can make two kinds of authorization requests: requestWhenInUseAuthorization or requestAlwaysAuthorization. The first lets your app use location services while it is in the foreground; the second authorizes your app whenever it is running. Apple’s documentation discourages the use of “Always”:

Requesting “Always” authorization is discouraged because of the potential negative impacts to user privacy. You should request this level of authorization only when doing so offers a genuine benefit to the user.

Info.plist item: important but easy to overlook!

There’s just one more authorization-related task you need to do – if you don’t, your app won’t crash but the locationManager’s request won’t appear. To get the request to work, you must add an item named NSLocationWhenInUseUsageDescription to your app’s Information Property List, and set its Type to String and its Value to a message that explains to the user why they should allow your app to access their location.

Open Info.plist and, if it’s closed, open the Information Property List. Hover your cursor over the up-down arrows, or click on any item in the list, to display the + and – symbols, then click the + symbol to create a new item. Name it NSLocationWhenInUseUsageDescription, check that its Type is String, then set its Value to something like To show you cool things nearby:


With a usage description like that, who wouldn’t allow access? ;]

Where To Go From Here?

Here is the final project with all of the code you’ve developed in this tutorial.

Now you know the basics of using MapKit, but there’s a lot more you can do from here, including geocoding, adding custom map overlays, and more. A great place to go to for additional information is Apple’s Location Awareness Programming Guide.

To take this app further you may want to look into the MKMapItem class method I hinted at for opening the Maps app with multiple items. Perhaps add a button to the toolbar that opens the Maps app with all of the artworks currently shown. Also, why not take a look at the other launch dictionary options to control what happens when Maps opens.

If you want to learn more about MapKit features in iOS 6 and iOS 7, including registering your app as a routing provider, you should check out our books iOS 6 By Tutorials and iOS 7 By Tutorials. Fully updated for iOS 8 and Xcode 6, each book contains a chapter on MapKit. The iOS 6 chapter covers how you can launch Maps with various options, and how you can register your own app as a routing provider to give directions to users! The iOS 7 chapter covers Flyover, overlays, and the Directions, Snapshots and Cameras APIs.

If you have any questions as you use MapKit in your apps, hints for other MapKit users, or are interested in using government data in your apps, please join in the forum discussion below!

Introduction to MapKit in Swift Tutorial is a post from: Ray Wenderlich

The post Introduction to MapKit in Swift Tutorial appeared first on Ray Wenderlich.



Write a comment