Skip To Content

Get driving directions

In this topic

This topic describes how to build an iOS application to find driving directions. It's assumed that you've already added a map to your application and are able to find addresses and places as described in the Find places and addresses tutorial.

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. Allow a user to input locations for the route

There are a number of ways in which a user can provide locations for a route. The user can input an address, tap a location on the map, or use the device location. For the purpose of this Guide, you'll allow the user to specify the destination by geocoding an address. you'll then generate a route from the user's current location to that destination.

The Find places and addresses tutorial showed how to take an address or place name and display its location on the map. In this tutorial, you'll build on that and allow the user to route to that location by first tapping on the graphic and then tapping the accessory button in the callout. To do so, you need to set the view controller as the callout's delegate when the view loads. This will allow the callout to inform you by invoking the didClickAccessoryButtonForCallout: method when a user taps the accessory button.

- (void)viewDidLoad {
   [super viewDidLoad];

   //code from previous tutorial to add a basemap tiled layer
   NSURL* url = [NSURL URLWithString:@""];
   AGSTiledMapServiceLayer *tiledLayer = [AGSTiledMapServiceLayer tiledMapServiceLayerWithURL:url];
   [self.mapView addMapLayer:tiledLayer withName:@"Basemap Tiled Layer"];
   self.mapView.layerDelegate = self;
   //set the callout's delegate
   self.mapView.callout.delegate = self;


//code from previous tutorial to enable map's location display
- (void)mapViewDidLoad:(AGSMapView *) mapView {
		//do something now that the map is loaded
  //for example, show the current location on the map
	 [mapView.locationDisplay startDataSource];

When the user taps the accessory button, you'll retrieve the graphic for which the callout was being shown. You'll then obtain the graphic's geometry and use it as the destination for the route.

- (void) didClickAccessoryButtonForCallout:(AGSCallout *)callout {
 AGSGraphic* graphic = (AGSGraphic*) callout.representedObject;
 AGSGeometry* destinationLocation = graphic.geometry;
 [self routeTo:destinationLocation];

2. Calculate the route

In this section, you'll implement the routeToDestination: method and calculate a route from the device's current location 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. This is because the task performs its operations, such as invoking the route service, on a background thread. When the operation is complete, the task needs a way to inform you. It does so by invoking methods on the delegate. In this example, you'll set the view controller as the task's delegate by setting the task's delegate property to self and by making the view controller adopt the AGSRouteTaskDelegate protocol. As part of adopting the protocol, you need to add the declaration to the view controller's header file (.h), and implement relevant methods defined in the protocol in the view controller's implementation file (.m).

- (void) routeTo:(AGSGeometry*)destination{

   self.routeTask = [AGSRouteTask routeTaskWithURL:[NSURL URLWithString:@""] credential:nil];
   self.routeTask.delegate = self;	        


Now that you have a route task, you need to specify the route to calculate. To do this, instantiate an object of the AGSRouteTaskParameters class and set some properties on it. To specify the locations along the route, invoke setStopsWithFeatures: using the device location as the start point and the destination location as the end point. Set some additional route preferences on the parameters object, and invoke solveWithParameters: on the route task to calculate the route.


     AGSRouteTaskParameters* params = [[AGSRouteTaskParameters alloc] init];

     AGSStopGraphic* firstStop = [AGSStopGraphic graphicWithGeometry:[self.mapView.locationDisplay mapLocation] symbol:nil attributes:nil ];
     AGSStopGraphic* lastStop = [AGSStopGraphic graphicWithGeometry:destination symbol:nil attributes:nil ];
     [params setStopsWithFeatures:@[firstStop, lastStop]];

    	//This returns entire route
    	params.returnRouteGraphics = YES;
    	//This returns turn-by-turn directions
    	params.returnDirections = YES;
    	//We don't want our stops reordered
    	params.findBestSequence = NO;
    	params.preserveFirstStop = YES;
    	params.preserveLastStop = YES;
    	// ensure the graphics are returned in our map's spatial reference
    	params.outSpatialReference = self.mapView.spatialReference;
    	//Don't ignore invalid stops, raise error instead
    	params.ignoreInvalidLocations = NO;
     [self.routeTask solveWithParameters:params];


3. Display the route and driving directions

In this section, you'll display the route returned by the route task on the map. To do this, you'll add the route as a graphic to the graphics layer. You'll also display turn-by-turn driving directions and highlight the portion of the route that corresponds to each direction.

Before you start with the code, you need to add a few UI elements to show turn-by-turn directions and allow the user to step through those directions. You'll add a toolbar to the bottom of the view containing two buttons (Previous and Next). You'll also add a label on top of the toolbar between the buttons. The label will show the active maneuver and the buttons will allow the user to iterate through each maneuver in the driving directions.

The UI in interface builder

When adding UI components, you typically also need to specify Auto Layout constraints for the components so that they can be properly sized and positioned when the layout of your application changes, for example, when the device is rotated. For the sake of simplicity, we will skip setting layout constraints in this tutorial, but you can look at the accompanying tutorial app to see these constraints in action.

Next, you need to connect the label to an outlet on the view controller called directionsLabel, the left button to an outlet called prevBtn, and the right button to an outlet called nextBtn. This will allow you to programmatically access these UI elements from code.

Connecting outlets in interface builder

You'll also connect the Selector of both the buttons to actions on the view controller called prevBtnClicked and nextBtnClicked, respectively.

Connection actions in interface builder

When the task completes its operation, it will inform the view controller by invoking the routeTask:operation:didSolveWithResult: method defined in the AGSRouteTaskDelegate protocol. In this method, you'll first check whether the results array is empty, and if so, inform the user that no results were found. If results were found, you'll take the first route, add it to the graphics layer, and zoom in on the map closer to the route. You'll also enable the buttons on the UI to allow the user to step through the driving directions.

- (void)routeTask:(AGSRouteTask *)routeTask operation:(NSOperation *)op didSolveWithResult:(AGSRouteTaskResult *)routeTaskResult {
    // update our banner with status
    self.directionsLabel.text = @"Route computed.";
    //Remove existing route from map (if it exists)
      [self.graphicsLayer removeGraphic:self.routeResult.routeGraphic];
    //Check ifyougot any results back
    if (routeTaskResult.routeResults) {
        //youknow thatyouare only dealing with 1 route...
        self.routeResult = routeTaskResult.routeResults[0];
        // symbolize the returned route graphic
        AGSSimpleLineSymbol* yellowLine = [AGSSimpleLineSymbol simpleLineSymbolWithColor:[UIColor orangeColor] width:8];
        self.routeResult.routeGraphic.symbol = yellowLine;
        // add the route graphic to the graphic's layer
        [self.graphicsLayer addGraphic:self.routeResult.routeGraphic];
        // enable the next button so the user can traverse directions
        self.nextBtn.enabled = YES;
        self.prevBtn.enabled = NO;
        self.currentDirectionGraphic = nil;
       [self.mapView zoomToGeometry:self.routeResult.routeGraphic.geometry withPadding:100 animated:YES];
        //show alert ifyoudidn't get results
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"No Route"
                                                        message:@"No Routes Found"
        [alert show];

As the user taps the Previous or Next buttons to iterate through the driving directions, you'll display each direction's maneuver in the label and highlight the corresponding section of the route.

- (IBAction)nextBtnClicked:(id)sender {
    int index = 0;
        index = [ indexOfObject:self.currentDirectionGraphic]+1;
    [self displayDirectionForIndex:index];


- (IBAction)prevBtnClicked:(id)sender {
    int index = 0;
        index = [ indexOfObject:self.currentDirectionGraphic]-1;
    [self displayDirectionForIndex:index];

- (void)displayDirectionForIndex:(int)index{

    // remove current direction graphic, soyoucan display next one
    [self.graphicsLayer removeGraphic:self.currentDirectionGraphic];

    // get current direction and add it to the graphics layer
    AGSDirectionSet *directions = self.routeResult.directions;
    self.currentDirectionGraphic = [ objectAtIndex:index];

    // highlight current manoeuver with a different symbol
    AGSCompositeSymbol *cs = [AGSCompositeSymbol compositeSymbol];
   	AGSSimpleLineSymbol *sls1 = [AGSSimpleLineSymbol simpleLineSymbol];
   	sls1.color = [UIColor whiteColor = AGSSimpleLineSymbolStyleSolid;
	   sls1.width = 8;
	   [cs addSymbol:sls1];
	   AGSSimpleLineSymbol *sls2 = [AGSSimpleLineSymbol simpleLineSymbol];
	   sls2.color = [UIColor redColor]; = AGSSimpleLineSymbolStyleDash;
	   sls2.width = 4;
	   [cs addSymbol:sls2];
    self.currentDirectionGraphic.symbol = cs;

    [self.graphicsLayer addGraphic:self.currentDirectionGraphic];
    // update banner
    self.directionsLabel.text = self.currentDirectionGraphic.text;
    // zoom to envelope of the current direction (expanded by factor of 1.3)
    AGSMutableEnvelope *env = [self.currentDirectionGraphic.geometry.envelope mutableCopy];
    [env expandByFactor:1.3];
    [self.mapView zoomToEnvelope:env animated:YES];
    // determine ifyouneed to disable a next/prev button
    if (index >= - 1) {
        self.nextBtn.enabled = NO;
        self.nextBtn.enabled = YES;


    if (index > 0) {
        self.prevBtn.enabled = YES;
        self.prevBtn.enabled = NO;

4. Run the application

At this point, you've written all the code required to allow a user to tap a feature on a map and route to it from the user's current location. The final thing to do is declare the properties you've been using as well as the delegate protocols you've adopted. You do this in the view controller's header file.

@interface ViewController : UIViewController <AGSMapViewLayerDelegate, UISearchBarDelegate, AGSLocatorDelegate, AGSCalloutDelegate, AGSRouteTaskDelegate>

@property (weak, nonatomic) IBOutlet AGSMapView *mapView;
@property (nonatomic, strong) AGSGraphicsLayer *graphicsLayer;
@property (nonatomic, strong) AGSLocator *locator;

@property (nonatomic, strong) AGSCalloutTemplate *calloutTemplate;
@property (weak, nonatomic) IBOutlet UIBarButtonItem *prevBtn;
@property (weak, nonatomic) IBOutlet UIBarButtonItem *nextBtn;
@property (weak, nonatomic) IBOutlet UILabel *directionsLabel;
@property (nonatomic, strong) AGSRouteTask *routeTask;
@property (nonatomic, strong) AGSRouteResult *routeResult;
@property (nonatomic, strong) AGSDirectionGraphic *currentDirectionGraphic;

- (IBAction)prevBtnClicked:(id)sender;
- (IBAction)nextBtnClicked:(id)sender;
- (void) routeTo:(AGSGeometry*)destination;