MapQuest iOS Navigation SDK

iPhones with MapQuest Maps

MapQuest Navigation SDK provides a way to easily add turn by turn navigation in any app.

Features

  • Traffic-influenced routes based on current traffic conditions.
  • Traffic-influenced reroutes return automatically when a faster route is available.
  • Route tracking with off-route detection and automatic rerouting to destination.
  • Turn-by-turn voice guidance, including robust highway sign information.
  • Pluggable text-to-speech (TTS) system allows developers to choose their own TTS system to enable custom voices or other system-wide behavior. We provide a default TTS system to work on any device.
  • Directions list with simple, easy-to-read turn instructions.
  • Dynamic ETA (Estimated Time of Arrival) based on current traffic conditions as a driver travels along the route.
  • Speed limits along the route.

Getting Started

MapQuest Key

In order to use MapQuest APIs and SDKs you will need a MapQuest Key. We use this key to associate your requests to APIs with your account. You can find your existing Keys or create a new one at Applications page.

Sample Key Page

If you don't have a MapQuest Developer Account you can sign up for one. Sign Up Here.

Xcode and Apple’s iOS SDK

You can get these for free from the Mac App Store. Download Xcode Here

Apple Developer Account (optional)

You can use your personal devices or Xcode’s iOS Simulator for this guide without an Apple Developer Account. If you want to distribute your app to others, you’ll need to sign up and pay for an account. Sign Up Here

Downloading the Navigation SDK

Manual

Download the zipped SDK that contains the framework and a resource bundle that need to be included in the project

CocoaPods

Use the Navigation SDK within your own Xcode project using CocoaPods.

You need to include the following in the podfile:

source 'https://github.com/MapQuest/podspecs-ios.git'

pod 'MQNavigation'

Using your MapQuest Key in the SDK

To use the MapQuest SDK in your app, you need to place your MapQuest Key inside the app’s Info.plist file:

  1. In the Project navigator (in the left sidebar), find your app’s Info.plist.
  2. Select the “Information Property List” row at the top, and go to Editor ‣ Add Item.
  3. In the new row that is inserted, set the Key to MQApplicationKey and set the Value to your Key.
Info.plist

You will also need to allow arbitrary loads in your app. You can do this by adding NSAppTransportSecurity to your Info.plist with NSAllowsArbitraryLoads set to true. We are actively working on resolving this requirement.

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

Quickstart

Host app developers instantiate MQNavigationManager which captures callbacks for navigation-related events. Events are triggered when:

  • The user passes a maneuver and the current step should be updated
  • The user has deviated from the route
  • A new reroute is received
  • A new ETA is received
  • New traffic data is received
  • A traffic-based reroute is received from the server
  • A navigation prompt should be spoken to the user
  • The speed limit changed
  • A new location is received

The Objective-C and Swift examples here demonstrate how to create a route from New York City to Boston and start a turn-by-turn navigation session.

          
//include the umbrella header
#import "MQNavigation.h"

//declare properties for MQNavigationManager and MQRouteService
@property (nonatomic, strong) MQNavigationManager *navigationManager;
@property (nonatomic, strong) MQRouteService *routeService;

//instantiate MQNavigationManager and assign delegates.
self.navigationManager = [[MQNavigationManager alloc] init];
self.navigationManager.delegate = self;
self.navigationManager.promptDelegate = self;

//instantiate MQRouteService
self.routeService = [[MQRouteService alloc] init];

//set up start and destination for the route
CLLocation *nyc = [[CLLocation alloc] initWithLatitude:40.7326808 longitude:-73.9843407];
CLLocation *boston = [[CLLocation alloc] initWithLatitude:42.355097  longitude:-71.055464];

//set up route options
MQRouteOptions *options = [MQRouteOptions new];

//get route/s from MQRouteService
[self.routeService requestRoutesWithStartLocation:nyc destinationLocations:@[boston] options:options completion:^(NSArray * _Nullable routes, NSError * _Nullable error) {
        if(error) {
            NSLog(@"ERROR:%@",error);
        } else if(routes) {
            if (routes.count > 0) {
              //start navigating with the route.
              [self.navigationManager startNavigationWithRoute:routes[0]];
            }
        }
    }];
          
          
//import the module
import MQNavigation`

//declare properties for MQNavigationManager and MQRouteService
fileprivate lazy var navigationManager: MQNavigationManager = {
    return MQNavigationManager().build {
        $0.delegate = self
        $0.promptDelegate = self
    }
}()
fileprivate lazy var routeService = MQRouteService()
//build route options
let routeOptions:MQRouteOptions = {
    return MQRouteOptions().build {
        $0.maxRoutes = 3
        $0.tolls = .disallow
        $0.ferries = .avoid
        $0.internationalBorders = .disallow
        $0.unpaved = .allow
        $0.seasonalClosures = .avoid
        $0.systemOfMeasurementForDisplayText = .unitedStatesCustomary
    }
}()

//request route
routeService.requestRoutes(withStart: nyc, destinationLocations:[boston], options: routeOptions) { [weak self] (routes, error) in
    guard let strongSelf = self else { return }

    //handle error
    if let error = error {
        let alert = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
        strongSelf.present(alert, animated: true, completion: nil)
        return
    }
    guard let routes = routes, routes.isEmpty == false else { return }

    //start navigation with first route
    strongSelf.navigationManager.startNavigation(with: routes[0])
}
        

Terminology

route: a complete trip from a start location through one or more destinations. For example, a trip starting from a user's home, stopping at the coffee shop, and then to work is a route. Multi-stop routing is not yet supported in this release.

leg: the part of a route between two destinations. You can think of a route as a sequence of legs joined together end-to-end. The route above has two legs, from home to the coffee shop, and from the coffee shop to work.

maneuver: an action informing that the user must make a turn, exit a highway, etc.

prompt: spoken narration as the user drives. Example: "In half a mile, turn right onto Main Street."

position: MQNavigation uses a common coordinate system for anything that references a point along a route. Anything that exists along the route line has a position. Maneuvers, prompts, and the user's current progress along the route are all expressed using positions. A position is a floating point number indexing into the route line's coordinate array. A maneuver with position of 3 is located at the 4th coordinate in the route's shape. A prompt with position 23.5 is placed halfway between the coordinates at index 23 and 24. This system provides a continuous reference of points along the route.

position span: MQNavigation uses spans to represent features that exist on long sections of the route, such as traffic information and speed limits. MQNavigation provides helpers to compute lengths, distances, and shapes of positions and position spans.

snapped location: While navigating, MQNavigation tracks the user as they progress along the route. With each GPS update, MQNavigation determines if the user is "on route" or whether they have deviated from the desired route. When "on route" MQNavigation computes a snapped location, which represent a position along the route, including a coordinate, bearing, and other properties that reflect a point exactly along the route line.

Functionality

Route Creation Options

Restrictions may be provided to influence the route path to avoid certain features like tolls, unpaved roads, and others. The following restriction types are available:

  • highways
  • tolls
  • ferries
  • unpaved roads
  • international borders
  • seasonal closures

Each type can be set to one of: allow, avoid, or disallow.

allow The route is allowed to traverse the restriction type. This is the default for all restriction types.

avoid Tells the routing engine to try to not traverse this type, but if the travel time becomes extremely large, it will allow it.

disallow Tells the routing engine that under no circumstances should it return a route containing this restriction type.

System of measurement for Prompts may be changed to Metric or United States Customery by setting systemOfMeasurementForDisplayText to one of MQSystemOfMeasurementUnitedStatesCustomary or MQSystemOfMeasurementMetric.

          
MQRouteOptions *options = [MQRouteOptions new];
options.highways = MQRouteOptionTypeAllow;
options.tolls = MQRouteOptionTypeDisallow;
options.ferries = MQRouteOptionTypeAvoid;
options.unpaved = MQRouteOptionTypeAllow;
options.internationalBorders = MQRouteOptionTypeDisallow;
options.seasonalClosures = MQRouteOptionTypeAvoid;
options.maxRoutes = 3;
options.systemOfMeasurementForDisplayText = MQSystemOfMeasurementUnitedStatesCustomary;
          
          
//build route options
let routeOptions:MQRouteOptions = {
  return MQRouteOptions().build {
      $0.highways = .allow
      $0.tolls = .disallow
      $0.ferries = .avoid
      $0.unpaved = .allow
      $0.internationalBorders = .disallow
      $0.seasonalClosures = .avoid
      $0.maxRoutes = 3
      $0.systemOfMeasurementForDisplayText = .unitedStatesCustomary
  }
}()
        

Receiving ETA and Traffic Updates

As the user progresses along the route, the MQNavigation periodically queries a server to receive updated arrival time and traffic information based on current traffic conditions. Developers may capture these events to obtain and update the displayed traffic information and ETA.

            
//make your view controller conform to MQNavigationManagerDelegate protocol
@interface ViewController () <MQNavigationManagerDelegate> {

}

//this method is called before the network call is made to get new ETA
- (void)navigationManagerWillUpdateETA:(nonnull MQNavigationManager *)navigationManager {
  //TODO: may be a spinner to show that something is happening in the background.
}

//This method will provide updated ETA.
- (void)navigationManagerDidUpdateETA:(MQNavigationManager *)navigationManager withETA:(nonnull MQEstimatedTimeOfArrival *)eta {
    //TODO: handle ETA update
}

//This method will provide details of errors if the network call to get new ETA fails for any reason.
- (void)navigationManager:(nonnull MQNavigationManager *)navigationManager failedToUpdateEtaWithError:(nonnull NSError *)error {
  //TODO: failed to get an ETA update. Do something.
}

//this method is called before the network call is made to get new Traffic
- (void)navigationManagerWillUpdateTraffic:(MQNavigationManager *)navigationManager {
    //TODO: may be a spinner to show that something is happening in the background.
}

//This method will provide updated Traffic for each leg of the route.
- (void)navigationManagerDidUpdateTraffic:(MQNavigationManager *)navigationManager withTrafficByRouteLegId:(NSDictionary *)trafficByRouteLegId {
    //TODO: update UI to show new traffic details on the route.
}

//This method is called when a faster route is available that avoids traffic congestions.
- (void)navigationManager:(MQNavigationManager *)navigationManager foundTrafficReroute:(MQRoute *)route {
  //TODO: ask the user if they want to follow a different route and start navigation with new route.
}

//This method will provide details of errors if the network call to get new Traffic fails for any reason.
- (void)navigationManager:(MQNavigationManager *)navigationManager failedToUpdateTrafficWithError:(nonnull NSError *)error {
  //TODO: handle error
}

            
            
//make your view controller conform to MQNavigationManagerDelegate protocol
class ViewController: UIViewController, MQNavigationManagerDelegate{

}

//this method is called before the network call is made to get new ETA
func navigationManagerWillUpdateETA(_ navigationManager: MQNavigationManager) {
  //TODO: may be a spinner to show that something is happening in the background.
}

//This method will provide updated ETA.
func navigationManagerDidUpdateETA(_ navigationManager: MQNavigationManager, withETA eta: MQEstimatedTimeOfArrival) {
  //TODO: handle ETA update
}

//This method will provide details of errors if the network call to get new ETA fails for any reason.
func navigationManager(_ navigationManager: MQNavigationManager, failedToUpdateEtaWithError error: Error) {
  //TODO: failed to get an ETA update. Do something.
}

//this method is called before the network call is made to get new Traffic
func navigationManagerWillUpdateTraffic(_ navigationManager: MQNavigationManager) {
  //TODO: may be a spinner to show that something is happening in the background.
}

//This method will provide updated Traffic for each leg of the route.
func navigationManagerDidUpdateTraffic(_ navigationManager: MQNavigationManager, withTrafficByRouteLegId trafficByRouteLegId: [AnyHashable : Any]) {
  //TODO: update UI to show new traffic details on the route.
}

//This method is called when a faster route is available that avoids traffic congestions.
func navigationManager(_ navigationManager: MQNavigationManager, foundTrafficReroute route: MQRoute) {
  //TODO: ask the user if they want to follow a different route and start navigation with new route.
}

//This method will provide details of errors if the network call to get new Traffic fails for any reason.
func navigationManager(_ navigationManager: MQNavigationManager, failedToUpdateTrafficWithError error: Error) {
  //TODO: handle error
}
          

Receiving Speed Limit Updates

As the user progresses along the route, Whenever the speed limit changes, a callback function is invoked specifying the speed limit zones exited and entered. These examples demonstrate how to listen to speed limit changes and update a speed limit display.

            
//make your view controller conform to MQNavigationManagerDelegate protocol
@interface ViewController () <MQNavigationManagerDelegate> {

}

//Implement the delegate method and handle changes in speed limits.
- (void)navigationManager:(nonnull MQNavigationManager *)navigationManager
crossedSpeedLimitBoundariesWithExitedZones:(nullable NSArray  *)exitedSpeedLimits
                              enteredZones:(nullable NSArray  *)enteredSpeedLimits
{
   // update UI to reflect new speeed limits.
}

            
            
//make your view controller conform to MQNavigationManagerDelegate protocol
class ViewController: UIViewController, MQNavigationManagerDelegate{

}

////Implement the delegate method and handle changes in speed limits.
func navigationManager(_ navigationManager: MQNavigationManager, crossedSpeedLimitBoundariesWithExitedZones exitedSpeedLimits: Set?, enteredZones enteredSpeedLimits: Set?) {
   // update UI to reflect new speeed limits.

}

          

Receiving Off-route reroute Updates

When MQNavigation detects that user has gone off-route, a callback function is invoked specifying a new route from current location to the destination. These examples demonstrate how to listen to off-route reroutes.

            
//called before network call is made to get a new route
-(void)navigationManagerWillReroute:(MQNavigationManager *)navigationManager {
  //TODO: update UI to show that user has gone off the route
}

//called after MQNavigation gets a new route.
-(void)navigationManager:(MQNavigationManager *)navigationManager didReroute:(MQRoute *)route {
    //TODO: update UI with new route
    //TODO: start navigation using the new route
}

//if the user gets back on the route before a new route is acquired, new route is discarded
-(void)navigationManagerDiscardedReroute:(MQNavigationManager *)navigationManager {
  //TODO: update UI to show that background network call is completed
}

//called when SDK fails to get a reroute.
-(void)navigationManager:(MQNavigationManager *)navigationManager failedToRerouteWithError:(NSError *)error {
    //TODO: note that there was an error
}

            
            
//called before network call is made to get a new route
func navigationManagerWillReroute(_ navigationManager: MQNavigationManager) {
  //TODO: update UI to show that user has gone off the route
}

//called after MQNavigation gets a new route.
func navigationManager(_ navigationManager: MQNavigationManager, didReroute route: MQRoute) {
  //TODO: update UI with new route
  //TODO: start navigation using the new route
}

//if the user gets back on the route before a new route is acquired, new route is discarded
func navigationManagerDiscardedReroute(_ navigationManager: MQNavigationManager) {
  //TODO: update UI to show that backgrond network call is completed
}

//called when SDK fails to get a reroute.
func navigationManager(_ navigationManager: MQNavigationManager, failedToRerouteWithError error: Error) {
  //TODO: note that there was an error
}
          

Voice Guidance

During navigation, when it is time to alert user of an upcoming maneuver or give an update on the route, a callback function is invoked specifying Prompt object that is suppose to be spoken. Host app developers are responsible for using a TTS engine to speak the prompt. These examples demonstrate how to listen to Prompt updates.

            
//make your class conform to MQNavigationManagerPromptDelegate protocol
@interface ViewController () <MQNavigationManagerDelegate> {

}

//this method is called when it is time to speak the prompt.
- (void)navigationManager:(nonnull MQNavigationManager *)navigationManager receivedPrompt:(nonnull MQPrompt *)promptToSpeak userInitiated:(BOOL)userInitiated {
    //TODO: use any Text To Speech engine to speak promptToSpeak.speech
    //TODO: If the app is in background, post a local notification with promptToSpeak.text
}

//this is called when the current prompt (if there is one spoken) is not valid anymore.
- (void)cancelPromptsForNavigationManager:(nonnull MQNavigationManager *)navigationManager {
    //TODO: stop speaking any prompt.
}

            
            
//make your class conform to MQNavigationManagerPromptDelegate protocol
class ViewController: UIViewController, MQNavigationManagerPromptDelegate {

}

//this method is called when it is time to speak the prompt.
func navigationManager(_ navigationManager: MQNavigationManager, receivedPrompt promptToSpeak: MQPrompt, userInitiated: Bool) {
  //TODO: use any Text To Speech engine to speak promptToSpeak.speech
  //TODO: If the app is in background, post a local notification with promptToSpeak.text
}

//this is called when the current prompt (if there is one spoken) is not valid anymore.
func cancelPrompts(for navigationManager: MQNavigationManager) {
  //TODO: stop speaking any prompt.
}

          

Refer to the Navigation SDK Reference Sample Application code to see a complete example of how to leverage the MQNavigation iOS SDK to create turn-by-turn navigation experience.

Supporting Documentation