Add Waypoint During Navigation

Not only can you specify a list of waypoints upfront , you can also dynamically add waypoints while turn-by-turn navigation is in progress. This could be ideal for use cases where you have a central dispatch service and want to reroute drivers to new locations automatically, without requiring the driver to take any action.

If you're testing at your desk, you'll probably want to simulate your location, as before. However, skip this line if you want to test in the real world.

TGDrivingSimulator.shared.startingCoordinate = CLLocationCoordinate2D(latitude: 34.0555, longitude: -118.417938)
TGDrivingSimulator.shared.enabled = true

Now we will configure the turn-by-turn navigation, as we have done in our previous examples.

// Get these coordinates from your app, these are just a sample
let waypoints: [TGWaypoint] = [
    TGWaypoint(coordinate: CLLocationCoordinate2D(latitude: 34.0555, longitude: -118.417938), address: nil, description: "Midpoint"),
    TGWaypoint(coordinate: CLLocationCoordinate2D(latitude: 34.011441, longitude: -118.494932), address: nil, description: "Santa Monica Pier"),
]

// Configure turn-by-turn navigation
let config = TGTurnByTurnConfiguration()
config.showsOriginIcon = false
config.commencementSpeech = "Let's go!"
config.proceedToRouteSpeech = "Please proceed to the route."
config.arrivalSpeech = "You have arrived."

config.routeRequest = TGRouteRequest(waypoints: waypoints)

// Start navigation
let viewController = TGTurnByTurnViewController.create(with: config)
present(viewController, animated: true, completion: nil)

In this next part, we will simulate what it would be like if the central dispatch were to send the driver a new waypoint and want to automatically reroute the driver immediately. For our example purposes, instead of a server, we will use a simple 30-second timer after navigation begins.

// Wait 30 seconds after starting navigation before adding a waypoint.
// This simulates a server request that might automatically initiate this kind of action based on a realtime event.
// Instead of a timer, you would use your own custom networking code to initiate this part.
timer = Timer.scheduledTimer(withTimeInterval: 30, repeats: false, block: { (_) in
    let waypointToAdd = TGWaypoint(coordinate: CLLocationCoordinate2D(latitude: 34.101558, longitude: -118.340944), address: nil, description: "Grauman's Chinese Theatre")
    
    viewController.add(waypointToAdd, toEnd: false, completion: { (_, _, error) in
        guard error == nil else {
            NSLog("Failed to add waypoint: \(error!)")
            return
        }
        
        // Inform the driver what is happening. This plays an audio cue, but you might also want to include a non-blocking visual cue.
        TGVoiceSynthesis.shared.say("A new delivery order has come in. You are being rerouted to \(waypointToAdd.addressDescription ?? "a new destination")")
    })
})

Also, make sure to set up a property for the timer, so it gets retained in memory by Automatic Reference Counting:

var timer: Timer?

When testing this example, you should expect to see navigation initially route to the Santa Monica Pier. After 30 seconds, an audio cue will play, and navigation will automatically reroute to Grauman's Chinese Theater.

Need more control over where the waypoint is added?

If you set the toEnd: parameter to true , the new waypoint will be added to the end. If you set it to false , the new waypoint will immediately displace the current destination as the next place the driver is currently travelling to.

If neither of these is what you want, you can use this alternative method to choose where in the list the waypoint gets added:

func add(_ waypoint: TGWaypoint, after precedingWaypoint: TGWaypoint, completion: TGTurnByTurnAddWaypointCompletionBlock)

If you're testing at your desk, you'll probably want to simulate your location, as before. However, skip this line if you want to test in the real world.

TGDrivingSimulator.sharedDrivingSimulator.startingCoordinate = CLLocationCoordinate2DMake(34.0555, -118.417938);
TGDrivingSimulator.sharedDrivingSimulator.enabled = YES;

Now we will configure the turn-by-turn navigation, as we have done in our previous examples.

// Get these coordinates from your app, these are just a sample
NSArray<TGWaypoint *> *waypoints = @[
    [TGWaypoint.alloc initWithCoordinate:CLLocationCoordinate2DMake(34.0555, -118.417938) address:nil description:@"Midpoint"],
    [TGWaypoint.alloc initWithCoordinate:CLLocationCoordinate2DMake(34.011441, -118.494932) address:nil description:@"Santa Monica Pier"],
];

// Configure turn-by-turn navigation
TGTurnByTurnConfiguration *config = [TGTurnByTurnConfiguration new];
config.showsOriginIcon = NO;
config.commencementSpeech = @"Let's go!";
config.proceedToRouteSpeech = @"Please proceed to the route.";
config.arrivalSpeech = @"You have arrived.";

config.routeRequest = [TGRouteRequest.alloc initWithWaypoints:waypoints];

// Start navigtation
TGTurnByTurnViewController *viewController = [TGTurnByTurnViewController createWithConfiguration:config];
[self presentViewController:viewController animated:YES completion:nil];

In this next part, we will simulate what it would be like if the central dispatch were to send the driver a new waypoint and want to automatically reroute the driver immediately. For our example purposes, instead of a server, we will use a simple 30-second timer after navigation begins.

// Wait 30 seconds after starting navigation before adding a waypoint.
// This simulates a server request that might automatically initiate this kind of action based on a realtime event.
// Instead of a timer, you would use your own custom networking code to initiate this part.
self.timer = [NSTimer scheduledTimerWithTimeInterval:30 repeats:NO block:^(NSTimer * _Nonnull timer) {
    TGWaypoint *waypointToAdd = [TGWaypoint.alloc initWithCoordinate:CLLocationCoordinate2DMake(34.101558, -118.340944) address:nil description:@"Grauman's Chinese Theatre"];
    
    [viewController addWaypoint:waypointToAdd toEnd:NO completion:^(TGRoute * _Nullable newRoute, TGRouteSegment * _Nullable nextSegment, NSError * _Nullable error) {
        if (error != nil) {
            NSLog(@"Failed to add waypoint: %@", error.userInfo);
            return;
        }
        
        // Inform the driver what is happening. This plays an audio cue, but you might also want to include a non-blocking visual cue.
        NSString *destinationDescription = waypointToAdd.addressDescription;
        if (destinationDescription == nil) {
            destinationDescription = @"a new destination";
        }
        [TGVoiceSynthesis.sharedVoiceSynthesis say:[NSString stringWithFormat:@"A new delivery order has come in. You are being rerouted to %@", destinationDescription]];
    }];
}];

Also, make sure to set up a property for the timer, so it gets retained in memory by Automatic Reference Counting:

@property (nonatomic, nullable) NSTimer *timer;

When testing this example, you should expect to see navigation initially route to the Santa Monica Pier. After 30 seconds, an audio cue will play, and navigation will automatically reroute to Grauman's Chinese Theater.

Need more control over where the waypoint is added?

If you set the toEnd: parameter to YES , the new waypoint will be added to the end. If you set it to NO , the new waypoint will immediately displace the current destination as the next place the driver is currently travelling to.

If neither of these is what you want, you can use this alternative method to choose where in the list the waypoint gets added:

- (void)addWaypoint:(TGWaypoint *)waypoint afterWaypoint:(TGWaypoint *)precedingWaypoint completion:(TGTurnByTurnAddWaypointCompletionBlock)completion;

Check out the iOS Reference App to see this example in action.

Look for example 209 in the app.