MapQuest iOS Navigation SDK

iPhones with MapQuest Maps

MapQuest Navigation SDK provides a way to easily add turn by turn navigation in any app. This SDK does not generate transactions on your account and is available free to use.

Navigation SDK is only available in the U.S. at this time. If you would like to use Navigation SDK outside of the U.S. please contact your account manager or customer support

Features

  • Route Summary provides high-performance estimated time of arrival, route length, and traffic information for POIs or lat/lng destinations. New in 3.4.0
  • Custom Arrival Radius allows you to customize the arrival radius for a specific destination, for example a stadium vs a home. New in 3.4.0
  • A sample navigation app designed for internal testing, to act as a reference design for customers, and to show off best UX practices for a navigation experience.
  • Improved Destination Requests support MapQuest POI identifiers for improved routing.
  • Multi-Stop Routes with pause/resume functionality.
  • Language support for US English and US Spanish.
  • Destination Arrival delegates give you control over when navigation ends or moves to the next leg.
  • Improved Power Efficiency along with notifications for when the navigation has background idled.
  • 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.
  • Traffic Data Collection is now enabled by default, but can be disabled; ask your MapQuest Account ask your MapQuest Account Representative for details.New in 3.3.0

Complementary APIs

The following APIs are often used with the Navigation SDK and are currently being used within the sample app, but do incur their normal transaction costs.

  • Maps SDK : used for the MapView as demonstrated in the Sample Application; further documentation is available here
  • Geocoding APIs : full documentation available here
  • Search / Search Ahead APIs : full documentation available here

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/CocoaPods/Specs.git'
source 'git@github.com:MapQuest/podspecs-ios.git'

platform :ios, '8.0'
use_frameworks!

target :'<targetName>' do
  pod 'MQNavigation', '~> 3.4.0'
end

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 provides delegate methods 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
  • Navigation has been paused or resumed
  • A destination has been reached
  • An error occurred in starting navigation or during a network call

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.

The examples here set the userLocationTrackingConsentStatus flag to granted for sample purposes only and cannot be used for production. Read more about Traffic Data Collection here.

          
//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] initWithDelegate:self promptDelegate:self];
self.navigationManager.userLocationTrackingConsentStatus = MQUserLocationTrackingConsentStatusGranted; // Production code requires this be set by providing the user with a Terms of Service dialog.

//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<MQRoute
            *> * _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]];
            }
        }
    }];


// This is the only required delegate for MQNavigationManager
- (void)navigationManager:(nonnull MQNavigationManager *)navigationManager failedToStartNavigationWithError:(nonnull NSError*)error {
    // Check for an error on why Navigation did not start
}
    
          
          
//import the module
import MQNavigation`

//declare properties for MQNavigationManager and MQRouteService
fileprivate lazy var navigationManager: MQNavigationManager! = {
    let manager = MQNavigationManager(delegate: self, promptDelegate: self)
    manager?.userLocationTrackingConsentStatus = .granted; // Production code requires this be set by providing the user with a Terms of Service dialog.
    return manager
}()
fileprivate lazy var routeService = MQRouteService()

//set up start and destination for the route
fileprivate let nyc = CLLocation(latitude:40.7326808, longitude:-73.9843407)
fileprivate let boston = CLLocation(latitude:42.355097, longitude:-71.055464)

//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])
}

// This is the only required delegate for MQNavigationManager
func navigationManager(_ navigationManager: MQNavigationManager, failedToStartNavigationWithError error: Error) {
    // Check for an error on why Navigation did not start
}


        

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.
  • destination: either the final destination of a route, per above; or, an intermediate destination along the route.
  • 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

MQNavigation provides a simple class for you to set route restrictions, system of measurement, and language locale when requesting a route.

Restrictions

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. This type does not work with multi-stop routes.

System of Measurement

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

Language

Language allows the navigation prompts, instructions, and maneuvers to be localized in a supported language. Current supported languages include (default) US English en_US and US Spanish es_US.

          
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;
options.language = @"en_US";
          
          
//build route options
let options = MQRouteOptions()
options.highways = .allow
options.tolls = .disallow
options.ferries = .avoid
options.unpaved = .allow
options.internationalBorders = .disallow
options.seasonalClosures = .avoid
options.maxRoutes = 3
options.systemOfMeasurementForDisplayText = .unitedStatesCustomary
options.language = "en_US"

        

Receiving On Route 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:(nonnull MQNavigationManager *)navigationManager withETAByRouteLegId:(nonnull NSDictionary<NSString*,MQEstimatedTimeOfArrival*> *)etaByRouteLegId {
    //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, withETAByRouteLegId etaByRouteLegId: [String : 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 delegate method 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 NSSet <MQSpeedLimit *> *)exitedSpeedLimits enteredZones:(nullable NSSet <MQSpeedLimit *> *)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<MQSpeedLimit>?, enteredZones enteredSpeedLimits: Set<MQSpeedLimit>?) {
   // update UI to reflect new speeed limits.

}

          

Receiving Off-route reroute Updates

When MQNavigation detects that user has gone off-route, the host app is informed via an optional delegate method requesting permission to reroute. If the delegate method returns TRUE or does not exist MQNavigation will attempt a reroute request which if successful will invoke a delegate method specifying a new route from the current location to the destination. These examples demonstrate how to listen to off-route reroutes.

            

//called providing the host app the ability to choose whether a reroute occurs or not
- (BOOL)navigationManagerShouldReroute:(nonnull MQNavigationManager *)navigationManager {
    return YES;
}

//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 providing the host app the ability to choose whether a reroute occurs or not
func navigationManagerShouldReroute(_ navigationManager: MQNavigationManager) -> Bool {
    return true
}

//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 delegate method 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.
}

          

Requesting a Route Summary - Getting information about potential routesNew in 3.4.0

Often you may need to provide a quick ETA (estimated time to arrival) and traffic information before you even start a navigation session. This may be useful as a dashboard for ETA to different points (such as home/work) or as users inquire about points of interest on the map or in your application. The route summary service provides a very quick response providing you with the ETA, Traffic, and Length to a specific destination without the heavy network bandwidth used for a whole route request.

The main advantages of using the route summary call over getting a route are: performance, optimized data delivery, and ease of use.

To get a route summary is very similar to getting a full route request. The differences are that you don't provide any route options and only one destination is supported.

            
@interface Destination : NSObject <MQRouteDestination>
@property (nonatomic, copy) CLLocation* location;
@property (nonatomic, copy, nullable) NSString* mqid;
-(instancetype)initWithLocation:(CLLocation*)location mqID:(nullable NSString*)mqID;
@end


- (void)getRouteSummary {
  //set up start and destination for the route summary
  CLLocation *nyc =    [[Destination alloc] initWithLocation:[[CLLocation alloc] initWithLatitude:40.7326808 longitude:-73.9843407] mqID:nil]
  CLLocation *boston = [[Destination alloc] initWithLocation:[[CLLocation alloc] initWithLatitude:42.355097 longitude:-71.055464] mqID:nil];

  //get route summary from MQRouteService
  [self.routeService requestRouteSummaryWithStartLocation:nyc destination:boston completion:^(MQEstimatedTimeOfArrival *estimatedTimeOfArrival, MQTrafficOverview trafficConditions, CLLocationDistance routeLength, NSError * _Nullable error) {
         //Show Summary on main thread
      }];
}

            
            
class Destination: NSObject, MQRouteDestination {
    var location: CLLocation
    var mqid: String?

    required init(location: CLLocation, mqid: String? = nil) {
        self.location = location
        self.mqid = mqid
    }
}


func getRouteSummary() {
  //set up start and destination for the route summary

  fileprivate let nyc = Destination(location: CLLocation(latitude:40.7326808, longitude:-73.9843407), mqid: nil)
  fileprivate let boston = Destination(location: CLLocation(latitude:42.355097, longitude:-71.055464), mqid: nil)

  //request route
  routeService.requestRouteSummary(withStart: nyc, destination:boston, ) { [weak self] (estimatedTimeOfArrival, trafficConditions, routeLength, error) in
      guard let strongSelf = self else { return }
      OperationQueue.main.addOperation {

        //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
        }

        //Show route summary
      }
  }
}
          

Requesting a Route

MQRouteService provides two ways to designate a destination for your route: by passing in your own coordinates or by passing in an object that conforms to the new MQRouteDestination protocol. Passing in your own coordinates is useful if you are routing to a known location, or if you use a geocoder to retrieve a street address or dropped pin. The MQRouteDestination protocol is used when you have both the coordinates and an MQID associated with a Point of Interest (POI) provided by a MapQuest service like our Search Ahead SDK. Routing using an MQID provides the routing engine with more detail and the ability to generate a route with considerations such as parking lot or the front door of an establishment.

The MQRouteDestination protocol also allows you to create a rich object that encapsulates properties and logic for the destinations - for example a display name, or formatted address, along with the required properties of MQID and coordinate. The protocol is defined to allow you maximum flexibility. While the MQID property is required to be implemented in your class, the property itself can be null. In all cases the location coordinate must be filled in – even with an MQID. The array of destinations passed into the route request is also available as a property on the MQRoute object that you receive back.

The Our Navigation SDK uses a default radius of 50m and which is optimized for most destinations; however in some cases you may wish to customize the arrival radius of a destination to better match its size profile. For example a city center or a stadium may be better handled with a larger radius. To add a custom destination arrival radius, simply implement the CLLocationDistance arrivalAcceptanceRadius property in your MQRouteDestination compliant class and set it to a value or 0 to use the default. If the value is <=0, we will use the default arrival radius. The SDK may evaluate the radius against certain rules and elect to use it selectively.New in 3.4.0

Arrival Confirmation

Upon reaching a destination MQNavigationManager provides an optional delegate method which provides the destination reached and if it is the final destination. If you implement this delegate method you are required to call the acceptance block to continue to the next destination or finish navigation – otherwise MQNavigationManager will assume the destination has been accepted.

By default navigation automatically stops when reaching the final destination. For intermediate destinations, you may want to pause navigation and update the user-interface to allow the user to resume.

            

@interface Destination : NSObject <MQRouteDestination>
@property (nonatomic, copy) CLLocation* location;
@property (nonatomic, copy, nullable) NSString* mqid;
-(instancetype)initWithLocation:(CLLocation*)location mqID:(nullable NSString*)mqID;
@end


- (void)startNavigation {
  //set up start and destination for the route
  CLLocation *nyc =    [[Destination alloc] initWithLocation:[[CLLocation alloc] initWithLatitude:40.7326808 longitude:-73.9843407] mqID:nil]
  CLLocation *boston = [[Destination alloc] initWithLocation:[[CLLocation alloc] initWithLatitude:42.355097 longitude:-71.055464] mqID:nil];
  CLLocation *paulRevereHouse = [[Destination alloc] initWithLocation:[[CLLocation alloc] initWithLatitude:42.3637 longitude:-71.0537] mqID:@"173568"];

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

  //get route/s from MQRouteService
  [self.routeService requestRoutesWithStartLocation:nyc destinations:@[boston, paulRevereHouse] options:options completion:^(NSArray<MQRoute *> * _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]];
              }
          }
      }];
}

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

}

- (void)navigationManager:(nonnull MQNavigationManager *)navigationManager reachedDestination:(nonnull id<MQRouteDestination>)routeDestination forRouteLeg:(nonnull MQRouteLeg *)completedRouteLeg isFinalDestination:(BOOL)isFinalDestination confirmArrival:(nonnull MQConfirmArrivalBlock)confirmArrival {
	// Handle Destination
	confirmArrival(YES);
}
            
            

class Destination: NSObject, MQRouteDestination {
    var location: CLLocation
    var mqid: String?

    required init(location: CLLocation, mqid: String? = nil) {
        self.location = location
        self.mqid = mqid
    }
}


func startNavigation() {
  //set up start and destination for the route

  fileprivate let nyc = Destination(location: CLLocation(latitude:40.7326808, longitude:-73.9843407), mqid: nil)
  fileprivate let boston = Destination(location: CLLocation(latitude:42.355097, longitude:-71.055464), mqid: nil)
  fileprivate let paulRevereHouse = Destination(location: CLLocation(latitude:42.3637, longitude:-71.0537), mqid: "173568")

  //request route
  routeService.requestRoutes(withStart: nyc, destinations:[boston, paulRevereHouse], 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])
  }
}

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

}

func navigationManager(_ navigationManager: MQNavigationManager, reachedDestination routeDestination: MQRouteDestination, for completedRouteLeg: MQRouteLeg, isFinalDestination: Bool, confirmArrival: @escaping MQConfirmArrivalBlock) {
	// Handle Destination
	confirmArrival(true)
}

          

Handling Background Idle

MQNavigation provides battery savings by letting you know if you've been running in the background, on battery, and without moving for a period of time. The framework will call you and you can decide what to do at that point. Some common options include:

  • Create a local notification to the user to let them know they still have navigation running and give them an option to pause, exit, or continue.
  • Automatically pause navigation until the app is back in the foreground
  • Automatically exit navigation
            
/// Called when the app is in the background and navigating, but has not moved enough in a period of time
- (void)navigationManagerBackgroundTimerExpired:(nonnull MQNavigationManager *)navigationManager {
    UNMutableNotificationContent* content = [UNMutableNotificationContent new];
    content.title = @"Do you wish to continue navigating?";
    content.body = @"You haven't moved in awhile while navigation has been in the background. Please let us know if you wish to continue navigating.";
    content.sound = [UNNotificationSound default];
    content.categoryIdentifier = @"BackgroundTimer";

    UNNotificationRequest* request = [UNNotificationRequest initWithIdentifier: @"BackgroundTimerExpired" content: content trigger:nil];

    // Schedule the notification
    UNUserNotificationCenter* center = [UNUserNotificationCenter current];
    [center removeAllPendingNotificationRequests];
    [center addRequest:request withCompletionHandler:nil];
}
             
            
            
/// Called when the app is in the background and navigating, but has not moved enough in a period of time
func navigationManagerBackgroundTimerExpired(_ navigationManager: MQNavigationManager) {
    let content = UNMutableNotificationContent()
    content.title = "Do you wish to continue navigating?"
    content.body = "You haven't moved in awhile while navigation has been in the background. Please let us know if you wish to continue navigating."
    content.sound = UNNotificationSound.default()
    content.categoryIdentifier = "BackgroundTimer"
    let request = UNNotificationRequest(identifier: "BackgroundTimerExpired", content: content, trigger: nil)

    // Schedule the notification.
    let center = UNUserNotificationCenter.current()
    center.removeAllPendingNotificationRequests()
    center.add(request, withCompletionHandler: nil)
}

            
          

Traffic Data Collection: Requesting User ConsentNew in 3.3.0

The MapQuest Navigation SDK is designed to collect location data during active navigation sessions in order to improve the quality of our routes and traffic data. Verizon/MapQuest believes the user's privacy is of the highest importance. Your application is required to request explicit consent to collect location data and to use it for the purposes enumerated below before navigation will proceed. Verizon may share de-identified location information with third parties for limited aggregate purposes, such as traffic reporting.

Your in-app disclosure language should include:

  • Allow [name of app] to collect location and navigation-session information from your device while navigating.
  • Anonymous location data will be collected by Verizon's location service and sent to 3rd-party traffic services.
  • Verizon and its affiliates may use this information to enhance other location-based services and experiences such as local advertising.
  • Buttons: [No thanks] [I Agree]

MQNavigationManager's userLocationTrackingConsentStatus flag has three possible states:

  • Awaiting - this is the default state. MQNavigationManager will not start navigation in this state.
  • Granted - the user has granted consent and MQNavigationManager will collect location information during active navigation only.
  • Denied - the user has denied consent and MQNavigationManager will not collect location information.

While in the awaiting consent state, navigation will not start and you will receive an error that consent has not been set. You may persist the user's response and set it for future navigation sessions.

Error Handling

The MapQuest Navigation SDK provides the NSError domain MQNavigationErrorDomain and a list of potential error codes listed in the MQNavigation SDK Documentation. Furthermore we also provide a set of error delegate methods that provide error information for situations such as:

  • Permissions requirements failed to start navigation
  • MapQuest Application Key invalid
  • Networking errors
  • Failed to reroute
  • Failed to update ETA/traffic
  • Unsupported Language Code
  • User consent has not be set

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