Build a navigation app for iOS
The Mapbox Navigation SDK for iOS gives you all the tools you need to add turn-by-turn navigation to your iOS app. You can get up and running in a few minutes with our drop-in navigation view controller, or build a completely custom app with our core components for routing and navigation. In this guide, you'll create a minimal navigation app, begin navigation by selecting a point on the map, and change the style of the navigation view controller.
This page uses 2.18.1 of the Mapbox Navigation SDK. A newer version of the SDK is available. Learn about the latest version, 3.2.0, in the Navigation SDK documentation.
Getting started
The Mapbox Navigation SDK for iOS runs on iOS 12.0 and above. It can be used with code written in Swift 5 and above. Here are the resources you’ll need before getting started:
- A Mapbox account and access token. Sign up for an account at mapbox.com/signup. You can find your access tokens on your Account page. You will add your access token to your Info.plist file as described in the installation instructions.
- An application including the Mapbox Maps SDK for iOS. This guide assumes that you have already begun building an iOS application that uses the Mapbox Maps SDK for iOS. If you're new to the Mapbox Maps SDK for iOS, complete the Mapbox Maps SDK installation guide to set up a map view first.
Install the Navigation SDK for iOS
You can install the Mapbox Navigation SDK for iOS using either CocoaPods or Swift Package Manager ('SPM'). If you are new to CocoaPods or 'SPM', you can get started with documentation for CocoaPods and 'SPM'.
Install the framework
You'll need to add MapboxNavigation
to your build to use the Mapbox Navigation SDK, map services, and directions services.
CocoaPods:
pod 'MapboxNavigation', '~> 2.18.1'
'SPM':
.package(name: "MapboxNavigation", url: "https://github.com/mapbox/mapbox-navigation-ios.git", from: "2.18.1")
Import the framework
After you've added this to your build, you'll need to import the following modules into your ViewController
to access them in your application.
Initialize a map
Once you've imported the classes in the previous step, you'll be able to initialize a map. Use NavigationMapView
to display the default Mapbox Streets map style. NavigationMapView
is a UIView
that provides additional functionality (like displaying a route on the map) that is helpful for navigation apps specifically by including MapView
on top of the display, and allowing access to it via public NavigationMapView.mapView
getter. The routeOptions
and indexedRouteResponse
variables will be used later to reference a route that will be generated by the Mapbox Navigation SDK. Add the following code within the view controller.
Run your application, and you will see a new map.
Display user location
For this project, you'll get directions between a user's location and a point they have placed on the map. To do this, you'll need to configure location permissions within your application to use the device's location services. Before you can draw a user’s location on the map, you must ask for their permission and give a brief explanation of how your application will use their location data.
Configure location permissions by setting the NSLocationWhenInUseUsageDescription
key in the Info.plist file. We recommend setting the value to the following string, which will become the application's location usage description: Shows your location on the map and helps improve the map
. When a user opens your application for the first time, they will be presented with an alert that asks them if they would like to allow your application to access their location.
Additionally, you may also choose to include the NSLocationAlwaysAndWhenInUseUsageDescription
key within your Info.plist file. We recommend providing a different string when using this key to help your users decide which level of permission they wish to grant to your application. For more information about the differences in these permission levels, see Apple’s Choosing the Location Services Authorization to Request document.
If you wish to access a user's location, you must include the NSLocationWhenInUseUsageDescription
key within the Info.plist file. For more information about location tracking in iOS,
we recommend watching Apple's What's New in Location Technologies
video.
Once you have configured your application's location permissions, display the device's current location on the map. NavigationMapView
is configured to fetch and display it by default. To configure the visual representation, set NavigationMapView.userLocationStyle
; to allow camera tracking position changes, override default NavigationCamera.viewportDataSource
to enable tracking raw
location.
When you run your app in Simulator or on your phone, you’ll be presented with a dialog box asking for permission to use Location Services. Click Allow while using the app. You won’t see your location on the map until you go to Simulator’s menu bar and select Debug ‣ Location ‣ Custom Location (Xcode 11.3 and prior versions). On Simulator shipped with Xcode 11.4 and higher select Features ‣ Location ‣ Custom Location. Enter 37.773
for latitude, -122.411
for longitude.
Add a destination point
In this application, you'll assume that the user wants to retrieve a route between their current location and any point that they select on the map. Next, you'll create the ability to add a marker, or annotation, to the map when the user taps and holds down on the map (a long press). This destination will be used when calculating the route for navigation.
Create a gesture recognizer
Start by creating a new method called didLongPress(_:)
. Within this method, get the point that has been selected by the user and store it in a variable called point
. Then, convert that point into a geographic coordinate and store it in a variable called coordinate
. Add a new didLongPress(_:)
method within your view controller as shown below.
Later on, you will add another method to calculate the route from the origin to your destination within this method.
Update the viewDidLoad
method to add a UILongPressGestureRecognizer
to your NavigationMapView
so it will accept the newly created gesture recognizer.
When you run your app again, you will see the map centered on the user's location. But this time, if you tap and hold anywhere on the map, a new marker will be dropped at the location the user specifies. This point marks the desired final destination for the route. Next, you'll create a method to calculate the route itself.
Generate a route
To generate a route, you'll need to create a new Route
object. When you install the Navigation SDK, it also includes Mapbox Directions for Swift. This library provides a convenient way to access the Mapbox Directions API in iOS applications. It will generate a Route
object that the Navigation SDK will use to display turn-by-turn directions.
The Mapbox Directions API requires at least two waypoints to generate a route. There is a limit to the number of waypoints you can include, depending on the profile you specify. Read more about waypoints in the Mapbox Directions for Swift documentation.
For this project, you'll use two waypoints: an origin and a destination. For this case, the origin will be user's location. The destination will be a point specified by the user when they drop a point on the map. For each waypoint, you'll specify the latitude and longitude of the location and a name that describes the location and add them using the Waypoint
class.
To mark the destination on the map, call the NavigationMapView.showWaypoints(on:legIndex:)
method. This method adds an PointAnnotation
. You can change the title by setting its textField
property.
Begin creating a full navigation experience by creating a new calculateRoute(from:to:)
method that contains the following code:
Note the drawRoute(route:)
function above. It does step-by-step displaying of various components on the NavigationMapView
. This way example creates a preview of the route line with marking destination waypoint and drawing annotation with route duration information. Or, you could call NavigationMapView.showcase(_:, animated:)
to let the SDK handle route displaying and camera updates. This method does not show route duration annotations though.
Route options
After creating the method that will create your route, set a few options to generate a route from the origin to the destination using the NavigationRouteOptions
class. This class is a subclass of RouteOptions
from Mapbox Directions for Swift. You can specify any of the same options you could using the RouteOptions
class found in Mapbox Directions for Swift, but the defaults are better suited to how the Navigation SDK uses the resulting routes. In this example, you'll use the default options, which include high-resolution routes and steps used for turn-by-turn instruction.
Read about all available NavigationRouteOptions
in the Navigation SDK for iOS
documentation.
Run your application, and do a long press on the map to set your destination. When the marker is added to the map, the route is calculated and drawn on the map.
Starting navigation
To start the turn-by-turn navigation sequence, set PointAnnotationManager.delegate
in viewDidLoad()
and implement the AnnotationInteractionDelegate.annotationManager(_:didDetectTappedAnnotations:)
method to receive tap events. When the annotation is tapped, present a new NavigationViewController
using the route that was generated. Add this new method within your view controller.
Finished product
Run the application and then select and hold on the map to add an annotation. Then tap the annotation to initialize navigation sequence between the origin and your specified destination. You built a small navigation app with the Mapbox Navigation SDK for iOS. You can find the full code for this tutorial on GitHub.
import MapboxMaps
import MapboxCoreNavigation
import MapboxNavigation
import MapboxDirections
class ViewController: UIViewController, AnnotationInteractionDelegate {
var navigationMapView: NavigationMapView!
var routeOptions: NavigationRouteOptions?
var routeResponse: RouteResponse?
var beginAnnotation: PointAnnotation?
override func viewDidLoad() {
super.viewDidLoad()
navigationMapView = NavigationMapView(frame: view.bounds)
view.addSubview(navigationMapView)
// Set the annotation manager's delegate
navigationMapView.mapView.mapboxMap.onNext(event: .mapLoaded) { [weak self] _ in
guard let self = self else { return }
self.navigationMapView.pointAnnotationManager?.delegate = self
}
// Configure how map displays the user's location
navigationMapView.userLocationStyle = .puck2D()
// Switch viewport datasource to track `raw` location updates instead of `passive` mode.
navigationMapView.navigationCamera.viewportDataSource = NavigationViewportDataSource(navigationMapView.mapView, viewportDataSourceType: .raw)
// Add a gesture recognizer to the map view
let longPress = UILongPressGestureRecognizer(target: self, action: #selector(didLongPress(_:)))
navigationMapView.addGestureRecognizer(longPress)
}
@objc func didLongPress(_ sender: UILongPressGestureRecognizer) {
guard sender.state == .began else { return }
// Converts point where user did a long press to map coordinates
let point = sender.location(in: navigationMapView)
let coordinate = navigationMapView.mapView.mapboxMap.coordinate(for: point)
if let origin = navigationMapView.mapView.location.latestLocation?.coordinate {
// Calculate the route from the user's location to the set destination
calculateRoute(from: origin, to: coordinate)
} else {
print("Failed to get user location, make sure to allow location access for this application.")
}
}
// Calculate route to be used for navigation
func calculateRoute(from origin: CLLocationCoordinate2D, to destination: CLLocationCoordinate2D) {
// Coordinate accuracy is how close the route must come to the waypoint in order to be considered viable. It is measured in meters. A negative value indicates that the route is viable regardless of how far the route is from the waypoint.
let origin = Waypoint(coordinate: origin, coordinateAccuracy: -1, name: "Start")
let destination = Waypoint(coordinate: destination, coordinateAccuracy: -1, name: "Finish")
// Specify that the route is intended for automobiles avoiding traffic
let routeOptions = NavigationRouteOptions(waypoints: [origin, destination], profileIdentifier: .automobileAvoidingTraffic)
// Generate the route object and draw it on the map
Directions.shared.calculate(routeOptions) { [weak self] (session, result) in
switch result {
case .failure(let error):
print(error.localizedDescription)
case .success(let response):
guard let route = response.routes?.first, let strongSelf = self else {
return
}
strongSelf.routeResponse = response
strongSelf.routeOptions = routeOptions
// Draw the route on the map after creating it
strongSelf.drawRoute(route: route)
if var annotation = strongSelf.navigationMapView.pointAnnotationManager?.annotations.first {
// Display callout view on destination annotation
annotation.textField = "Start navigation"
annotation.textColor = .init(UIColor.white)
annotation.textHaloColor = .init(UIColor.systemBlue)
annotation.textHaloWidth = 2
annotation.textAnchor = .top
annotation.textRadialOffset = 1.0
strongSelf.beginAnnotation = annotation
strongSelf.navigationMapView.pointAnnotationManager?.annotations = [annotation]
}
}
}
}
func drawRoute(route: Route) {
navigationMapView.show([route])
navigationMapView.showRouteDurations(along: [route])
// Show destination waypoint on the map
navigationMapView.showWaypoints(on: route)
}
// Present the navigation view controller when the annotation is selected
func annotationManager(_ manager: AnnotationManager, didDetectTappedAnnotations annotations: [Annotation]) {
guard annotations.first?.id == beginAnnotation?.id,
let routeResponse = routeResponse, let routeOptions = routeOptions else {
return
}
let navigationViewController = NavigationViewController(for: routeResponse, routeIndex: 0, routeOptions: routeOptions)
navigationViewController.modalPresentationStyle = .fullScreen
self.present(navigationViewController, animated: true, completion: nil)
}
}
Next steps
There are many other ways you can customize the Mapbox Navigation SDK for iOS beyond what you've done in this tutorial. For a complete reference of customization options see the Navigation SDK for iOS documentation. Options include:
- Walking and cycling modes.
- Delivering voice instructions using third-party speech synthesizer.
- Building a custom turn-by-turn experience using Core Navigation.