Skip To Content ArcGIS for Developers Sign In Dashboard

ArcGIS Runtime SDK for macOS

Routing

Download Sample Application

Calculate point-to-point and multi-point routes and driving directions using the Route Task. The sample uses the Sketch Layer to allow you to add stops (green points) interactively and to add barriers (red points, lines or polygons) to avoid certain roads and intersections. Click on the Route button to generate a route between the stops, and to show the route on the map using a blue line. Click on the Directions button to get visual and descriptive details of the directions to follow.

//header file
@interface RoutingSample : NSViewController <AGSRouteTaskDelegate>
@property (strong) AGSRouteTask *routeTask;
@property (strong) AGSRouteTaskParameters *routeTaskParameters;
@property (strong) AGSRouteResult *routeResult;
...
@end

//implementation file
//set up the route task and set delegate
self.routeTask = [AGSRouteTask routeTaskWithURL:[NSURL URLWithString:@"http://sampleserver6.arcgisonline.com/arcgis/rest/services/NetworkAnalysis/SanDiego/NAServer/Route"]];
self.routeTask.delegate = self;

//set up the parameters
[self.routeTaskParameters setStopsWithFeatures:stops];
[self.routeTaskParameters setPolylineBarriersWithFeatures:barriers];
self.routeTaskParameters.returnRouteGraphics = YES;
self.routeTaskParameters.returnDirections = YES;
...

//use the route task to solve the route providing the parameters
[self.routeTask solveWithParameters:self.routeTaskParameters];

//process the results when the solve has returned some results
- (void)routeTask:(AGSRouteTask *)routeTask operation:(NSOperation *)op didSolveWithResult:(AGSRouteTaskResult *)routeTaskResult {
  ...
}

Sample Code

//SWIFT SAMPLE CODE
/*
Copyright 2014 Esri

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import Cocoa
import ArcGIS

//layer/routetask urls
let kRSBaseMapURL = "http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer"
let kRouteTaskURL = "http://sampleserver6.arcgisonline.com/arcgis/rest/services/NetworkAnalysis/SanDiego/NAServer/Route"

class DirectionsTableCellViewSwift: NSTableCellView {
        @IBOutlet weak var directionTextField:NSTextField!
}

class RoutingSwiftSample: NSViewController, AGSRouteTaskDelegate, NSTableViewDataSource, NSTableViewDelegate, NSPopoverDelegate {
    
    @IBOutlet weak var mapView:AGSMapView!
    @IBOutlet weak var stopsBarriersSegmentedControl:NSSegmentedControl!
    @IBOutlet weak var sketchTools:NSSegmentedControl!
    @IBOutlet weak var addStopOrBarrierButton:NSButton!
    @IBOutlet weak var clearSketchButton:NSButton!
    @IBOutlet weak var routeButton:NSButton!
    @IBOutlet weak var directionsButton:NSButton!
    @IBOutlet weak var tableView:NSTableView!
    @IBOutlet weak var popover:NSPopover!
    @IBOutlet weak var messageLabel:NSTextField!
    
    var graphicsLayer:AGSGraphicsLayer!
    var sketchLayer:AGSSketchGraphicsLayer!
    var routeTask:AGSRouteTask!
    var routeTaskParameters:AGSRouteTaskParameters!
    var routeResult:AGSRouteResult!
    var currentDirectionGraphic:AGSDirectionGraphic!
    var stops = Array<AGSStopGraphic>()
    var pointBarriers = Array<AGSGraphic>()
    var polylineBarriers = Array<AGSGraphic>()
    var polygonBarriers = Array<AGSGraphic>()
    var directionGraphics = Array<AGSDirectionGraphic>()
    var numStops:UInt = 0
    var numBarriers:UInt = 0
    var awaken = false
    
    //MARK: - awakeFromNib
    
    // -------------------------------------------------------------------------------
    //  awakeFromNib
    // -------------------------------------------------------------------------------
    override func awakeFromNib() {
        if !self.awaken {
        
            //add base layer to map
            let baseMapLayer = AGSTiledMapServiceLayer(URL: NSURL(string: kRSBaseMapURL))
            self.mapView.addMapLayer(baseMapLayer, withName:"Base Layer")
            
            //add graphics layer
            self.graphicsLayer = AGSGraphicsLayer()
            self.mapView.addMapLayer(self.graphicsLayer, withName:"Graphics Layer")
            
            //add sketch graphics layer and set as mapView's touch delegate so it begin tracking touch events
            self.sketchLayer = AGSSketchGraphicsLayer(geometry: AGSMutablePoint(spatialReference: AGSSpatialReference.webMercatorSpatialReference()))
            self.mapView.touchDelegate = self.sketchLayer
            self.mapView.addMapLayer(self.sketchLayer, withName:"Sketch Layer")
            
            //setup the route task and set delegate
            self.routeTask = AGSRouteTask(URL: NSURL(string: kRouteTaskURL))
            self.routeTask.delegate = self
            
            //kick off asynchronous method to retrieve default parameters for the route task
            self.routeTask.retrieveDefaultRouteTaskParameters()
            
            //init stops and barriers array
//            self.stops = [NSMutableArray array]
//            self.pointBarriers = [NSMutableArray array]
//            self.polylineBarrieres = [NSMutableArray array]
//            self.polygonBarrieres = [NSMutableArray array]
            
            //register for "Geometry Changed" notifications
            //we want to enable/disable UI elements when sketch geometry is modified
            NSNotificationCenter.defaultCenter().addObserver(self, selector: "respondToGeometryChanged:", name:AGSSketchGraphicsLayerGeometryDidChangeNotification, object:self.sketchLayer)
            
            //set message
            self.messageLabel.stringValue = "Tap on the map to add stops and barriers"
            
            //zoom to predefined extend with known spatial reference of the map
            let envelope = AGSEnvelope(xmin: -13044000, ymin:3855000, xmax:-13040000, ymax:3858000, spatialReference:AGSSpatialReference.webMercatorSpatialReference())
            self.mapView.zoomToEnvelope(envelope, animated:true)
//            [self.mapView.maxEnvelope envelope]
            
            self.awaken = true
        }
    }
    
    //MARK: - Symbols
    
    // -------------------------------------------------------------------------------
    //  stopSymbolWithNumber:stopNumber
    // -------------------------------------------------------------------------------
    func stopSymbolWithNumber(stopNumber:Int) -> AGSCompositeSymbol {
    
        //init composite symbol
        let cs = AGSCompositeSymbol()
        
        // create main circle
        let sms = AGSSimpleMarkerSymbol()
        sms.color = NSColor.greenColor()
        sms.size = CGSizeMake(20, 20)
        sms.style = .Circle
        cs.addSymbol(sms)
        
        // add number as a text symbol
        let ts = AGSTextSymbol(text: "\(stopNumber)", color: NSColor.blackColor())
        ts.vAlignment = .Middle
        ts.hAlignment = .Center
        ts.fontSize	= 16
        cs.addSymbol(ts)
        
        return cs
    }
    
    // -------------------------------------------------------------------------------
    //  pointBarrierSymbol
    // -------------------------------------------------------------------------------
    func pointBarrierSymbol() -> AGSSimpleMarkerSymbol {
        //create a simple marker symbol
        let sms = AGSSimpleMarkerSymbol()
        sms.color = NSColor.redColor()
        sms.size = CGSizeMake(20, 20)
        sms.style = .Circle
        return sms
    }
    
    // -------------------------------------------------------------------------------
    //  polylineBarrierSymbol
    // -------------------------------------------------------------------------------
    func polylineBarrierSymbol() -> AGSSimpleLineSymbol {
        //create a simple line symbol
        let sls = AGSSimpleLineSymbol()
        sls.color = NSColor.redColor()
        sls.style = .Solid
        sls.width = 4
        return sls
    }
    
    // -------------------------------------------------------------------------------
    //  polygonBarrierSymbol
    // -------------------------------------------------------------------------------
    func polygonBarrierSymbol() -> AGSSimpleFillSymbol {
        //create simple fill symbol
        let sfs = AGSSimpleFillSymbol()
        sfs.style = .Solid
        sfs.color = NSColor.redColor().colorWithAlphaComponent(0.5)
        sfs.outline.width = 4
        sfs.outline.color = NSColor.redColor()
        return sfs
    }
    
    // -------------------------------------------------------------------------------
    //  routeSymbol
    // -------------------------------------------------------------------------------
    func routeSymbol() -> AGSCompositeSymbol {
        
        //init composite symbol
        let cs = AGSCompositeSymbol()
        
        let sls1 = AGSSimpleLineSymbol()
        sls1.color = NSColor.blueColor()
        sls1.style = .Solid
        sls1.width = 6
        cs.addSymbol(sls1)
        
        let sls2 = AGSSimpleLineSymbol()
        sls2.color = NSColor(red: 0/255.0, green:170/255.0, blue:255/255.0, alpha:1.0)
        sls2.style = .Solid
        sls2.width = 4
        cs.addSymbol(sls2)
        
        return cs
    }
    
    // -------------------------------------------------------------------------------
    //  currentDirectionSymbol
    // -------------------------------------------------------------------------------
    func currentDirectionSymbol() -> AGSCompositeSymbol {
        //init composite symbol
        let cs = AGSCompositeSymbol()
        
        let sls1 = AGSSimpleLineSymbol()
        sls1.color = NSColor.blackColor()
        sls1.style = .Solid
        sls1.width = 8
        cs.addSymbol(sls1)
        
        let sls2 = AGSSimpleLineSymbol()
        sls2.color = NSColor.yellowColor()
        sls2.style = .Solid
        sls2.width = 4
        cs.addSymbol(sls2)
        
        return cs
    }

    //MARK: - Respond To Geometry Changed
    
    // -------------------------------------------------------------------------------
    //  respondToGeometryChanged:aNotification
    // -------------------------------------------------------------------------------
    func respondToGeometryChanged(aNotification:NSNotification) {
    
        //enable/disable add/clear buttons
        self.clearSketchButton.enabled = !self.sketchLayer.geometry.isEmpty() && self.sketchLayer.geometry != nil
        self.addStopOrBarrierButton.enabled = self.sketchLayer.geometry.isValid()
    }
    
    //MARK: - Actions
    
    // -------------------------------------------------------------------------------
    //  stopsBarriersAction:sender
    // -------------------------------------------------------------------------------
    @IBAction func stopsBarriersAction(sender:NSSegmentedControl) {
    
        if sender.selectedSegment == 0 {
            //clear sketch geometry
            self.sketchLayer.clear()
            
            //disable polyline and polygon sketch tools
            self.sketchTools.setEnabled(true, forSegment:0)
            self.sketchTools.setEnabled(false, forSegment:1)
            self.sketchTools.setEnabled(false, forSegment:2)
            
            self.mapView.touchDelegate = self.sketchLayer
            self.sketchLayer.geometry = AGSMutablePoint(spatialReference: self.mapView.spatialReference)
        }
        else {
        
            //clear sketch geometry
            self.sketchLayer.clear()
            
            //enable all sketch tools
            self.sketchTools.setEnabled(true, forSegment:0)
            self.sketchTools.setEnabled(true, forSegment:1)
            self.sketchTools.setEnabled(true, forSegment:2)
        }
    }
    
    // -------------------------------------------------------------------------------
    //  sketchToolsAction:sender
    // -------------------------------------------------------------------------------
    @IBAction func sketchToolsAction(sender:NSSegmentedControl) {
        
        switch sender.selectedSegment {
            case 0://point tool
            //sketch layer should begin tracking touch events to sketch a point
            self.mapView.touchDelegate = self.sketchLayer
            self.sketchLayer.geometry = AGSMutablePoint(spatialReference: self.mapView.spatialReference)
            break
            
            case 1://polyline tool
            //sketch layer should begin tracking touch events to sketch a polyline
            self.mapView.touchDelegate = self.sketchLayer
            self.sketchLayer.geometry = AGSMutablePolyline(spatialReference: self.mapView.spatialReference)
            break
            
            case 2://polygon tool
            //sketch layer should begin tracking touch events to sketch a polygon
            self.mapView.touchDelegate = self.sketchLayer
            self.sketchLayer.geometry = AGSMutablePolygon(spatialReference: self.mapView.spatialReference)
            break
            
            default:
            break
        }
    
    }
    
    // -------------------------------------------------------------------------------
    //  addStopOrBarrierAction:sender
    // -------------------------------------------------------------------------------
    @IBAction func addStopOrBarrierAction(sender:AnyObject) {
    
        //grab the geometry, then clear the sketch
        let geometry = self.sketchLayer.geometry.copy() as AGSGeometry
        self.sketchLayer.clear()
        
        //Prepare symbol and attributes for the Stop/Barrier
        let attributes = NSMutableDictionary()
        
        if self.stopsBarriersSegmentedControl.selectedSegment == 0 {
        
            //increase counter
            self.numStops++
            
            //set stop symbol
            let symbol = self.stopSymbolWithNumber(Int(self.numStops))
            
            //create stop graphic
            let stopGraphic = AGSStopGraphic(geometry: geometry, symbol: symbol, attributes: attributes)
            //set stop graphic sequence
            //You can set additional properties on the stop here
            //refer to the conceptual helf for Routing task
            stopGraphic.sequence = self.numStops
            
            //add graphic to stop graphics array
            self.stops.append(stopGraphic)
            
            //add stop graphic to graphics layer
            self.graphicsLayer.addGraphic(stopGraphic)
        }
        else {
        
            //increase counter
            self.numBarriers++
            
            //set barrierNumber attribute
            //you can set additional properties on the barrier here
            //refer to the conceptual help for Routing task
            attributes["barrierNumber"] = NSNumber(unsignedLong:self.numBarriers)
            
            //create a barrier graphic
            let g = AGSGraphic(geometry: geometry, symbol: nil, attributes: attributes)
            
            //set symbol based on type of geometry
            if geometry is AGSPoint {
            
                //set symbol
                g.symbol = self.pointBarrierSymbol()
                
                //add to point barriers array
                self.pointBarriers.append(g)
            }
            else if geometry is AGSPolyline {
            
                //set symbol
                g.symbol = self.polylineBarrierSymbol()
                
                //add to point barriers array
                self.polylineBarriers.append(g)
            }
            else if geometry is AGSPolygon {
            
                //set symbol
                g.symbol = self.polygonBarrierSymbol()
                
                //add to point barriers array
                self.polygonBarriers.append(g)
            }
            
            //add to graphics layer
            self.graphicsLayer.addGraphic(g)
        }
        
        if self.stops.count > 1 {
            self.routeButton.enabled = true
            self.messageLabel.stringValue = "Add another stop/barrier or click Route button"
        }
    }
    
    // -------------------------------------------------------------------------------
    //  clearSketchAction:sender
    // -------------------------------------------------------------------------------
    @IBAction func clearSketchAction(sender:AnyObject) {
        //clear sketch geometry
        self.sketchLayer.clear()
    }
    
    // -------------------------------------------------------------------------------
    //  routeAction:sender
    // -------------------------------------------------------------------------------
    @IBAction func routeAction(sender:AnyObject) {
        
        // update message
        self.messageLabel.stringValue = "Routing..."
        
        //clear sketch layer
        self.sketchLayer.clear()
        
        //remove sketch layer as mapView's touch delegate
        self.mapView.touchDelegate = nil
        
        // set the stops and  barriers on the parameters object
        if self.stops.count > 0 {
            self.routeTaskParameters.setStopsWithFeatures(self.stops)
        }
        if self.pointBarriers.count > 0 {
            self.routeTaskParameters.setPointBarriersWithFeatures(self.pointBarriers)
        }
        if self.polylineBarriers.count > 0 {
            self.routeTaskParameters.setPolylineBarriersWithFeatures(self.polylineBarriers)
        }
        if self.polygonBarriers.count > 0 {
            self.routeTaskParameters.setPolygonBarriersWithFeatures(self.polygonBarriers)
        }
        
        // 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
        
        // the next 3 lines will cause the task to find the
        // best route regardless of the stop input order
        self.routeTaskParameters.findBestSequence = true
        self.routeTaskParameters.preserveFirstStop = true
        self.routeTaskParameters.preserveLastStop = false
        
        // since we used "findBestSequence" we need to
        // get the newly reordered stops
        self.routeTaskParameters.returnStopGraphics = true
        
        // ensure the graphics are returned in our map's spatial reference
        self.routeTaskParameters.outSpatialReference = self.mapView.spatialReference
        
        // let's ignore invalid locations
        self.routeTaskParameters.ignoreInvalidLocations = true
        
        // you can also set additional properties here that should
        // be considered during analysis.
        // See the conceptual help for Routing task.
        
        // execute the route task
        self.routeTask.solveWithParameters(self.routeTaskParameters)
    }
    
    // -------------------------------------------------------------------------------
    //  directionsAction:sender
    // -------------------------------------------------------------------------------
    @IBAction func directionsAction(directionsButton:NSButton) {
    
        //reload table
        self.tableView.reloadData()
    
        if !self.popover.shown {
            //show popover
            self.popover.showRelativeToRect(directionsButton.bounds, ofView:directionsButton, preferredEdge:NSMaxYEdge)
        } else {
            //remove current direction graphic
            self.graphicsLayer.removeGraphic(self.currentDirectionGraphic)
            
            //close popover
            self.popover.close()
        }
    }
    
    // -------------------------------------------------------------------------------
    //  resetAction:sender
    // -------------------------------------------------------------------------------
    @IBAction func resetAction(sender:AnyObject) {
        
        // set stop counter back to 0
        self.numStops = 0
        
        // set barrier counter back to 0
        self.numBarriers = 0
        
        //reset stops and barriers array
        self.stops = Array<AGSStopGraphic>()
        self.pointBarriers = Array<AGSGraphic>()
        self.polylineBarriers = Array<AGSGraphic>()
        self.polygonBarriers = Array<AGSGraphic>()
        
        // remove all graphics
        self.graphicsLayer.removeAllGraphics()
        
        //clear sketch layer
        self.sketchLayer.clear()
        
        //set sketch layer as mapView's touch delegate
        self.mapView.touchDelegate = self.sketchLayer
        
        //disable directions button
        self.directionsButton.enabled = false
        
        //disable route button
        self.routeButton.enabled = false
        
        //update message
        self.messageLabel.stringValue = "Tap on the map to add stops and barriers"
    }
    
    //MARK: - RouteTask Delegate
    
    // -------------------------------------------------------------------------------
    //  routeTask:didRetrieveDefaultRouteTaskParameters
    //
    //  this will be called when route task retrieve default parameters successfully
    // -------------------------------------------------------------------------------
    func routeTask(routeTask: AGSRouteTask!, operation op: NSOperation!, didRetrieveDefaultRouteTaskParameters routeParams: AGSRouteTaskParameters!) {
        self.routeTaskParameters = routeParams
    }
    
    // -------------------------------------------------------------------------------
    //  routeTask:didFailToRetrieveDefaultRouteTaskParametersWithError
    //
    //  this will be called when route tssk fails to retrieve default parameters
    // -------------------------------------------------------------------------------
    func routeTask(routeTask: AGSRouteTask!, operation op: NSOperation!, didFailToRetrieveDefaultRouteTaskParametersWithError error: NSError!) {
        // let the user know the retrieval failed
        self.showError(error, withMessageText:"Failed to retrieve default route parameters")
    }
    
    // -------------------------------------------------------------------------------
    //  routeTask:didSolveWithResult
    //
    //  this will be called when route task solve route successfully
    // -------------------------------------------------------------------------------
    func routeTask(routeTask: AGSRouteTask!, operation op: NSOperation!, didSolveWithResult routeTaskResult: AGSRouteTaskResult!) {
        
        // update message
        self.messageLabel.stringValue = "Routing completed. See directions..."
        
        // we know that we are only dealing with 1 route...
        self.routeResult = routeTaskResult.routeResults.last as AGSRouteResult
        if self.routeResult != nil {
        
            //set direction graphics
            self.directionGraphics = self.routeResult.directions.graphics as [AGSDirectionGraphic]
            
            //disable route button
            self.routeButton.enabled = false
            
            //enable directions button
            self.directionsButton.enabled = true
            
            //disable sketch tools
            self.sketchTools.setEnabled(false, forSegment:0)
            self.sketchTools.setEnabled(false, forSegment:1)
            self.sketchTools.setEnabled(false, forSegment:2)
            
            // symbolize the returned route graphic
            self.routeResult.routeGraphic.symbol = self.routeSymbol()
            
            // add the route graphic to the graphic's layer
            self.graphicsLayer.addGraphic(self.routeResult.routeGraphic)
            
            // remove the stop graphics from the graphics layer
            // careful not to attempt to mutate the graphics array while
            // it is being enumerated
            let graphics = self.graphicsLayer.graphics
            for g in self.graphicsLayer.graphics {
                if g is AGSStopGraphic {
                    self.graphicsLayer.removeGraphic(g as AGSGraphic)
                }
            }
        
        
            // add the returned stops...it's possible these came back in a different order
            // because we specified findBestSequence
            for sg in self.routeResult.stopGraphics as [AGSStopGraphic] {
            
                // get the sequence from the attributes
                let sequence = Int(sg.attributeAsIntForKey("Sequence", exists:nil))
                
                // create a composite symbol using the sequence number
                sg.symbol = self.stopSymbolWithNumber(sequence)
                
                // add the graphic
                self.graphicsLayer.addGraphic(sg)
            }
            
            //zoom to graphics layer
            self.mapView.zoomToGeometry(self.graphicsLayer.fullEnvelope, withPadding:150, animated:true)
        }
    }
    
    // -------------------------------------------------------------------------------
    //  routeTask:didFailSolveWithError
    //
    //  this will be called when route tssk fails to solve route
    // -------------------------------------------------------------------------------
    func routeTask(routeTask: AGSRouteTask!, operation op: NSOperation!, didFailSolveWithError error: NSError!) {
    
        //update message
        self.messageLabel.stringValue = "Routing failed"
        
        // the solve route failed...
        // let the user know
        self.showError(error, withMessageText:"Routing failed")
    }
    
    //MARK: - NSTableView Delegate/Datasource Methods
    
    // -------------------------------------------------------------------------------
    //  numberOfRowsInTableView:tableView
    // -------------------------------------------------------------------------------
    func numberOfRowsInTableView(tableView: NSTableView) -> Int {
        return self.directionGraphics.count
    }
    
    // -------------------------------------------------------------------------------
    //  tableView:viewForTableColumn:row
    // -------------------------------------------------------------------------------
    func tableView(tableView: NSTableView, viewForTableColumn tableColumn: NSTableColumn?, row: Int) -> NSView? {
        
        let result = tableView.makeViewWithIdentifier(tableColumn!.identifier, owner:self) as DirectionsTableCellViewSwift
        
        if self.directionGraphics.count > 0 {
            let directionGraphic = self.directionGraphics[row]
            result.imageView?.image = self.imageForDirectionManeuverType(directionGraphic.maneuverType)
            result.textField?.stringValue = directionGraphic.text
            
            if directionGraphic.length > 0 {
                result.directionTextField.stringValue = String(format: "Drive %.2f miles", directionGraphic.length)
                if result.textField != nil {
                    result.textField!.setFrameOrigin( NSMakePoint(result.textField!.frame.origin.x,4))
                }
            }
            else {
                result.directionTextField.stringValue = ""
                if result.textField != nil {
                    result.textField!.setFrameOrigin(NSMakePoint(result.textField!.frame.origin.x,11))
                }
            }
        }
        
        return result
    }
    
    // -------------------------------------------------------------------------------
    //  tableViewSelectionDidChange:aNotification
    // -------------------------------------------------------------------------------
    func tableViewSelectionDidChange(notification: NSNotification) {
        
        if self.directionGraphics.count > 0 {
            
            //remove current direction graphic, so we can display next one
            self.graphicsLayer.removeGraphic(self.currentDirectionGraphic)
            
            //get current direction and add it to the graphics layer
            self.currentDirectionGraphic = self.directionGraphics[notification.object!.selectedRow]
            self.currentDirectionGraphic.symbol = self.currentDirectionSymbol()
            self.graphicsLayer.addGraphic(self.currentDirectionGraphic)
            
            //zoom to envelope of the current direction
            let env = self.currentDirectionGraphic.geometry.envelope
            self.mapView.zoomToGeometry(env, withPadding:150, animated:true)
        }
    }
    
    //MARK: - Popover Delegate
    
    // -------------------------------------------------------------------------------
    //  popoverDidClose:notification
    // -------------------------------------------------------------------------------
    func popoverDidClose(notification: NSNotification) {
        //remove current direction graphic
        self.graphicsLayer.removeGraphic(self.currentDirectionGraphic)
    }
    
    //MARK: - Helper Message
    
    // -------------------------------------------------------------------------------
    //  imageForDirectionManeuverType:maneuverType
    // -------------------------------------------------------------------------------
    func imageForDirectionManeuverType(maneuverType: AGSNADirectionsManeuver) -> NSImage {
        
        //image
        var image:NSImage!
        
        switch (maneuverType.rawValue) {
        case 1:/*!< Arrive At Stop. */
            image = NSImage(named: "speedometer")
            break
        case 2:/*!< Go straight. */
            image = NSImage(named: "ManeuverImageStraightArrow")
            break
        case 3:/*!< Bear left.*/
            image = NSImage(named: "ManeuverImageBearLeft")
            break
        case 4:/*!< Bear right. */
            image = NSImage(named: "ManeuverImageBearRight")
            break
        case 5:/*!< Turn left.*/
            image = NSImage(named: "ManeuverImageTurnLeft")
            break
        case 6:/*!< Turn right. */
            image = NSImage(named: "ManeuverImageTurnRight")
            break
        case 7:/*!< Make sharp left. */
            image = NSImage(named: "ManeuverImageTurnSharpLeft")
            break
        case 8:/*!< Make sharp right. */
            image = NSImage(named: "ManeuverImageTurnSharpRight")
            break
        case 9:/*!< Make a U-turn. */
            image = NSImage(named: "ManeuverImageUTurn")
            break
        case 10:/*!< Take ferry. */
            image = NSImage(named: "ManeuverImageTakeFerry")
            break
        case 11:/*!< Take roundabout. */
            image = NSImage(named: "ManeuverImageGetOnRoundabout")
            break
        case 12:/*!< Merge to highway. */
            image = NSImage(named: "ManeuverImageMergeOntoHighway")
            break
        case 13:/*!< Exit highway. */
            image = NSImage(named: "ManeuverImageTakeExit")
            break
        case 14:/*!< Go on another highway. */
            image = NSImage(named: "ManeuverImageHighwayChange")
            break
        case 15:/*!< At fork keep center. */
            image = NSImage(named: "ManeuverImageTakeCenterFork")
            break
        case 16:/*!< At fork keep left. */
            image = NSImage(named: "ManeuverImageTakeForkLeft")
            break
        case 17:/*!< At fork keep right. */
            image = NSImage(named: "ManeuverImageTakeForkRight")
            break
        case 18:/*!< Depart stop. */
            image = NSImage(named: "speedometer")
            break
        case 20:/*!< End of ferry. */
            image = NSImage(named: "ManeuverImageGetOffFerry")
            break
        case 21:/*!< Ramp right. */
            image = NSImage(named: "ManeuverImageTakeRampRight")
            break
        case 22:/*!< Ramp left. */
            image = NSImage(named: "ManeuverImageTakeRampLeft")
            break
        default:
            break
        }
        
        return image
    }
    
    //MARK: - Show Error
    
    // -------------------------------------------------------------------------------
    //  showError:withMessageText
    // -------------------------------------------------------------------------------
    func showError(error: NSError, withMessageText messageText:String) {
        if let viewWindow = self.view.window {
            let alert = NSAlert()
            alert.messageText = messageText
            alert.informativeText = error.localizedDescription
            alert.beginSheetModalForWindow(viewWindow, modalDelegate:self, didEndSelector:nil, contextInfo:nil)
        }
    }
    
    //MARK: - deinit
    
    // -------------------------------------------------------------------------------
    //  deinit
    // -------------------------------------------------------------------------------
    deinit {
    
        //remove observer...
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }
}
//OBJECTIVE C SAMPLE CODE
/*
 Copyright 2013 Esri
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
 
 http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

#import "RoutingSample.h"

//layer/routetask urls
#define kBaseMapURL @"http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer"
#define kRouteTaskURL @"http://sampleserver6.arcgisonline.com/arcgis/rest/services/NetworkAnalysis/SanDiego/NAServer/Route"

#pragma mark - DirectionsTableCellView

@interface DirectionsTableCellView: NSTableCellView
@property (strong) IBOutlet NSTextField *directionTextField;
@end

@implementation DirectionsTableCellView
@end

#pragma mark - RoutingSample


@interface RoutingSample () {
    BOOL _awaken;
}
@end

@implementation RoutingSample

#pragma mark - awakeFromNib

// -------------------------------------------------------------------------------
//  awakeFromNib
// -------------------------------------------------------------------------------
- (void)awakeFromNib {
    
    if (!_awaken) {
        
        //add base layer to map
        AGSTiledMapServiceLayer *baseMapLayer = [[AGSTiledMapServiceLayer alloc] initWithURL:[NSURL URLWithString:kBaseMapURL]];
        [self.mapView addMapLayer:baseMapLayer withName:@"Base Layer"];
        
        //add graphics layer
        self.graphicsLayer = [AGSGraphicsLayer graphicsLayer];
        [self.mapView addMapLayer:self.graphicsLayer withName:@"Graphics Layer"];
        
        //add sketch graphics layer and set as mapView's touch delegate so it begin tracking touch events
        self.sketchLayer = [[AGSSketchGraphicsLayer alloc] initWithGeometry:[[AGSMutablePoint alloc] initWithSpatialReference:[AGSSpatialReference webMercatorSpatialReference]]];
        self.mapView.touchDelegate = self.sketchLayer;
        [self.mapView addMapLayer:self.sketchLayer withName:@"Sketch Layer"];
        
        //setup the route task and set delegate
        self.routeTask = [AGSRouteTask routeTaskWithURL:[NSURL URLWithString:kRouteTaskURL]];        
        self.routeTask.delegate = self;
        
        //kick off asynchronous method to retrieve default parameters for the route task
        [self.routeTask retrieveDefaultRouteTaskParameters];
        
        //init stops and barriers array
        self.stops = [NSMutableArray array];
        self.pointBarriers = [NSMutableArray array];
        self.polylineBarrieres = [NSMutableArray array];
        self.polygonBarrieres = [NSMutableArray array];
        
        //register for "Geometry Changed" notifications
        //we want to enable/disable UI elements when sketch geometry is modified
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(respondToGeometryChanged:) name:AGSSketchGraphicsLayerGeometryDidChangeNotification object:self.sketchLayer];
        
        //set message
        self.messageLabel.stringValue = @"Tap on the map to add stops and barriers";
        
        //zoom to predefined extend with known spatial reference of the map
        AGSEnvelope *envelope = [AGSEnvelope envelopeWithXmin:-13044000 ymin:3855000 xmax:-13040000 ymax:3858000 spatialReference:[AGSSpatialReference webMercatorSpatialReference]];
        [self.mapView zoomToEnvelope:envelope animated:YES];
        [self.mapView.maxEnvelope envelope];
        
        _awaken = YES;
    }
}

#pragma mark - Symbols

// -------------------------------------------------------------------------------
//  stopSymbolWithNumber:stopNumber
// -------------------------------------------------------------------------------
- (AGSCompositeSymbol*)stopSymbolWithNumber:(NSInteger)stopNumber {
    
    //init composite symbol
	AGSCompositeSymbol *cs = [AGSCompositeSymbol compositeSymbol];
	
    // create main circle
	AGSSimpleMarkerSymbol *sms = [AGSSimpleMarkerSymbol simpleMarkerSymbol];
	sms.color = [NSColor greenColor];
	sms.size = CGSizeMake(20, 20);
	sms.style = AGSSimpleMarkerSymbolStyleCircle;
	[cs addSymbol:sms];
	
    // add number as a text symbol
    AGSTextSymbol *ts = [[AGSTextSymbol alloc] initWithText:[NSString stringWithFormat:@"%i", (int)stopNumber] color:[NSColor blackColor]];
    ts.vAlignment = AGSTextSymbolVAlignmentMiddle;
    ts.hAlignment = AGSTextSymbolHAlignmentCenter;
    ts.fontSize	= 16;
    [cs addSymbol:ts];
	
	return cs;
}

// -------------------------------------------------------------------------------
//  pointBarrierSymbol
// -------------------------------------------------------------------------------
- (AGSSimpleMarkerSymbol*)pointBarrierSymbol {
    //create a simple marker symbol 
    AGSSimpleMarkerSymbol *sms = [AGSSimpleMarkerSymbol simpleMarkerSymbol];
	sms.color = [NSColor redColor];
	sms.size = CGSizeMake(20, 20);
	sms.style = AGSSimpleMarkerSymbolStyleCircle;
    return sms;
}

// -------------------------------------------------------------------------------
//  polylineBarrierSymbol
// -------------------------------------------------------------------------------
- (AGSSimpleLineSymbol*)polylineBarrierSymbol {
    //create a simple line symbol
    AGSSimpleLineSymbol *sls = [AGSSimpleLineSymbol simpleLineSymbol];
	sls.color = [NSColor redColor];
	sls.style = AGSSimpleLineSymbolStyleSolid;
	sls.width = 4;
    return sls;
}

// -------------------------------------------------------------------------------
//  polygonBarrierSymbol
// -------------------------------------------------------------------------------
- (AGSSimpleFillSymbol*)polygonBarrierSymbol {
    //create simple fill symbol
    AGSSimpleFillSymbol *sfs = [AGSSimpleFillSymbol simpleFillSymbol];
	sfs.style = AGSSimpleFillSymbolStyleSolid;
	sfs.color = [[NSColor redColor] colorWithAlphaComponent:0.5];
    sfs.outline.width = 4;
    sfs.outline.color = [NSColor redColor];
    return sfs;
}

// -------------------------------------------------------------------------------
//  routeSymbol
// -------------------------------------------------------------------------------
- (AGSCompositeSymbol*)routeSymbol {
    
    //init composite symbol
	AGSCompositeSymbol *cs = [AGSCompositeSymbol compositeSymbol];
	
	AGSSimpleLineSymbol *sls1 = [AGSSimpleLineSymbol simpleLineSymbol];
    sls1.color = [NSColor blueColor];
	sls1.style = AGSSimpleLineSymbolStyleSolid;
	sls1.width = 6;
	[cs addSymbol:sls1];
	
	AGSSimpleLineSymbol *sls2 = [AGSSimpleLineSymbol simpleLineSymbol];
	sls2.color = [NSColor colorWithRed:0/255.0f green:170/255.0f blue:255/255.0f alpha:1.0];
	sls2.style = AGSSimpleLineSymbolStyleSolid;
	sls2.width = 4;
	[cs addSymbol:sls2];
	
	return cs;
}

// -------------------------------------------------------------------------------
//  currentDirectionSymbol
// -------------------------------------------------------------------------------
- (AGSCompositeSymbol*)currentDirectionSymbol {
    
    //init composite symbol
	AGSCompositeSymbol *cs = [AGSCompositeSymbol compositeSymbol];
	
	AGSSimpleLineSymbol *sls1 = [AGSSimpleLineSymbol simpleLineSymbol];
	sls1.color = [NSColor blackColor];
	sls1.style = AGSSimpleLineSymbolStyleSolid;
	sls1.width = 8;
	[cs addSymbol:sls1];
	
	AGSSimpleLineSymbol *sls2 = [AGSSimpleLineSymbol simpleLineSymbol];
	sls2.color = [NSColor yellowColor];
	sls2.style = AGSSimpleLineSymbolStyleSolid;
	sls2.width = 4;
	[cs addSymbol:sls2];
	
	return cs;
}

#pragma mark - Respond To Geometry Changed

// -------------------------------------------------------------------------------
//  respondToGeometryChanged:aNotification
// -------------------------------------------------------------------------------
- (void)respondToGeometryChanged:(NSNotification*)aNotification {
    
    //enable/disable add/clear buttons
    self.clearSketchButton.enabled = ![self.sketchLayer.geometry isEmpty] && self.sketchLayer.geometry!=nil;
    self.addStopOrBarrierButton.enabled = [self.sketchLayer.geometry isValid];
}

#pragma mark - Actions

// -------------------------------------------------------------------------------
//  stopsBarriersAction:sender
// -------------------------------------------------------------------------------
- (IBAction)stopsBarriersAction:(id)sender {
    
    if ([sender selectedSegment] == 0) {
        
        //clear sketch geometry
        [self.sketchLayer clear];
        
        //disable polyline and polygon sketch tools
        [self.sketchTools setEnabled:YES forSegment:0];
        [self.sketchTools setEnabled:NO forSegment:1];
        [self.sketchTools setEnabled:NO forSegment:2];
        
        [self.sketchTools setSelected:YES forSegment:0];
        self.mapView.touchDelegate = self.sketchLayer;
        self.sketchLayer.geometry = [[AGSMutablePoint alloc] initWithSpatialReference:self.mapView.spatialReference];
    }
    else {
        
        //clear sketch geometry
        [self.sketchLayer clear];
        
        //enable all sketch tools
        [self.sketchTools setEnabled:YES forSegment:0];
        [self.sketchTools setEnabled:YES forSegment:1];
        [self.sketchTools setEnabled:YES forSegment:2];
    }
}

// -------------------------------------------------------------------------------
//  sketchToolsAction:sender
// -------------------------------------------------------------------------------
- (IBAction)sketchToolsAction:(id)sender {
    
    switch ([sender selectedSegment]) {
		case 0://point tool
			//sketch layer should begin tracking touch events to sketch a point
			self.mapView.touchDelegate = self.sketchLayer;
			self.sketchLayer.geometry = [[AGSMutablePoint alloc] initWithSpatialReference:self.mapView.spatialReference];
			break;
            
		case 1://polyline tool
			//sketch layer should begin tracking touch events to sketch a polyline
			self.mapView.touchDelegate = self.sketchLayer;
			self.sketchLayer.geometry = [[AGSMutablePolyline alloc] initWithSpatialReference:self.mapView.spatialReference];
			break;
            
		case 2://polygon tool
			//sketch layer should begin tracking touch events to sketch a polygon
			self.mapView.touchDelegate = self.sketchLayer;
			self.sketchLayer.geometry = [[AGSMutablePolygon alloc] initWithSpatialReference:self.mapView.spatialReference];
			break;

		default:
			break;
	}
    
}

// -------------------------------------------------------------------------------
//  addStopOrBarrierAction:sender
// -------------------------------------------------------------------------------
- (IBAction)addStopOrBarrierAction:(id)sender {
    
    //grab the geometry, then clear the sketch
	AGSGeometry *geometry = [self.sketchLayer.geometry copy];
	[self.sketchLayer clear];
	
	//Prepare symbol and attributes for the Stop/Barrier
	NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
    
    if ([self.stopsBarriersSegmentedControl selectedSegment] == 0) {
        
        //increase counter
        self.numStops++;
        
        //set stop symbol
        AGSSymbol *symbol = [self stopSymbolWithNumber:_numStops];
        
        //create stop graphic
        AGSStopGraphic *stopGraphic = [AGSStopGraphic graphicWithGeometry:geometry
                                                                   symbol:symbol
                                                               attributes:attributes];
        //set stop graphic sequence
        //You can set additional properties on the stop here
        //refer to the conceptual helf for Routing task
        stopGraphic.sequence = self.numStops;
        
        //add graphic to stop graphics array
        [self.stops addObject:stopGraphic];

        //add stop graphic to graphics layer
        [self.graphicsLayer addGraphic:stopGraphic];
    }
    else {
                
        //increase counter
        self.numBarriers++;
        
        //set barrierNumber attribute
        //you can set additional properties on the barrier here
        //refer to the conceptual helf for Routing task
        [attributes setValue:[NSNumber numberWithInt:self.numBarriers] forKey:@"barrierNumber"];
        
        //create a barrier graphic
        AGSGraphic *g = [AGSGraphic graphicWithGeometry:geometry symbol:nil attributes:attributes];
        
        //set symbol based on type of geometry
        if ([geometry isKindOfClass:[AGSPoint class]]) {
            
            //set symbol
            g.symbol = [self pointBarrierSymbol];
            
            //add to point barriers array
            [self.pointBarriers addObject:g];
        }
        else if ([geometry isKindOfClass:[AGSPolyline class]]) {
            
            //set symbol
            g.symbol = [self polylineBarrierSymbol];
            
            //add to point barriers array
            [self.polylineBarrieres addObject:g];
        }
        else if ([geometry isKindOfClass:[AGSPolygon class]]) {
            
            //set symbol
            g.symbol = [self polygonBarrierSymbol];
                       
            //add to point barriers array
            [self.polygonBarrieres addObject:g];
        }

        //add to graphics layer
        [self.graphicsLayer addGraphic:g];
    }
    
    if ([self.stops count] > 1) {
        [self.routeButton setEnabled:YES];
        self.messageLabel.stringValue = @"Add another stop/barrier or click Route button";
    }
}

// -------------------------------------------------------------------------------
//  clearSketchAction:sender
// -------------------------------------------------------------------------------
- (IBAction)clearSketchAction:(id)sender {
    //clear sketch geometry
    [self.sketchLayer clear];
}

// -------------------------------------------------------------------------------
//  routeAction:sender
// -------------------------------------------------------------------------------
- (IBAction)routeAction:(id)sender {
    
    // update message
	self.messageLabel.stringValue = @"Routing...";
    
    //clear sketch layer
    [self.sketchLayer clear];
    
    //remove sketch layer as mapView's touch delegate
    self.mapView.touchDelegate = nil;
	    	
	// set the stops and  barriers on the parameters object
	if (self.stops.count > 0) {
		[self.routeTaskParameters setStopsWithFeatures:self.stops];
	}
    if (self.pointBarriers.count > 0) {
		[self.routeTaskParameters setPointBarriersWithFeatures:self.pointBarriers];
	}
    if (self.polylineBarrieres.count > 0) {
		[self.routeTaskParameters setPolylineBarriersWithFeatures:self.polylineBarrieres];
	}
    if (self.polygonBarrieres.count > 0) {
		[self.routeTaskParameters setPolygonBarriersWithFeatures:self.polygonBarrieres];
	}
	
	// this generalizes the route graphics that are returned
	self.routeTaskParameters.outputGeometryPrecision = 5.0;
	self.routeTaskParameters.outputGeometryPrecisionUnits = AGSUnitsMeters;
    
    // return the graphic representing the entire route, generalized by the previous
    // 2 properties: outputGeometryPrecision and outputGeometryPrecisionUnits
	self.routeTaskParameters.returnRouteGraphics = YES;
    
	// this returns turn-by-turn directions
	self.routeTaskParameters.returnDirections = YES;
	
	// the next 3 lines will cause the task to find the
	// best route regardless of the stop input order
	self.routeTaskParameters.findBestSequence = YES;
	self.routeTaskParameters.preserveFirstStop = YES;
	self.routeTaskParameters.preserveLastStop = NO;
	
	// since we used "findBestSequence" we need to
	// get the newly reordered stops
	self.routeTaskParameters.returnStopGraphics = YES;
	
	// ensure the graphics are returned in our map's spatial reference
	self.routeTaskParameters.outSpatialReference = self.mapView.spatialReference;
	
	// let's ignore invalid locations
	self.routeTaskParameters.ignoreInvalidLocations = YES;
	
	// you can also set additional properties here that should
	// be considered during analysis.
	// See the conceptual help for Routing task.
	
	// execute the route task
	[self.routeTask solveWithParameters:self.routeTaskParameters];
}

// -------------------------------------------------------------------------------
//  directionsAction:sender
// -------------------------------------------------------------------------------
- (IBAction)directionsAction:(id)sender {
    
    //reload table
    [self.tableView reloadData];
        
    NSButton *directionsButton = (NSButton*)sender;
    if (!self.popover.shown) {
        //show popover
        [self.popover showRelativeToRect:[directionsButton bounds] ofView:directionsButton preferredEdge:NSMaxYEdge];
    } else {
        //remove current direction graphic
        [self.graphicsLayer removeGraphic:self.currentDirectionGraphic];
                
        //close popover
        [self.popover close];
    }
}

// -------------------------------------------------------------------------------
//  resetAction:sender
// -------------------------------------------------------------------------------
- (IBAction)resetAction:(id)sender {
    
    // set stop counter back to 0
	self.numStops = 0;
	
	// set barrier counter back to 0
	self.numBarriers = 0;
    
    //reset stops and barriers array
    self.stops = [NSMutableArray array];
    self.pointBarriers = [NSMutableArray array];
    self.polylineBarrieres = [NSMutableArray array];
    self.polygonBarrieres = [NSMutableArray array];
    
	// remove all graphics
	[self.graphicsLayer removeAllGraphics];
    
    //clear sketch layer
    [self.sketchLayer clear];
    
    //set sketch layer as mapView's touch delegate
    self.mapView.touchDelegate = self.sketchLayer;
    
    //disable directions button
    [self.directionsButton setEnabled:NO];
    
    //disable route button
    [self.routeButton setEnabled:NO];
    
    //update message
    self.messageLabel.stringValue = @"Tap on the map to add stops and barriers";
}


#pragma mark - RouteTask Delegate

// -------------------------------------------------------------------------------
//  routeTask:didRetrieveDefaultRouteTaskParameters
//
//  this will be called when route task retrieve default parameters successfully
// -------------------------------------------------------------------------------
- (void)routeTask:(AGSRouteTask *)routeTask operation:(NSOperation *)op didRetrieveDefaultRouteTaskParameters:(AGSRouteTaskParameters *)routeParams {
	self.routeTaskParameters = routeParams;
}

// -------------------------------------------------------------------------------
//  routeTask:didFailToRetrieveDefaultRouteTaskParametersWithError
//
//  this will be called when route tssk fails to retrieve default parameters
// -------------------------------------------------------------------------------
- (void)routeTask:(AGSRouteTask *)routeTask operation:(NSOperation *)op didFailToRetrieveDefaultRouteTaskParametersWithError:(NSError *)error {
	// let the user know the retrieval failed
    [self showError:error withMessageText:@"Failed to retrieve default route parameters"];
}

// -------------------------------------------------------------------------------
//  routeTask:didSolveWithResult
//
//  this will be called when route task solve route successfully
// -------------------------------------------------------------------------------
- (void)routeTask:(AGSRouteTask *)routeTask operation:(NSOperation *)op didSolveWithResult:(AGSRouteTaskResult *)routeTaskResult {
	
    // update message
    self.messageLabel.stringValue = @"Routing completed. See directions...";
	
	// we know that we are only dealing with 1 route...
	self.routeResult = [routeTaskResult.routeResults lastObject];
	if (self.routeResult) {
        
        //set direction graphics
        self.directionGraphics = self.routeResult.directions.graphics;
        
        //disable route button
        [self.routeButton setEnabled:NO];
        
        //enable directions button
        [self.directionsButton setEnabled:YES];
        
        //disable sketch tools
        [self.sketchTools setEnabled:NO forSegment:0];
        [self.sketchTools setEnabled:NO forSegment:1];
        [self.sketchTools setEnabled:NO forSegment:2];
        
		// symbolize the returned route graphic
		self.routeResult.routeGraphic.symbol = [self routeSymbol];
        
        // add the route graphic to the graphic's layer
		[self.graphicsLayer addGraphic:self.routeResult.routeGraphic];
		
        // remove the stop graphics from the graphics layer
        // careful not to attempt to mutate the graphics array while
        // it is being enumerated
		NSMutableArray *graphics = [self.graphicsLayer.graphics mutableCopy];
		for (AGSGraphic *g in graphics) {
			if ([g isKindOfClass:[AGSStopGraphic class]]) {
				[self.graphicsLayer removeGraphic:g];
			}
		}
		
        // add the returned stops...it's possible these came back in a different order
        // because we specified findBestSequence
		for (AGSStopGraphic *sg in self.routeResult.stopGraphics) {
            
            // get the sequence from the attribetus
            BOOL exists;
			NSInteger sequence = [sg attributeAsIntForKey:@"Sequence" exists:&exists];
            
            // create a composite symbol using the sequence number
			sg.symbol = [self stopSymbolWithNumber:sequence];
            
            // add the graphic
			[self.graphicsLayer addGraphic:sg];
		}
        
        //zoom to graphics layer
        [self.mapView zoomToGeometry:self.graphicsLayer.fullEnvelope withPadding:150 animated:YES];
	}
}

// -------------------------------------------------------------------------------
//  routeTask:didFailSolveWithError
//
//  this will be called when route tssk fails to solve route
// -------------------------------------------------------------------------------
- (void)routeTask:(AGSRouteTask *)routeTask operation:(NSOperation *)op didFailSolveWithError:(NSError *)error {
	
    //update message
    self.messageLabel.stringValue = @"Routing failed";
	
	// the solve route failed...
	// let the user know
    [self showError:error withMessageText:@"Routing failed"];
}

#pragma mark - NSTableView Delegate/Datasource Methods

// -------------------------------------------------------------------------------
//  numberOfRowsInTableView:tableView
// -------------------------------------------------------------------------------
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
    return [self.directionGraphics count];
}

// -------------------------------------------------------------------------------
//  tableView:viewForTableColumn:row
// -------------------------------------------------------------------------------
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
    
    DirectionsTableCellView *result = [tableView makeViewWithIdentifier:tableColumn.identifier owner:self];
    
    if ([self.directionGraphics count] > 0) {
        AGSDirectionGraphic *directionGraphic = [self.directionGraphics objectAtIndex:row];
        result.imageView.image = [self imageForDirectionManeuverType:directionGraphic.maneuverType];
        result.textField.stringValue = directionGraphic.text;
        if (directionGraphic.length > 0) {
            result.directionTextField.stringValue = [NSString stringWithFormat:@"Drive %.2f miles",directionGraphic.length];
            [result.textField setFrameOrigin:NSMakePoint(result.textField.frame.origin.x,4)];
        }
        else {
            result.directionTextField.stringValue = @"";
            [result.textField setFrameOrigin:NSMakePoint(result.textField.frame.origin.x,11)];
        }
    }
    
    return result;
}

// -------------------------------------------------------------------------------
//  tableViewSelectionDidChange:aNotification
// -------------------------------------------------------------------------------
- (void)tableViewSelectionDidChange: (NSNotification *)aNotification {
    
    if ([self.directionGraphics count] > 0) {
        
        //remove current direction graphic, so we can display next one
        [self.graphicsLayer removeGraphic:self.currentDirectionGraphic];
        
        //get current direction and add it to the graphics layer
        self.currentDirectionGraphic = [self.directionGraphics objectAtIndex:[aNotification.object selectedRow]];
        self.currentDirectionGraphic.symbol = [self currentDirectionSymbol];
        [self.graphicsLayer addGraphic:self.currentDirectionGraphic];
        
        //zoom to envelope of the current direction
        AGSMutableEnvelope *env = [self.currentDirectionGraphic.geometry.envelope mutableCopy];
        [self.mapView zoomToGeometry:env withPadding:150 animated:YES];
    }
}

#pragma mark - Popover Delegate

// -------------------------------------------------------------------------------
//  popoverDidClose:notification
// -------------------------------------------------------------------------------
- (void)popoverDidClose:(NSNotification *)notification {
    //remove current direction graphic
    [self.graphicsLayer removeGraphic:self.currentDirectionGraphic];
}

#pragma mark - Helper Message

// -------------------------------------------------------------------------------
//  imageForDirectionManeuverType:maneuverType
// -------------------------------------------------------------------------------
- (NSImage*)imageForDirectionManeuverType:(AGSNADirectionsManeuver)maneuverType {
    
    //image
    NSImage *image = nil;
    
    switch (maneuverType) {
		case 1:/*!< Arrive At Stop. */
            image = [NSImage imageNamed:@"speedometer"];
            break;
		case 2:/*!< Go straight. */
            image = [NSImage imageNamed:@"ManeuverImageStraightArrow"];
            break;
		case 3:/*!< Bear left.*/
            image = [NSImage imageNamed:@"ManeuverImageBearLeft"];
            break;
		case 4:/*!< Bear right. */
            image = [NSImage imageNamed:@"ManeuverImageBearRight"];
            break;
		case 5:/*!< Turn left.*/
            image = [NSImage imageNamed:@"ManeuverImageTurnLeft"];
            break;
		case 6:/*!< Turn right. */
            image = [NSImage imageNamed:@"ManeuverImageTurnRight"];
            break;
		case 7:/*!< Make sharp left. */
            image = [NSImage imageNamed:@"ManeuverImageTurnSharpLeft"];
            break;
		case 8:/*!< Make sharp right. */
            image = [NSImage imageNamed:@"ManeuverImageTurnSharpRight"];
            break;
		case 9:/*!< Make a U-turn. */
            image = [NSImage imageNamed:@"ManeuverImageUTurn"];
            break;
		case 10:/*!< Take ferry. */
            image = [NSImage imageNamed:@"ManeuverImageTakeFerry"];
            break;
		case 11:/*!< Take roundabout. */
            image = [NSImage imageNamed:@"ManeuverImageGetOnRoundabout"];
            break;
		case 12:/*!< Merge to highway. */
            image = [NSImage imageNamed:@"ManeuverImageMergeOntoHighway"];
            break;
		case 13:/*!< Exit highway. */
            image = [NSImage imageNamed:@"ManeuverImageTakeExit"];
            break;
		case 14:/*!< Go on another highway. */
            image = [NSImage imageNamed:@"ManeuverImageHighwayChange"];
            break;
		case 15:/*!< At fork keep center. */
            image = [NSImage imageNamed:@"ManeuverImageTakeCenterFork"];
            break;
		case 16:/*!< At fork keep left. */
            image = [NSImage imageNamed:@"ManeuverImageTakeForkLeft"];
            break;
		case 17:/*!< At fork keep right. */
            image = [NSImage imageNamed:@"ManeuverImageTakeForkRight"];
            break;
		case 18:/*!< Depart stop. */
            image = [NSImage imageNamed:@"speedometer"];
            break;
		case 20:/*!< End of ferry. */
            image = [NSImage imageNamed:@"ManeuverImageGetOffFerry"];
            break;
		case 21:/*!< Ramp right. */
            image = [NSImage imageNamed:@"ManeuverImageTakeRampRight"];
            break;
		case 22:/*!< Ramp left. */
            image = [NSImage imageNamed:@"ManeuverImageTakeRampLeft"];
            break;
        default:
			break;
    }
    
    return image;
}

#pragma mark - Show Error

// -------------------------------------------------------------------------------
//  showError:withMessageText
// -------------------------------------------------------------------------------
- (void)showError:(NSError*)error withMessageText:(NSString*)messageText {
    
    NSAlert *alert = [[NSAlert alloc] init];
    [alert setMessageText:messageText];
    [alert setInformativeText:[NSString stringWithFormat:@"%@",[error localizedFailureReason]]];
    [alert beginSheetModalForWindow:self.view.window modalDelegate:self didEndSelector:nil contextInfo:nil];
}

#pragma mark - Dealloc

// -------------------------------------------------------------------------------
//  dealloc
// -------------------------------------------------------------------------------
- (void)dealloc {
    
    //remove observer...
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

@end
//OBJECTIVE C SAMPLE CODE
/*
 Copyright 2013 Esri
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
 
 http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

#import <Cocoa/Cocoa.h>

@interface RoutingSample : NSViewController <AGSRouteTaskDelegate>

@property (strong) IBOutlet AGSMapView *mapView;
@property (strong) IBOutlet NSSegmentedControl *stopsBarriersSegmentedControl;
@property (strong) IBOutlet NSSegmentedControl *sketchTools;
@property (strong) IBOutlet NSButton *addStopOrBarrierButton;
@property (strong) IBOutlet NSButton *clearSketchButton;
@property (strong) IBOutlet NSButton *routeButton;
@property (strong) IBOutlet NSButton *directionsButton;
@property (strong) IBOutlet NSTableView *tableView;
@property (strong) IBOutlet NSPopover *popover;
@property (strong) IBOutlet NSTextField *messageLabel;
@property (strong) AGSGraphicsLayer *graphicsLayer;
@property (strong) AGSSketchGraphicsLayer *sketchLayer;
@property (strong) AGSRouteTask *routeTask;
@property (strong) AGSRouteTaskParameters *routeTaskParameters;
@property (strong) AGSRouteResult *routeResult;
@property (strong) AGSDirectionGraphic *currentDirectionGraphic;
@property (strong) NSMutableArray *stops;
@property (strong) NSMutableArray *pointBarriers;
@property (strong) NSMutableArray *polylineBarrieres;
@property (strong) NSMutableArray *polygonBarrieres;
@property (strong) NSArray *directionGraphics;
@property (assign) int numStops;
@property (assign) int numBarriers;

- (IBAction)stopsBarriersAction:(id)sender;
- (IBAction)sketchToolsAction:(id)sender;
- (IBAction)addStopOrBarrierAction:(id)sender;
- (IBAction)clearSketchAction:(id)sender;
- (IBAction)routeAction:(id)sender;
- (IBAction)directionsAction:(id)sender;
- (IBAction)resetAction:(id)sender;

@end
Feedback on this topic?