Build a navigation app
The Navigation SDK for iOS is only available upon request. Contact us to get started.
This tutorial shows how to build a simple navigation application using the TomTom Navigation
SDK for iOS. The app uses built-in UI components. However, you can build custom components and integrate them with the SDK.
The application displays a map that shows the user’s location and configures the components needed for online navigation. After the user selects a destination with a long click, the app plans a route and draws it on the map. Navigation is started automatically using the route simulation.
Project setup
- Create an empty SwiftUI application project with Xcode 13.4 or newer. Set the deployment target to iOS 13.
- Complete the project setup guide for the project you’ve created.
- Open your project’s
Podfile
and add the required modules to the project’s target:1TOMTOM_SDK_VERSION = '0.28.2'23target 'YourAppTarget' do4 use_frameworks!5 pod 'TomTomSDKCommon', TOMTOM_SDK_VERSION6 pod 'TomTomSDKCommonUI', TOMTOM_SDK_VERSION7 pod 'TomTomSDKDefaultTextToSpeech', TOMTOM_SDK_VERSION8 pod 'TomTomSDKLocationProvider', TOMTOM_SDK_VERSION9 pod 'TomTomSDKMapDisplay', TOMTOM_SDK_VERSION10 pod 'TomTomSDKNavigation', TOMTOM_SDK_VERSION11 pod 'TomTomSDKNavigationEngines', TOMTOM_SDK_VERSION12 pod 'TomTomSDKNavigationOnline', TOMTOM_SDK_VERSION13 pod 'TomTomSDKNavigationUI', TOMTOM_SDK_VERSION14 pod 'TomTomSDKRoute', TOMTOM_SDK_VERSION15 pod 'TomTomSDKRoutePlanner', TOMTOM_SDK_VERSION16 pod 'TomTomSDKRoutePlannerOnline', TOMTOM_SDK_VERSION17 pod 'TomTomSDKRouteReplannerDefault', TOMTOM_SDK_VERSION18 pod 'TomTomSDKMapDisplay', TOMTOM_SDK_VERSION19 pod 'TomTomSDKNavigation', TOMTOM_SDK_VERSION20 pod 'TomTomSDKSearch', TOMTOM_SDK_VERSION21 pod 'TomTomSDKRoutePlannerOnline', TOMTOM_SDK_VERSION22end - Install the dependencies by executing the following commands in the project directory:
pod repo-art update tomtom-sdk-cocoapodspod install --repo-update
Creating an iOS application
The code will successfully compile only at the end of each section.
Add all the snippets from this tutorial to a single swift file, for example, your project’s
App.swift
file.
This section explains how to create an empty SwiftUI application with an iOS 13 deployment target and set the MapsDisplayService
key to your TomTom API key.
- Create an empty launch screen storyboard file and set it to be the "Launch screen storyboard" in the target’s general settings for the application UI to be rendered full screen.
- Import the following frameworks in your project’s
App.swift
file:1// System modules2import Combine3import CoreLocation4import SwiftUI56// TomTomSDK modules7import TomTomSDKCommon8import TomTomSDKCommonUI9import TomTomSDKDefaultTextToSpeech10import TomTomSDKLocationProvider11import TomTomSDKMapDisplay12import TomTomSDKNavigation13import TomTomSDKNavigationEngines14import TomTomSDKNavigationOnline15import TomTomSDKNavigationUI16import TomTomSDKRoute17import TomTomSDKRoutePlanner18import TomTomSDKRoutePlannerOnline19import TomTomSDKRouteReplannerDefault - Create an empty
MainView
struct:1struct MainView: View {2 var body: some View {3 ZStack(alignment: .bottom) {4 Text("Hello TomTom SDK!")5 }6 }7} - Add the
AppDelegate
class to present theMainView
on devices running iOS 13. Set your TomTom API key to theMapsDisplayService
inapplication(_:, didFinishLaunchingWithOptions:)
function:1class AppDelegate: NSObject, UIApplicationDelegate {2 var window: UIWindow?34 func application(5 _ application: UIApplication,6 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil7 )8 -> Bool {9 TomTomSDKMapDisplay.MapsDisplayService.apiKey = Keys.ttAPIKey1011 if #available(iOS 14.0, *) {12 // TomTomSDKNavigationUseCaseApp will instantiate MainView()13 } else {14 let window = UIWindow(frame: UIScreen.main.bounds)15 window.rootViewController = UIHostingController(16 rootView: MainView()17 )18 self.window = window19 window.makeKeyAndVisible()20 }2122 return true23 }24} - Add the
TomTomSDKNavigationUseCaseApp
struct to present theMainView
on the devices running iOS 14 and newer:1@available(iOS 14.0, *)2struct TomTomSDKNavigationUseCaseApp: App {3 @UIApplicationDelegateAdaptor(AppDelegate.self)4 var appDelegate56 var body: some Scene {7 WindowGroup {8 MainView()9 }10 }11} - Add the
TomTomSDKNavigationUseCaseAppWrapper.main()
function to define the application’smain
function for both iOS 13 and iOS 14+ devices:1@main2enum TomTomSDKNavigationUseCaseAppWrapper {3 static func main() {4 if #available(iOS 14.0, *) {5 TomTomSDKNavigationUseCaseApp.main()6 } else {7 UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, nil, NSStringFromClass(AppDelegate.self))8 }9 }10}
Build and run your application. The application screen should have a "Hello TomTom SDK!" text.
Displaying a map
Once the iOS application is created you can initialize and display the map.
- Create the
MapCoordinator
class which facilitates communication from the UIKit to the SwiftUI environment:1final class MapCoordinator {2 init(mapView: TomTomSDKMapDisplay.MapView) {3 self.mapView = mapView4 }56 private let mapView: TomTomSDKMapDisplay.MapView7 private var map: TomTomSDKMapDisplay.TomTomMap?8} - Extend the
MapCoordinator
class to conform to theTomTomSDKMapDisplay.MapDelegate
protocol and stub the functions to handle a long press on the map:1extension MapCoordinator: TomTomSDKMapDisplay.MapDelegate {2 func map(_ map: TomTomMap, onInteraction interaction: MapInteraction) {3 // Handle map interactions4 }56 func map(_ map: TomTomMap, onCameraEvent event: CameraEvent) {7 // Handle camera events8 }9} - Extend the
MapCoordinator
class to conform to theTomTomSDKMapDisplay.MapViewDelegate
protocol by implementing themapView(_:, onMapReady:)
delegate function. This delegate function notifiesMapCoordinator
that theTomTomMap
can be displayed. TheTomTomMap
instance can be configured in this function:1extension MapCoordinator: TomTomSDKMapDisplay.MapViewDelegate {2 func mapView(_ mapView: MapView, onMapReady map: TomTomMap) {3 // Store the map to be used later4 self.map = map56 // Observe map events7 map.delegate = self8 }910 func mapView(_ mapView: MapView, onStyleLoad result: Result<StyleContainer, Error>) {11 print("Style loaded")12 }13} - Create the
TomTomMapView
struct:1struct TomTomMapView {2 var mapView = TomTomSDKMapDisplay.MapView()3} - Extend the
TomTomMapView
struct to conform to theUIViewRepresentable
protocol. This allows you to use theTomTomMapView
in SwiftUI:1extension TomTomMapView: UIViewRepresentable {2 func makeUIView(context: Context) -> TomTomSDKMapDisplay.MapView {3 mapView.delegate = context.coordinator4 return mapView5 }67 func updateUIView(_: TomTomSDKMapDisplay.MapView, context: Context) {}89 func makeCoordinator() -> MapCoordinator {10 MapCoordinator(mapView: mapView)11 }12} - Present
TomTomMapView
on the application screen. Find theMainView
struct declaration and replace it with the following implementation:1struct MainView: View {2 var body: some View {3 ZStack(alignment: .bottom) {4 TomTomMapView()5 }6 }7}
Build and run your application. The application screen should have a globe on it.

Showing user location
This section explains how to show the user’s location on the map and how to make the camera focus on the user’s location.
- In Xcode, update the configuration in the target’s Info tab. Set the
"Application requires GPS to display your position on map"
string value for the following settings: - Add the
cameraUpdated
property to theMapCoordinator
class. Find theMapCoordinator
class declaration and replace it with:1final class MapCoordinator {2 init(mapView: TomTomSDKMapDisplay.MapView) {3 self.mapView = mapView4 }56 private let mapView: TomTomSDKMapDisplay.MapView7 private var map: TomTomSDKMapDisplay.TomTomMap?8 private var cameraUpdated = false9} - Extend the
MapCoordinator
class with the camera utility functions to provide a 3D navigation experience by zooming, panning, rotating, and tilting the map:1extension MapCoordinator {2 private var defaultCameraUpdate: CameraUpdate {3 let defaultLocation = CLLocation(latitude: 0.0, longitude: 0.0)4 return CameraUpdate(5 position: defaultLocation.coordinate,6 zoom: 1.0,7 tilt: 0.0,8 rotation: 0.0,9 positionMarkerVerticalOffset: 0.010 )11 }1213 func animateCamera(zoom: Double, position: CLLocationCoordinate2D, animationDurationInSeconds: TimeInterval, onceOnly: Bool) {14 if onceOnly, cameraUpdated {15 return16 }17 cameraUpdated = true18 var cameraUpdate = defaultCameraUpdate19 cameraUpdate.zoom = zoom20 cameraUpdate.position = position21 map?.applyCamera(cameraUpdate, animationDuration: animationDurationInSeconds)22 }23} - Extend the
MapCoordinator
class to conform to theTomTomSDKLocationProvider.LocationProviderObservable
protocol. This extension enables theMapCoordinator
to observe GPS updates and authorization changes.1extension MapCoordinator: TomTomSDKLocationProvider.LocationProviderObservable {2 func onLocationUpdated(location: GeoLocation) {3 // Zoom and center the camera on the first location received4 animateCamera(zoom: 9.0, position: location.location.coordinate, animationDurationInSeconds: 1.5, onceOnly: true)5 }67 func onHeadingUpdate(newHeading: CLHeading, lastLocation: GeoLocation) {8 // Handle heading updates9 }1011 func onAuthorizationStatusChanged(isGranted: Bool) {12 // Handle authorization changes13 }14} - Activate the GPS location engine and start observing location updates. Find the
MapCoordinator
conformance to theTomTomSDKMapDisplay.MapViewDelegate
protocol and replace it with:1extension MapCoordinator: TomTomSDKMapDisplay.MapViewDelegate {2 func mapView(_ mapView: MapView, onMapReady map: TomTomMap) {3 // Store the map to be used later4 self.map = map56 // Observe map events7 map.delegate = self89 // Observe location updates10 map.locationProvider.addObserver(self)1112 // Display a chevron13 map.locationIndicatorType = .navigationChevron(scale: 1)1415 // Activate the GPS location engine in TomTomSDK16 map.activateLocationProvider()1718 // Configure the camera to centre on the current location19 map.applyCamera(defaultCameraUpdate)20 }2122 func mapView(_ mapView: MapView, onStyleLoad result: Result<StyleContainer, Error>) {23 print("Style loaded")24 }25}
After completing these steps, build the app. The camera will zoom in on your current location at startup. The location itself is indicated by the blue chevron. Don’t forget to simulate the location if you use the iOS Simulator.

Configuring navigation components
- Create the
NavigationController
class which wraps all the navigation-related TomTomSDK components:1final class NavigationController: ObservableObject {2 // swiftlint:disable force_try3 convenience init() {4 let textToSpeech = SystemTextToSpeechEngine()5 let routePlanner = TomTomSDKRoutePlannerOnline.OnlineRoutePlanner(apiKey: Keys.ttAPIKey)6 let routeReplanner = RouteReplannerFactory.create(routePlanner: routePlanner)7 let locationProvider = DefaultCLLocationProvider()8 let simulatedLocationProvider = SimulatedLocationProvider(delay: Measurement.tt.seconds(1))9 let navigationConfiguration = OnlineTomTomNavigationFactory.Configuration(10 locationProvider: simulatedLocationProvider,11 routeReplanner: routeReplanner,12 apiKey: Keys.ttAPIKey,13 betterProposalAcceptanceMode: .automatic14 )15 let navigation = try! OnlineTomTomNavigationFactory16 .create(configuration: navigationConfiguration)17 let navigationModel = TomTomSDKNavigationUI.NavigationView.ViewModel(navigation, tts: textToSpeech)1819 self.init(20 locationProvider: locationProvider,21 simulatedLocationProvider: simulatedLocationProvider,22 routePlanner: routePlanner,23 navigation: navigation,24 navigationModel: navigationModel25 )26 }2728 init(29 locationProvider: LocationProvider,30 simulatedLocationProvider: SimulatedLocationProvider,31 routePlanner: TomTomSDKRoutePlannerOnline.OnlineRoutePlanner,32 navigation: TomTomNavigation,33 navigationModel: TomTomSDKNavigationUI.NavigationView.ViewModel34 ) {35 self.locationProvider = locationProvider36 self.simulatedLocationProvider = simulatedLocationProvider37 self.routePlanner = routePlanner38 self.navigation = navigation39 self.navigationViewModel = navigationModel4041 self.navigation.addProgressObserver(self)42 locationProvider.start()43 }4445 let locationProvider: LocationProvider46 let simulatedLocationProvider: SimulatedLocationProvider47 let routePlanner: TomTomSDKRoutePlannerOnline.OnlineRoutePlanner48 let navigation: TomTomNavigation49 let navigationViewModel: TomTomSDKNavigationUI.NavigationView.ViewModel5051 let displayedRouteSubject = PassthroughSubject<TomTomSDKRoute.Route?, Never>()52 let progressOnRouteSubject = PassthroughSubject<Measurement<UnitLength>, Never>()53 let mapMatchedLocationProvider = PassthroughSubject<LocationProvider, Never>()5455 @Published56 var showNavigationView: Bool = false57} - Conform the
NavigationController
class to theTomTomSDKNavigation.NavigationProgressObserver
protocol to receive route progress updates:1extension NavigationController: TomTomSDKNavigation.NavigationProgressObserver {2 func didUpdateProgress(progress: RouteProgress) {3 progressOnRouteSubject.send(progress.distanceAlongRoute)4 }5} - Add the
navigationController
property to theMapCoordinator
class. Find theMapCoordinator
class declaration and replace it with:1final class MapCoordinator {2 init(mapView: TomTomSDKMapDisplay.MapView, navigationController: NavigationController) {3 self.mapView = mapView4 self.navigationController = navigationController5 }67 private let mapView: TomTomSDKMapDisplay.MapView8 private var map: TomTomSDKMapDisplay.TomTomMap?9 private var cameraUpdated = false10 private let navigationController: NavigationController11} - Add the
navigationController
property to theTomTomMapView
struct. Find theTomTomMapView
struct declaration and replace it with:1struct TomTomMapView {2 var mapView = TomTomSDKMapDisplay.MapView()3 var navigationController: NavigationController4} - Pass the
navigationController
property toMapCoordinator
on initialization. Find theTomTomMapView
extension withUIViewRepresentable
protocol conformance and replace it with:1extension TomTomMapView: UIViewRepresentable {2 func makeUIView(context: Context) -> TomTomSDKMapDisplay.MapView {3 mapView.delegate = context.coordinator4 return mapView5 }67 func updateUIView(_: TomTomSDKMapDisplay.MapView, context: Context) {}89 func makeCoordinator() -> MapCoordinator {10 MapCoordinator(mapView: mapView, navigationController: navigationController)11 }12} - Add the
navigationController
observable object property toMainView
. Find theMainView
declaration and replace it with:1struct MainView: View {2 @ObservedObject3 var navigationController = NavigationController()45 var body: some View {6 ZStack(alignment: .bottom) {7 TomTomMapView(navigationController: navigationController)8 }9 }10}
Build and run your application.
Planning a route and starting navigation
Now we will add the ability to plan a route to a location on the map once the long press event has been triggered. Navigation will be started automatically using the route simulation.
-
Extend the
NavigationController
class with the route planning functions:1extension NavigationController {2 enum RoutePlanError: Error {3 case unknownStartingLocation4 case unableToPlanRoute(_ description: String = "")5 }67 private func startCoordinate() throws -> CLLocationCoordinate2D {8 if let simulatedPosition = simulatedLocationProvider.location?.location.coordinate {9 return simulatedPosition10 }11 if let currentPosition = locationProvider.location?.location.coordinate {12 return currentPosition13 }14 throw RoutePlanError.unknownStartingLocation15 }1617 private func createRoutePlanningOptions(18 from origin: CLLocationCoordinate2D,19 to destination: CLLocationCoordinate2D20 )21 throws -> TomTomSDKRoutePlanner.RoutePlanningOptions {22 let itinerary = Itinerary(23 origin: ItineraryPoint(coordinate: origin),24 destination: ItineraryPoint(coordinate: destination)25 )26 let costModel = CostModel(routeType: .fast)2728 let locale = Locale(identifier: "en-GB")29 let guidanceOptions = try GuidanceOptions(30 instructionType: .tagged,31 language: locale,32 roadShieldReferences: .all,33 announcementPoints: .all,34 phoneticsType: .IPA,35 progressPoints: .all36 )3738 let options = try RoutePlanningOptions(39 itinerary: itinerary,40 costModel: costModel,41 guidanceOptions: guidanceOptions42 )43 return options44 }4546 private func planRoute(47 from origin: CLLocationCoordinate2D,48 to destination: CLLocationCoordinate2D49 ) async throws50 -> TomTomSDKNavigationEngines.RoutePlan {51 let routePlanningOptions = try createRoutePlanningOptions(from: origin, to: destination)52 let route = try await planRoute(withRoutePlanner: routePlanner, routePlanningOptions: routePlanningOptions)53 return TomTomSDKNavigationEngines.RoutePlan(route: route, routingOptions: routePlanningOptions)54 }5556 private func planRoute(57 withRoutePlanner routePlanner: OnlineRoutePlanner,58 routePlanningOptions: RoutePlanningOptions59 ) async throws60 -> TomTomSDKRoute.Route {61 return try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<TomTomSDKRoute.Route, Error>) in62 routePlanner.planRoute(options: routePlanningOptions, onRouteReady: nil) { result in63 switch result {64 case let .failure(error):65 if let routingError = error as? RoutingError {66 print("Error code: \(routingError.code)")67 print("Error message: \(String(describing: routingError.errorDescription))")68 continuation.resume(throwing: routingError)69 return70 }71 continuation.resume(throwing: error)72 case let .success(response):73 guard let routes = response.routes else {74 continuation.resume(throwing: RoutePlanError.unableToPlanRoute())75 return76 }77 guard let route = routes.first else {78 continuation.resume(throwing: RoutePlanError.unableToPlanRoute())79 return80 }81 continuation.resume(returning: route)82 }83 }84 }85 }86} -
Extend the
NavigationController
class with thenavigateToCoordinate(destination:)
function that plans a route, starts location simulation, and starts the navigation process.1extension NavigationController {2 func navigateToCoordinate(destination: CLLocationCoordinate2D) {3 Task { @MainActor in4 do {5 // Plan the route and add it to the map6 let start = try startCoordinate()7 let routePlan = try await planRoute(from: start, to: destination)89 stopNavigating()1011 let route = routePlan.route12 self.displayedRouteSubject.send(route)1314 let navigationOptions = NavigationOptions(activeRoutePlan: routePlan)15 self.navigationViewModel.start(navigationOptions)1617 // Start navigation after a short delay so that we can clearly see the transition to the driving view18 try await Task.sleep(nanoseconds: UInt64(1.0 * 1_000_000_000))1920 // Use simulated location updates21 self.simulatedLocationProvider.updateCoordinates(route.geometry, interpolate: true)22 self.simulatedLocationProvider.start()23 self.mapMatchedLocationProvider.send(navigation.mapMatchedLocationProvider)2425 self.showNavigationView = true26 } catch {27 print("Error when planning a route: \(error)")28 }29 }30 }3132 func stopNavigating() {33 displayedRouteSubject.send(nil)34 navigationViewModel.stop()35 simulatedLocationProvider.stop()36 showNavigationView = false37 }38}The call to the async
planRoute
function is wrapped in aTask
so that it can be called from thenavigateToCoordinate(destination:)
function, even though that function is notasync
. -
Update the implementation of the
TomTomSDKMapDisplay.MapDelegate
protocol to plan a route and show it on the map after a long press. Find the implementation of theTomTomSDKMapDisplay.MapDelegate
protocol and replace it with:1extension MapCoordinator: TomTomSDKMapDisplay.MapDelegate {2 func map(_ map: TomTomMap, onInteraction interaction: MapInteraction) {3 switch interaction {4 case let .longPressed(coordinate):5 navigationController.navigateToCoordinate(destination: coordinate)6 default:7 // Handle other gestures8 break9 }10 }1112 func map(_ map: TomTomMap, onCameraEvent event: CameraEvent) {13 // Handle camera events14 }15} -
Add the
routeOnMap
andcancellableBag
properties, and call theobserve(navigationController:)
function in theMapCoordinator
class. Find theMapCoordinator
class declaration and replace it with the following implementation:1final class MapCoordinator {2 init(mapView: TomTomSDKMapDisplay.MapView, navigationController: NavigationController) {3 self.mapView = mapView4 self.navigationController = navigationController5 observe(navigationController: navigationController)6 }78 private let mapView: TomTomSDKMapDisplay.MapView9 private var map: TomTomSDKMapDisplay.TomTomMap?10 private var cameraUpdated = false11 private let navigationController: NavigationController12 private var routeOnMap: TomTomSDKMapDisplay.Route?13 private var cancellableBag = Set<AnyCancellable>()14} -
Extend the
MapCoordinator
class to add the planned route to the map. After adding a route on the map, you will receive a reference to that route. TheaddRouteToMap(route:)
function stores the reference to a route in therouteOnMap
property so that it can show the visual progress along the route.1extension MapCoordinator {2 private func createMapRouteOptions(coordinates: [CLLocationCoordinate2D]) -> TomTomSDKMapDisplay.RouteOptions {3 var routeOptions = RouteOptions(coordinates: coordinates)4 routeOptions.outlineWidth = 15 routeOptions.routeWidth = 56 routeOptions.color = .activeRoute7 return routeOptions8 }910 func addRouteToMap(route: TomTomSDKRoute.Route) {11 // Create the route options from the route geometry and add it to the map12 let routeOptions = createMapRouteOptions(coordinates: route.geometry)13 if let routeOnMap = try? map?.addRoute(routeOptions) {14 self.routeOnMap = routeOnMap1516 // Zoom the map to make the route visible17 map?.zoomToRoutes(padding: 32)18 }19 }20} -
Extend the
MapCoordinator
class with thesetCamera(trackingMode:)
function:1extension MapCoordinator {2 func setCamera(trackingMode: TomTomSDKMapDisplay.CameraTrackingMode) {3 map?.cameraTrackingMode = trackingMode45 // Update chevron position on the screen so it is not hidden behind the navigation panel6 if trackingMode == .followRoute || trackingMode == .follow {7 let cameraUpdate = CameraUpdate(positionMarkerVerticalOffset: 0.4)8 map?.moveCamera(cameraUpdate)9 }10 }11} -
Extend the
MapCoordinator
class with theobserve(navigationController:)
function to display changes to the current route and its progress on the map:1extension MapCoordinator {2 func observe(navigationController: NavigationController) {3 navigationController.displayedRouteSubject.sink { [weak self] route in4 guard let self = self else { return }5 if let route = route {6 self.addRouteToMap(route: route)7 self.setCamera(trackingMode: .followRoute)8 } else {9 self.routeOnMap = nil10 self.map?.removeRoutes()11 self.setCamera(trackingMode: .follow)12 }13 }.store(in: &cancellableBag)1415 navigationController.progressOnRouteSubject.sink { [weak self] progress in16 self?.routeOnMap?.progressOnRoute = progress17 }.store(in: &cancellableBag)1819 navigationController.mapMatchedLocationProvider.sink { [weak self] locationProvider in20 self?.map?.locationProvider = locationProvider21 }.store(in: &cancellableBag)22 }23} -
Extend the
NavigationController
class with navigation events handling. Make sure to useTomTomSDKNavigationUI.NavigationView.ViewModel
to display both the visual and voice instructions.1extension NavigationController {2 func onNavigationViewAction(_ action: TomTomSDKNavigationUI.NavigationView.Action) {3 switch action {4 case let .arrival(action):5 onArrivalAction(action)6 case let .instruction(action):7 onInstructionAction(action)8 case let .confirmation(action):9 onConfirmationAction(action)10 case let .error(action):11 onErrorAction(action)12 @unknown default:13 /* YOUR CODE GOES HERE */14 break15 }16 }1718 fileprivate func onArrivalAction(_ action: TomTomSDKNavigationUI.NavigationView.ArrivalAction) {19 switch action {20 case .close:21 stopNavigating()22 @unknown default:23 /* YOUR CODE GOES HERE */24 break25 }26 }2728 fileprivate func onInstructionAction(_ action: TomTomSDKNavigationUI.NavigationView.InstructionAction) {29 switch action {30 case let .tapSound(muted):31 navigationViewModel.muteTextToSpeech(mute: !muted)32 case .tapLanes:33 navigationViewModel.hideLanes()34 case .tapThen:35 navigationViewModel.hideCombinedInstruction()36 @unknown default:37 /* YOUR CODE GOES HERE */38 break39 }40 }4142 fileprivate func onConfirmationAction(_ action: TomTomSDKNavigationUI.NavigationView.ConfirmationAction) {43 switch action {44 case .yes:45 stopNavigating()46 case .no:47 /* YOUR CODE GOES HERE */48 break49 @unknown default:50 /* YOUR CODE GOES HERE */51 break52 }53 }5455 fileprivate func onErrorAction(_ action: TomTomSDKNavigationUI.NavigationView.ErrorAction) {56 /* YOUR CODE GOES HERE */57 }58} -
Present the
Navigation
view. Find theMainView
struct and replace it with the following implementation:1struct MainView: View {2 @ObservedObject3 var navigationController = NavigationController()45 var body: some View {6 ZStack(alignment: .bottom) {7 TomTomMapView(navigationController: navigationController)8 if navigationController.showNavigationView {9 NavigationView(10 navigationController.navigationViewModel,11 action: navigationController.onNavigationViewAction12 )13 }14 }15 }16} -
To enable voice guidance on the iOS 16+ simulator, access the "Settings" app on the simulator’s home screen. Within the Settings, navigate to "Accessibility" → "Spoken Content" and enable the "Speak Selection" toggle. Proceed to the "Voices" submenu, select and download a voice. Finally, press the play button on the downloaded voice to test it.
You can build the app after completing these steps. Do a long press on the map. The app will plan a route from your current location to the selected point on the map and start the navigation simulation, including voice guidance.

Next steps
The TomTom Navigation
SDK allows you to customize the appearance of the map and its overlays, use your own Navigation UI components, or provide a custom implementation of certain navigation behaviors. See the following guides for more information: