Routing With MapKit and Core Location
Learn how to use MapKit and CoreLocation to help users with address completion and route visualization using multiple addresses. By Ryan Ackermann.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Routing With MapKit and Core Location
25 mins
- Getting Started
- Using MapKit With CoreLocation
- Getting the User’s Location With CoreLocation
- Autocompleting the User’s Location
- Getting Authorization to Access the User’s Location
- Turning the User’s Coordinates Into an Address
- Processing User Input With MKLocalSearchCompleter
- Improving MKLocalSearchCompleter’s Accuracy
- Finishing the Address’ Autocomplete
- Calculating Routes With MapKit
- Requesting MKRoute Directions
- Rendering Routes Into the Map
- Walking Through Each MKRoute.Step
- Where to Go From Here?
Apple’s been hard at work improving its maps with better land detail, pedestrian data and road coverage, closing the gap between it and its competitors. So hop aboard the Apple Maps bandwagon by getting to know MapKit
and CoreLocation
.
In this tutorial, you’ll create an app named RWRouter to help you find a round-trip route between your starting point and up to two other locations. To do that, you’ll use CoreLocation
and MKLocalSearch
to fetch address data and MKDirections
to find the quickest route between addresses.
In this tutorial, you’ll learn how to:
- Handle user location and authorization requests with
CoreLocation
. - Reverse geocode with
CLGeocoder
to convert a location’s coordinates into a human-readable address. - Use
MKLocalSearchCompleter
to autocomplete an address. - Generate routes using
MKRoute
and display them usingMapKit
.
Now, you’re ready to dive into your map app!
Getting Started
To get started, click the Download Materials button at the top or bottom of this tutorial. Inside the zip file, you’ll find two folders: final and starter. Open the starter folder.
The first view of the app has three text fields: one for the starting/ending address and two for the in-between stops. The second view has a map view and a table view to show the routes and directions.
The app’s layout is complete, but it’s up to you to add the features.
Using MapKit With CoreLocation
What’s the difference between CoreLocation
and MapKit
?
- CoreLocation handles your location data. It uses all available components on the device including the Wi-Fi, GPS, Bluetooth, magnetometer, barometer and cellular hardware.
- MapKit is all about visual operations, like rendering maps, and user-friendly operations, like address search and route directions. After all, who wants to type latitude and longitude instead of a human-readable address?
In the next steps, you’ll gather the user’s starting location with CoreLocation
, use MapKit
to handle addresses that the user enters manually, create a round-trip route between them and then, finally, use all that data to show a map with the entire route. Cool!
Getting the User’s Location With CoreLocation
In ViewControllers/RouteSelectionViewController.swift, add the following property to the top of the class:
private let locationManager = CLLocationManager()
You declare locationManager
as a property of the class so you can access it as needed.
Next, add the following code to attemptLocationAccess()
to set up and instantiate CLLocationManager
:
// 1
guard CLLocationManager.locationServicesEnabled() else {
return
}
// 2
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
// 3
locationManager.delegate = self
// 4
if CLLocationManager.authorizationStatus() == .notDetermined {
locationManager.requestWhenInUseAuthorization()
} else {
locationManager.requestLocation()
}
Taking each numbered section in turn:
- Before using the location manager, it’s good practice to make sure the user has enabled location services.
- When you geocode the location’s coordinates, you’ll probably lose some precision. An accuracy of 100 meters is more than adequate.
- The location manager’s delegate informs the app when new locations arrive and when the privacy setting changes.
- If the user enabled and authorized location services, you request the current location.
Build and run. Did you get an alert asking for your authorization? No.
That’s because there’s one more thing left to take care of: You need to tell the user why you’re making the request.
Autocompleting the User’s Location
You want your app to be able to make sensible suggestions about where the user might want to go. To do this, it needs to know where the user currently is. So you need to get permission to access the user’s current location.
Getting Authorization to Access the User’s Location
Open Supporting Info ▸ Info.plist and follow these steps:
- Add NSLocationWhenInUseUsageDescription to Info.plist.
- Keep the Type as String.
- Set the Value to the message to show users, which explains why you’re asking for their location: Used to autofill the start/end location.
Build and run again; an alert should now pop up, as expected.
Tap Allow While Using App or Allow Once to make the location manager aware of your location.
NSLocationAlwaysUsageDescription
and requestAlwaysAuthorization()
lets the app access the user’s location, even when the app is running in the background.
NSLocationWhenInUseUsageDescription
and requestWhenInUseAuthorization()
lets the app access the user’s location while the app is running. Starting in iOS 13, when requesting this permission level, there’s a third option: Allow Once. This option sets CLAuthorizationStatus
to authorizedWhenInUse
while the app is running. The next time the app launches, it resets the authorization to notDetermined
.
NSLocationAlwaysUsageDescription
and requestAlwaysAuthorization()
lets the app access the user’s location, even when the app is running in the background.
Now that you have permission to use the user’s location, it’s time to put that information to work!
Turning the User’s Coordinates Into an Address
The location information that your app receives will be in the form of coordinates — but most users want to enter their routes as an address.
Next, you’ll create a CLGeocoder
to reverse geocode the user’s location. Reverse geocoding is the process of turning a location’s coordinates into a human-readable address.
Add a new property to the top of ViewControllers/RouteSelectionViewController.swift:
private var currentPlace: CLPlacemark?
Here, you use currentPlace
to store the geocoded information from the current location. You’ll use this information later to generate a route.
Scroll to the end of CLLocationManagerDelegate
and replace the placeholder methods with:
func locationManager(
_ manager: CLLocationManager,
didChangeAuthorization status: CLAuthorizationStatus
) {
// 1
guard status == .authorizedWhenInUse else {
return
}
manager.requestLocation()
}
func locationManager(
_ manager: CLLocationManager,
didUpdateLocations locations: [CLLocation]
) {
guard let firstLocation = locations.first else {
return
}
// TODO: Configure MKLocalSearchCompleter here...
// 2
CLGeocoder().reverseGeocodeLocation(firstLocation) { places, _ in
// 3
guard
let firstPlace = places?.first,
self.originTextField.contents == nil
else {
return
}
// 4
self.currentPlace = firstPlace
self.originTextField.text = firstPlace.abbreviation
}
}
Here’s what this code does:
- Ensure the user has given the app authorization to access location information.
-
reverseGeocodeLocation(_:completionHandler:)
returns an array of placemarks in its completion handler. For most geocoding results, this array will only contain one element. In rare situations, a single location can return many nearby locations. In this case,places?.first
suffices. - Since the user can edit the origin text field, it’s a good idea to make sure that it’s empty before changing it.
- Store the current location and update the field.
firstPlace.abbreviation
is an extension property that determines an appropriate description for a given CLPlacemark
.By default, the simulator doesn’t have a default location set. To configure the location, open the simulator, if it’s not already running. Under Features ▸ Location, select Apple.
Build and run. Once the geocode finishes, you’ll see Apple Campus appear in the Start / End field.
Next, you’ll need to handle user input by implementing MKLocalSearchCompleter
to suggest a location in real time.