Get driving directions

This topic describes how to build an OS X application to find driving directions. It's assumed that you've already added a map to your application as described in Add a map to your app.

The API provides a route task (class name AGSRouteTask) that allows you to find driving directions between two or more locations. You can customize the route by specifying preferences, such as whether to find the shortest or the fastest route, or by adding constraints, such as time windows for visiting each location, barriers representing road closures, and so on.

Learn more about the route task

The route task relies on ArcGIS Server Network Analyst services. These services can be hosted in ArcGIS Online, on the Esri cloud platform, or on your on-premises servers. ArcGIS Online provides worldwide Network Analyst services you can use with a subscription to ArcGIS Online. You can also create your own services using ArcGIS for Server. In this topic, you'll use a sample routing web service on ArcGIS Online covering North America.

1. Input and set the start of the route

There are a number of ways in which a user can provide the start and end points of a route. The user can input an address, tap a location on the map, or use the computer's location. For the purpose of this Tutorial, you will programmatically set the start point but allow the user to specify the destination by clicking on the map.

Firstly, set up a tiled map service layer and a graphics layer then add a start location and zoom to an area in San Diego, USA.

var graphicsLayer:AGSGraphicsLayer!
func applicationDidFinishLaunching(aNotification: NSNotification) {
 //add a graphics layer to the map to show the start, destination and route
 self.graphicsLayer = AGSGraphicsLayer()
 self.mapView.addMapLayer(self.graphicsLayer, withName:"Graphics Layer")
 //create the start point
 var startLocation = AGSPoint(x: -13042365, y: 3856910, spatialReference:AGSSpatialReference.webMercatorSpatialReference())
 //add the start point (stop 1) to the array and onto the map
 self.addStop(startLocation, sequence: 1)
 //zoom to San Diego, USA
 var envelope = AGSEnvelope(xmin: -13044000, ymin: 3855000, xmax: -13040000, ymax: 3858000, spatialReference:AGSSpatialReference.webMercatorSpatialReference())
 self.mapView.zoomToEnvelope(envelope, animated: true)


Add the starting point graphic (class name AGSStopGraphic) to the graphics layer and to an array of stops. The sequence property of this graphic object determines the order in which the stops are to be visited. This array of stops will be processed by the Route Task. Note this code will also be used when the destination point is generated.

var stops = Array<AGSStopGraphic>()
func addStop(point:AGSPoint, sequence:UInt) {
 var geometry = AGSGeometry(spatialReference: AGSSpatialReference.webMercatorSpatialReference())
 geometry = point
 //prepare a dictionary to hold the attributes of the stop
 var attributes:[NSObject:AnyObject]?
 //create a stop symbol
 var symbol = self.stopSymbolWithNumber(sequence)
 //create a stop graphic
 var stopGraphic = AGSStopGraphic(geometry: geometry, symbol: symbol, attributes: attributes)
 stopGraphic.sequence = sequence
 //add the stop to an array of stops
 //add the stop graphic to the map

//create the stop symbol
func stopSymbolWithNumber(stopNumber:UInt) -> AGSCompositeSymbol {
 //init composite symbol
 var cs = AGSCompositeSymbol()
 // create main circle
 var sms = AGSSimpleMarkerSymbol()
 sms.color = NSColor.greenColor()
 sms.size = CGSizeMake(20, 20) = .Circle
 // add number as a text symbol
 var ts = AGSTextSymbol(text: "\(stopNumber)", color:NSColor.blackColor())
 ts.vAlignment = .Middle
 ts.hAlignment = .Center
 ts.fontSize	= 16
 return cs;

If you run your application at this stage it should look like this with the start location (1) displayed.

Start Location

2. Allow the user to set the route's destination

To allow the user pick a destination point on the map, the map has to respond to the user click. Add a button (class NSButton) of Type 'On Off' to the user interface. Add an IBOutlet and IBAction outlets for the button. When the button is in On state, set the current class to be the map’s touch delegate and implement the delegate method didClickAtPoint:mapPoint:. When the user clicks on the map, didClickAtPoint:mapPoint: is called. The method returns a mapPoint (class AGSPoint) which you’ll pass to addStops:sequence: as the destination point (i.e. the second stop).

Button Type

Insert both an IBOutlet and IBAction outlets for the button and in its method set the mapView's touchDelegate property to be self. Ensure that AGSMapViewTouchDelegate protocol has been implemented so that the mapView's didClickAtPoint: method is fired when the user clicks on the map. In here the addStop method will then add the destination point to both the graphic layer and the array of stops.

You can then reset the map’s touch delegate to nil so that didClickAtPoint:mapPoint: doesn’t get called on further user interaction with the map. Also reset the state of the button.

class AppDelegate: NSObject, NSApplicationDelegate, AGSMapViewLayerDelegate, AGSMapViewTouchDelegate {

 @IBOutlet weak var btnDestination: NSButton!
 @IBAction func addDestination(sender: AnyObject) {
  self.mapView.touchDelegate = self

 func mapView(mapView: AGSMapView!, didClickAtPoint screen: CGPoint, mapPoint mappoint: AGSPoint!, features: [NSObject : AnyObject]!) {
  //add the destination location (stop 2) to the map and the array of stops
  self.addStop(mappoint, sequence:2)
  //ensure the touchdelegate property is set to nil
  self.mapView.touchDelegate = nil
  //unclick the button
  self.btnDestination.state = NSOffState


If you run your application it should look like this with the start (1) and destination (2) locations displayed.

Destination location

3. Solve and display the route

In this section, you'll implement the solveWithParameters: method and calculate a route from the start to the destination.

To calculate a route, you first need to instantiate the route task. To do this, you need the URL of a routing service, and optionally, the user credentials to access the service. Since the service you'll be using in this topic is public, you don't need credentials to access it.

You also need to set the task's delegate. A route task delegate is required to handle the results of the route task. We must assign one of our classes to do the job, in this case, we simply assign the current class (self) to be the delegate. It must then implement the methods of AGSRouteTaskDelegate necessary to handle the results of a successful result, as well as to inform the user of any error.

In this example, you'll adopt the AGSRouteTaskDelegate protocol by adding the declaration to the swift file and setting the task's delegate to self.

You define input parameters and format of results using AGSRouteTaskParameters class. The retrieveDefaultRouteTaskParameters: method is an async method that upon completions returns a default route task parameters object for you to use.

class AppDelegate: NSObject, NSApplicationDelegate, AGSMapViewLayerDelegate, AGSMapViewTouchDelegate, AGSRouteTaskDelegate {
 var routeTask:AGSRouteTask!
 var routeResult:AGSRouteResult?
 var routeTaskParameters:AGSRouteTaskParameters!

 func applicationDidFinishLaunching(aNotification: NSNotification) {

  //setup the route task and set delegate
  self.routeTask = AGSRouteTask(URL: NSURL(string: ""))
  self.routeTask.delegate = self
  //kick off asynchronous method to retrieve default parameters for the route task

 func routeTask(routeTask: AGSRouteTask!, operation op: NSOperation!, didRetrieveDefaultRouteTaskParameters routeParams: AGSRouteTaskParameters!) {
  self.routeTaskParameters = routeParams


Now that you have a route task and the default AGSRouteTaskParameters, you need to provide the stops that will be analysed to create the route. Invoke the setStopsWithFeatures: method and pass in the array of stops containing the start point and the destination location as the end point. Set some additional route parameters, and call the solveWithParameters: method on the route task to calculate the route.

Add an NSButton for the user to click that will calculate the route. Create an IBAction method showRoute that will call the solveWithParameters.

@IBOutlet weak var btnRoute: NSButton!

 @IBAction func showRoute(sender: AnyObject) {
  if self.stops.count > 0 {
   // this generalizes the route graphics that are returned
   self.routeTaskParameters.outputGeometryPrecision = 5.0
   self.routeTaskParameters.outputGeometryPrecisionUnits = .Meters
   // return the graphic representing the entire route, generalized by the previous
   // 2 properties: outputGeometryPrecision and outputGeometryPrecisionUnits
   self.routeTaskParameters.returnRouteGraphics = true
   // this returns turn-by-turn directions
   self.routeTaskParameters.returnDirections = true
   // ensure the graphics are returned in our map's spatial reference
   self.routeTaskParameters.outSpatialReference = self.mapView.spatialReference
   // execute the route task using the solveWithParameters method

When the route has been solved successfully, the routeTask:operation:didSolveWithResult: is invoked, and an AGSRouteTaskResult is returned. If the object is not nil (make sure to inform the user if it is nil), you’ll have to get hold of its ‘graphic’ property. This graphic contains the route. Add a symbol for the graphic, and add it to the graphics layer of the map.

Also implement routeTask:operation:didFailSolveWithError: delegate method in case the route task fails.

func routeTask(routeTask: AGSRouteTask!, operation op: NSOperation!, didSolveWithResult routeTaskResult: AGSRouteTaskResult!) {
 //there is only one route
 self.routeResult = routeTaskResult.routeResults.last as? AGSRouteResult
 if self.routeResult != nil {
  // make symbol for this route
  self.routeResult!.routeGraphic.symbol = self.routeSymbol()
  //set direction graphics. These will be used to get the directions along the route
  self.directionGraphics = self.routeResult! as [AGSDirectionGraphic]
  // add the route graphic to the graphic's layer
  //zoom to graphics layer
  self.mapView.zoomToGeometry(self.graphicsLayer.fullEnvelope, withPadding:150, animated:true)
//get the route symbol
func routeSymbol() -> AGSCompositeSymbol {
 //init composite symbol
 var cs = AGSCompositeSymbol()
 var sls1 = AGSSimpleLineSymbol()
 sls1.color = NSColor.blueColor() = .Solid
 sls1.width = 6
 var sls2 = AGSSimpleLineSymbol()
 sls2.color = NSColor(red: 0/255.0, green:170/255.0, blue:255/255.0, alpha:1.0) = .Solid
 sls2.width = 4
  return cs

If you run your application it should show the best route bewteen the start (1) and the destination (2).

Start and Destination

4. Show the route's driving directions

The driving directions are returned along with the route if you set the AGSRouteTaskParameters property returnDirections to be YES. The direction information is returned as an array of AGSDirectionGraphicsin AGSRouteResult.

Iterate through the directions graphics. In this tutorial the directions text was added to a text view that was displayed in an NSPopover invoked by the click of a button called "Directions" in the UI.

@IBOutlet weak var btnDirections: NSButton!
@IBAction func showDirections(sender: AnyObject) {
 if self.popover.shown {
 //empty the text view
 self.textView.string = ""
 //iterate through the directions and add the text to a text view
 for directionGraphic in self.directionGraphics as [AGSDirectionGraphic] {
 //show the popover
 self.popover.showRelativeToRect(sender.bounds, ofView: sender as NSView, preferredEdge: NSMaxYEdge)

If you run your application the driving directions will be displayed in the Popover directly below the Directions button.


This tutorial has provided you with the fundamental steps for creating a route and retrieving the directions. Please examine the OS X sample code for more examples involving multiple stops, for creating a route with barriers in place and for both online and offline routing.