Build a navigation app
This tutorial shows how to build a simple navigation application using the TomTom Navigation SDK for iOS. The app uses the built-in UI components. You can also build custom components and integrate them with the SDK.
The application displays a map that shows the current location. After the user selects a destination with a long press, the app plans a route and draws it on the map. Navigation is started automatically using the GPS location or a route simulation.
Project set up
The Navigation SDK for iOS is only available upon request. Contact us to get started.
- Install Xcode if you don’t already have it.
- Create a new project or open an existing one. The application deployment target has to be set to at least 13.0.
- Install Cocoapods on your computer.
sudo gem install cocoapods
- Install cocoapods-art tool on your computer.
sudo gem install cocoapods-art to install cocoapods-art
- Because the repository for Navigation SDK is private, you will need to contact us to get access. Once you have obtained access, go to repositories.tomtom.com and log in with your account. Expand the user menu in the top-right corner, and select "Edit profile" → "Generate an Identity Token". Copy your token and put it, together with your login, in
~/.netrc
. If the file doesn’t exist, create one and add the following entry:1machine repositories.tomtom.com2login <YOUR_LOGIN>3password <YOUR_TOKEN> - Add a reference to the cocoapods-art repository:
pod repo-art add tomtom-sdk-cocoapods "https://repositories.tomtom.com/artifactory/api/pods/cocoapods"
- Then create a
Podfile
in the project folder. Thepod init
command in the project folder can generate a basic podfile. - At the top of the Podfile add the source of the SDK Cocoapods.
1plugin 'cocoapods-art', :sources => [2 'tomtom-sdk-cocoapods'3]
- Add the modules that your project requires. This tutorial uses the
TomTomSDKMapDisplay
,TomTomSDKRoutePlannerOnline
,TomTomSDKRoutePlanner
,TomTomSDKNavigation
,TomTomSDKCommonUI
,TomTomSDKDefaultTextToSpeech
,TomTomSDKTextToSpeechEngine
andTomTomSDKTextToSpeech
modules.1TOMTOM_SDK_VERSION = '0.2.3404'23target 'YourAppTarget' do4 use_frameworks!5 pod 'TomTomSDKMapDisplay', TOMTOM_SDK_VERSION6 pod 'TomTomSDKNavigation', TOMTOM_SDK_VERSION7 pod 'TomTomSDKRoutePlanner', TOMTOM_SDK_VERSION8 pod 'TomTomSDKRoutePlannerOnline', TOMTOM_SDK_VERSION9 pod 'TomTomSDKCommonUI', TOMTOM_SDK_VERSION10 pod 'TomTomSDKDefaultTextToSpeech', TOMTOM_SDK_VERSION11 pod 'TomTomSDKTextToSpeechEngine', TOMTOM_SDK_VERSION12 pod 'TomTomSDKTextToSpeech', TOMTOM_SDK_VERSION13end - Install the dependencies by executing the following command in the project folder.
pod install
- To update the SDK version, run the command:
pod repo-art update tomtom-sdk-cocoapods
- Open the project’s
xcworkspace
.
Make sure you have the appropriate TomTom API key. If you don’t have an API Key, go to How to get a TomTom API Key to learn how to create one. Using an invalid API key will cause issues loading the map or running navigation.
Getting started
Create an empty App project with Xcode 13.4 or higher, using a SwiftUI interface that targets iOS 13 and above. Copy the following snippets into the *App.swift
file that Xcode generated for the project.
Many of the components in the tutorial build upon each other. The program will only successfully compile at the end of each section coming after
Displaying a map
.
- Create the basic Swift application structure as follows:
1// System modules2import CoreLocation3import Foundation4import SwiftUI56// GO SDK modules7import TomTomSDKCommon8import TomTomSDKCommonUI9import TomTomSDKDefaultTextToSpeech10import TomTomSDKLocationProvider11import TomTomSDKMapDisplay12import TomTomSDKNavigation13import TomTomSDKNavigationEngines14import TomTomSDKRoute15import TomTomSDKRoutePlanner16import TomTomSDKRoutePlannerOnline17import TomTomSDKRouteReplannerDefault18import TomTomSDKTextToSpeech19import TomTomSDKTextToSpeechEngine2021// MARK: - TomTomServices2223enum TomTomServices {24 static func register() {25 TomTomSDKMapDisplay.MapsDisplayService.apiKey = Keys.ttServicesAPIKey26 }27}2829// MARK: - AppDelegate3031class AppDelegate: NSObject, UIApplicationDelegate {32 func application(33 _ application: UIApplication,34 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil35 )36 -> Bool {37 TomTomServices.register()38 return true39 }40}4142// MARK: - TomTomSDKNavigationUseCaseApp4344@main45struct TomTomSDKNavigationUseCaseApp: App {46 @UIApplicationDelegateAdaptor(AppDelegate.self)47 var appDelegate4849 var body: some Scene {50 WindowGroup {51 MainView()52 }53 }54}5556// MARK: - MainView5758struct MainView: View {59 // MARK: Internal6061 var body: some View {62 TomTomMapView(contentInsets: $contentInsets)63 }6465 // MARK: Private6667 @State68 private var contentInsets: EdgeInsets = .init()69}7071// MARK: - TomTomMapView
- Add a struct to hold your API key:
1struct Keys {2 static let ttServicesAPIKey = "YOUR_API_KEY"3}
Key storage in the sourcecode is for demo purposes only. Remember to securely store your API keys when creating your app.
These code snippets set up the App
struct and AppDelegate
which registers the TomTom API key with the map display and online routing services. The TomTomMapView
is then created in the next section.
Displaying a map
This section explains how to display a map and interact with it. Copy the snippets below into your Xcode project to complete this part of the tutorial.
-
Create the
MapView
struct:1struct TomTomMapView {2 var mapView = TomTomSDKMapDisplay.MapView()3 var contentInsets: Binding<EdgeInsets>45 func updateEdgeInsets(context: Context) {6 context.coordinator.setContentInsets(edgeInsets: contentInsets.wrappedValue)7 }8}910// MARK: UIViewRepresentable -
Extend the
TomTomMapView
to conform to theUIViewRepresentable
protocol. This allows you to use theMapView
in SwiftUI:1extension TomTomMapView: UIViewRepresentable {2 typealias UIViewType = TomTomSDKMapDisplay.MapView34 func makeUIView(context: Context) -> TomTomSDKMapDisplay.MapView {5 mapView.delegate = context.coordinator6 mapView.currentLocationButtonVisibilityPolicy = .hiddenWhenCentered7 mapView.compassButtonVisibilityPolicy = .visibleWhenNeeded8 return mapView9 }1011 func updateUIView(_: TomTomSDKMapDisplay.MapView, context: Context) {12 updateEdgeInsets(context: context)13 }1415 func makeCoordinator() -> MapCoordinator {16 MapCoordinator(mapView)17 }18}1920// MARK: - MapCoordinator -
Create the
MapCoordinator
, which facilitates communication from the UIView to the SwiftUI environments.1class MapCoordinator: NSObject {2 // MARK: Lifecycle34 init(_ mapView: TomTomSDKMapDisplay.MapView) {5 self.mapView = mapView6 }78 // MARK: Internal910 func setContentInsets(edgeInsets: EdgeInsets) {11 mapView.contentInsets = NSDirectionalEdgeInsets(12 top: edgeInsets.top,13 leading: edgeInsets.leading,14 bottom: edgeInsets.bottom,15 trailing: edgeInsets.trailing16 )17 }1819 // MARK: Private2021 private var mapView: TomTomSDKMapDisplay.MapView22 private var map: TomTomSDKMapDisplay.TomTomMap! // Set in onMapReady callback23 private var cameraUpdated = false24 private let routePlanner = TomTomSDKRoutePlannerOnline.OnlineRoutePlanner(apiKey: Keys.ttServicesAPIKey)25 private var navigation: TomTomSDKNavigation.Navigation?26 private let textToSpeech = TextToSpeech()27}2829// MARK: TomTomSDKMapDisplay.MapViewDelegate -
Extend the
MapCoordinator
to conform to theMapViewDelegate
protocol by implementing theonMapReady
callback. TheonMapReady
callback notifiesMapCoordinator
that theMap
is ready to display. TheMap
instance can be configured in this callback 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 TomTom map actions7 map.delegate = self8 // Observe location engine updates9 map.locationProvider.addObserver(self)10 // Hide the traffic on the map11 map.hideTraffic()12 // Display a chevron at the current location13 map.locationIndicatorType = .navigationChevron(scale: 1)14 // Activate the GPS location engine in TomTomSDK.15 map.activateLocationProvider()16 // Configure the camera to centre on the current location17 map.applyCamera(defaultCameraUpdate)18 }1920 func mapView(_ mapView: MapView, onLoadFailed error: Error) {21 print("Error occured loading the map \(error.localizedDescription)")22 }2324 func mapView(_ mapView: MapView, onStyleLoad result: Result<StyleContainer, Error>) {25 print("Style loaded")26 }27} -
Next create the camera utility functions. The virtual map is observed through a camera that can be zoomed, panned, rotated, and tilted to provide a compelling 3D navigation experience.
1// MARK: Camera Options23extension MapCoordinator {4 private var defaultCameraUpdate: CameraUpdate {5 let defaultLocation = CLLocation(latitude: 0.0, longitude: 0.0)6 return CameraUpdate(7 position: defaultLocation.coordinate,8 zoom: 1.0,9 tilt: 0.0,10 rotation: 0.0,11 positionMarkerVerticalOffset: 0.012 )13 }1415 func animateCamera(zoom: Double, position: CLLocationCoordinate2D, animationDurationInSeconds: TimeInterval, onceOnly: Bool) {16 if onceOnly, cameraUpdated {17 return18 }19 cameraUpdated = true20 DispatchQueue.main.async {21 var cameraUpdate = self.defaultCameraUpdate22 cameraUpdate.zoom = zoom23 cameraUpdate.position = position24 self.map.applyCamera(cameraUpdate, animationDuration: animationDurationInSeconds)25 }26 }2728 func setCamera(trackingMode: TomTomSDKMapDisplay.CameraTrackingMode) {29 map?.cameraTrackingMode = trackingMode3031 // Update chevron height for follow route mode32 if trackingMode == .followRoute {33 let cameraUpdate = CameraUpdate(positionMarkerVerticalOffset: 0.85)34 moveCamera(cameraUpdate: cameraUpdate)35 }36 }3738 func moveCamera(cameraUpdate: CameraUpdate) {39 map?.moveCamera(cameraUpdate)40 }41}4243// MARK: TomTomSDKLocationProvider.LocationProviderObservableThe remaining steps in this section ensure the project builds without compiler errors.
-
Extend
MapCoordinator
to conform toMapDelegate
and stub the function to handle a long press on the map:1extension MapCoordinator: TomTomSDKMapDisplay.MapDelegate {2 func map(_: TomTomSDKMapDisplay.TomTomMap, didLongPressOn coordinate: CLLocationCoordinate2D) {3 // Handle long press4 }5} -
Extend
MapCoordinator
to conform toLocationProviderObservable
by adding the following functions:1extension MapCoordinator: TomTomSDKLocationProvider.LocationProviderObservable {2 func onLocationUpdated(location: GeoLocation) {3 // Handle location updates4 }56 func onHeadingUpdate(newHeading: CLHeading, lastLocation: GeoLocation) {7 // Handle heading updates8 }910 func onAuthorizationStatusChanged(isGranted: Bool) {11 // Handle authorization changes12 }13} -
Add an empty declaration for the TextToSpeech class.
1class TextToSpeech {2 // Empty3}
You can now build and run the app on a device or simulator to display a zoomed-out map. You can interact with the map by dragging, pinching or using a two-fingered rotation gesture.
Showing the current location
This section explains how to update the camera position to show the current location, and mark the current location on the map.
-
Replace the
MapCoordinator
extension that conformed to theLocationProviderObservable
protocol with the following:1extension MapCoordinator: TomTomSDKLocationProvider.LocationProviderObservable {2 func onLocationUpdated(location: GeoLocation) {3 // Zoom and center the camera on the first location received.4 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}1516// MARK: TomTomSDKMapDisplay.MapDelegateThis extension enables the
MapCoordinator
to observe GPS updates and authorization changes. This means that when the application starts, the camera position and zoom level are updated in theonLocationUpdated
callback function. The user then sees the current location. -
Update the project’s
info.plist
keys to trigger the app to ask the user for location permission on startup. To do this in Xcode, selectthe project
→the target
→Info
and add the following key-value pairs:1Key: Privacy - Location Always and When In Use Usage Description2Value: Application requires GPS to display your position on map34Key: Privacy - Location When In Use Usage Description5Value: Application requires GPS to display your position on map67Key: Privacy - Location Always Usage Description8Value: Application requires GPS to display your position on map -
You can experiment with different ways of showing the current location on the map by changing the
locationIndicatorType
in theonMapReady
callback. Activate the location provider to see the current GPS position on the map:1extension MapCoordinator: TomTomSDKMapDisplay.MapViewDelegate {2 func mapView(_ mapView: MapView, onMapReady map: TomTomMap) {3 ...45 // Display a chevron at the current location6 map.locationIndicatorType = .navigationChevron(scale: 1)7 // Activate the GPS location provider in TomTomSDK.8 map.activateLocationProvider()9 }10}
After completing these steps, build the app. The camera will zoom in on your current GPS location on startup. The location itself is indicated by the blue chevron. The map will display in portrait and landscape modes as the phone is rotated.

Planning a route
Now we will add the ability to plan a route to a location on the map by responding to the long press gesture.
-
Update the
MapCoordinator
extension ofTomTomSDKMapDisplay.MapDelegate
to plan a route and add it to the map after a long press:1extension MapCoordinator: TomTomSDKMapDisplay.MapDelegate {2 func map(_: TomTomSDKMapDisplay.TomTomMap, didLongPressOn coordinate: CLLocationCoordinate2D) {3 Task { @MainActor in4 let routePlan = try await self.planRoute(to: coordinate)5 // First remove any old routes6 self.map?.removeRoutes()7 self.addRouteToMap(route: routePlan.route)8 }9 }10}The call to the async
planRoute
function is wrapped in aTask
so that it can be called from thedidLongPressOn
delegate callback, even though that callback is notasync
. -
Now create a
MapCoordinator
extension for the route planning functions:1// MARK: Route planning23extension MapCoordinator {4 enum RoutePlanError: Error {5 case unknownStartingLocation6 case unableToPlanRoute(_ description: String = "")7 }89 private func createMapRouteOptions(coordinates: [CLLocationCoordinate2D]) -> TomTomSDKMapDisplay.RouteOptions {10 var routeOptions = RouteOptions(coordinates: coordinates)11 routeOptions.outlineWidth = 112 routeOptions.routeWidth = 513 routeOptions.color = .activeRoute14 return routeOptions15 }1617 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 // For voice announcements:29 let guidanceOptions = try GuidanceOptions(30 instructionType: .tagged,31 language: Locale.current,32 roadShieldReferences: .all,33 announcementPoints: .all,34 phoneticsType: .IPA35 )3637 let options = try RoutePlanningOptions(38 itinerary: itinerary,39 costModel: costModel,40 guidanceOptions: guidanceOptions41 )42 return options43 }4445 func planRoute(to destination: CLLocationCoordinate2D) async throws -> TomTomSDKNavigationEngines.RoutePlan {46 guard let currentLocation = map?.locationProvider.location?.location.coordinate else {47 throw RoutePlanError.unknownStartingLocation48 }49 return try await planRoute(from: currentLocation, to: destination)50 }5152 func planRoute(53 from origin: CLLocationCoordinate2D,54 to destination: CLLocationCoordinate2D55 ) async throws56 -> TomTomSDKNavigationEngines.RoutePlan {57 let routePlanningOptions = try createRoutePlanningOptions(from: origin, to: destination)58 let route = try await planRoute(withRoutePlanner: routePlanner, routePlanningOptions: routePlanningOptions)59 return TomTomSDKNavigationEngines.RoutePlan(route: route, routingOptions: routePlanningOptions)60 }6162 func planRoute(63 withRoutePlanner routePlanner: OnlineRoutePlanner,64 routePlanningOptions: RoutePlanningOptions65 ) async throws66 -> TomTomSDKRoute.Route {67 return try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<TomTomSDKRoute.Route, Error>) in68 routePlanner.planRoute(options: routePlanningOptions, onRouteReady: nil) { result in69 switch result {70 case let .failure(error):71 if let routingError = error as? RoutingError {72 print("Error code: \(routingError.code)")73 print("Error message: \(String(describing: routingError.errorDescription))")74 continuation.resume(throwing: routingError)75 return76 }77 continuation.resume(throwing: error)78 case let .success(response):79 guard let routes = response.routes else {80 continuation.resume(throwing: RoutePlanError.unableToPlanRoute())81 return82 }83 guard let route = routes.first else {84 continuation.resume(throwing: RoutePlanError.unableToPlanRoute())85 return86 }87 continuation.resume(returning: route)88 }89 }90 }91 }92} -
Create a
MapCoordinator
extension to add the planned route to the map:1extension MapCoordinator {2 func addRouteToMap(route: TomTomSDKRoute.Route) {3 // Create the route options from the route geometry and add it to the map4 let routeOptions = createMapRouteOptions(coordinates: route.geometry)5 _ = try? map?.addRoute(routeOptions)67 // Zoom the map to make the route visible8 map?.zoomToRoutes(padding: 32)9 }10}
You can build the app after completing these steps. The app can then plan a route from your current location to a destination selected with a long press on the map.

Starting navigation
This section explains how to start navigating along the planned route and simulate the journey.
-
Create a
MapCoordinator
extension that contains the functions to start and configure navigation:1// MARK: Navigation23extension MapCoordinator {4 func createSimulatedLocationProvider(route: TomTomSDKRoute.Route) -> LocationProvider {5 let simulatedLocationProvider = SimulatedLocationProvider(delay: Measurement<UnitDuration>(value: 0.2, unit: .seconds))6 simulatedLocationProvider.updateCoordinates(route.geometry, interpolate: true)7 return simulatedLocationProvider8 }910 func createNavigation(locationProvider: LocationProvider) -> TomTomSDKNavigation.Navigation {11 let navigationConfiguration = NavigationConfiguration(12 apiKey: Keys.ttServicesAPIKey,13 locationProvider: locationProvider,14 routeReplanner: DefaultRouteReplanner(routePlanner: routePlanner, replanningPolicy: .findBetter),15 continuousReplanningMode: .automatic16 )17 let navigation = Navigation(configuration: navigationConfiguration)18 // Assign the MapCoordinator as respective observers to navigation in order to receive navigation callbacks about the journey19 navigation.addRouteObserver(self)20 navigation.addProgressObserver(self)21 navigation.addGuidanceObserver(self)22 return navigation23 }2425 func startNavigation(26 navigation: TomTomSDKNavigation.Navigation,27 locationEngine: TomTomSDKLocationProvider.LocationProvider,28 routePlan: TomTomSDKNavigationEngines.RoutePlan29 ) {30 // Set the navigation location engine31 navigation.stop()32 navigation.locationProvider = locationEngine3334 let navigationOptions = NavigationOptions(activeRoutePlan: routePlan)35 navigation.start(navigationOptions: navigationOptions)3637 // The map matched location engine snaps to the route38 let locationEngine = navigation.mapMatchedLocationProvider39 map?.locationProvider = locationEngine4041 // Set the camera tracking mode to follow the current position42 setCamera(trackingMode: .followRoute)43 }44}4546// MARK: TomTomSDKNavigation.NavigationRouteObserver -
Next update the
didLongPressOn
callback function fromTomTomMapDelegate
to plan the route and start navigating:1extension MapCoordinator: TomTomSDKMapDisplay.MapDelegate {2 func map(_: TomTomSDKMapDisplay.TomTomMap, didLongPressOn coordinate: CLLocationCoordinate2D) {3 Task { @MainActor in4 do {5 // Plan the route and add it to the map6 let routePlan = try await planRoute(to: coordinate)7 // First remove any old routes8 map?.removeRoutes()9 addRouteToMap(route: routePlan.route)1011 // Sart navigation after a short delay so that we can clearly see the transition to the driving view12 DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in13 guard let self = self else {14 return15 }1617 // Use real location updates18 // let locationProvider = self.map.locationProvider1920 // Use simulated location updates21 let locationEngine = self.createSimulatedLocationProvider(route: routePlan.route)2223 let navigation = self.navigation ?? self.createNavigation(locationProvider: locationEngine)24 self.startNavigation(25 navigation: navigation,26 locationEngine: locationEngine,27 routePlan: routePlan28 )29 self.navigation = navigation30 // Start location updates31 locationEngine.start()32 }33 } catch {34 print("Error when planning a route: \(error)")35 }36 }37 }3839 func map(_ map: TomTomMap, onInteraction interaction: MapInteraction) {40 // Handle gesture41 }4243 func map(_: TomTomSDKMapDisplay.TomTomMap, didChangeCameraTrackingMode mode: CameraTrackingMode) {44 // Handle camera tracking mode change45 }4647 func mapDidTapOnRecenterButton(_ map: TomTomSDKMapDisplay.TomTomMap) {48 // Handle recenter map49 }50}The
didLongPressOn
callback function starts navigation by callingstartNavigation
with the desired location provider. Experiment by switching between real and simulated GPS location providers as shown in the code snippet above. -
Extend
MapCoordinator
to conform to the entireNavigationObserver
protocol or to conform to certain protocols which composesNavigationObserver
protocol, depend on your needs. The observers are set in thecreateNavigation
function.1extension MapCoordinator: TomTomSDKNavigation.NavigationRouteObserver {2 func didDeviateFromRoute(currentRoute: TomTomSDKRoute.Route, location: GeoLocation) {}34 func didProposeRoutePlan(routePlan: RoutePlan, reason: RouteReplanningReason) {}56 func didReplanRoute(replannedRoute: TomTomSDKRoute.Route, reason: RouteReplanningReason) {7 print("replanned route")8 }910 func didReplanRouteOnLanguageChange(replannedRoute: TomTomSDKRoute.Route, reason: RouteReplanningReason, language: Locale) {11 print("replanned route as language is changed to \(language.identifier).")12 }13}1415// MARK: TomTomSDKNavigation.NavigationProgressObserver1617extension MapCoordinator: TomTomSDKNavigation.NavigationProgressObserver {18 func didUpdateProgress(progress: TomTomSDKNavigationEngines.RouteProgress) {19 print("distance along route = \(progress.distanceAlongRoute.tt.meters)m")20 }21}2223// MARK: TomTomSDKNavigation.NavigationGuidanceObserver2425extension MapCoordinator: TomTomSDKNavigation.NavigationGuidanceObserver {26 func didUpdateInstructions(instructions: [GuidanceInstruction]) {}2728 func didUpdateDistanceToNextInstruction(29 distance: Measurement<UnitLength>,30 instructions: [GuidanceInstruction],31 currentPhase: InstructionPhase32 ) {}3334 func didGenerateAnnouncement(announcement: GuidanceAnnouncement, shouldPlay: Bool) {35 guard shouldPlay else { return }36 textToSpeech.play(announcement: announcement)37 }3839 func didStartLaneGuidance(laneGuidance: LaneGuidance) {}4041 func didEndLaneGuidance(laneGuidance: LaneGuidance) {}42}4344// MARK: - TextToSpeech -
Now stub the play function in the
TextToSpeech
class:1class TextToSpeech {2 func play(announcement: TomTomSDKNavigation.GuidanceAnnouncement) {3 // Do nothing4 }5}
You can build the app after completing these steps. Do a long press on the map to plan a route to that location and start the navigation simulation.
Voice guidance
This section explains how to add voice instructions to the app.
-
Implement the
TextToSpeech
class as follows:1class TextToSpeech {2 // MARK: Lifecycle34 init(languageCode: String) {5 let ttsEngine = SystemTextToSpeechEngine(language: languageCode)6 self.ttsProvider = TomTomSDKTextToSpeech.makeTextToSpeech(ttsEngine: ttsEngine)7 }89 convenience init() {10 let languageCode = Locale.preferredLanguages.first ?? Locale.current.languageCode ?? "en-GB"11 self.init(languageCode: languageCode)12 }1314 // MARK: Internal1516 func play(announcement: TomTomSDKNavigationEngines.GuidanceAnnouncement) {17 guard !announcement.message.isEmpty else { return }18 var phonetics = [TomTomSDKTextToSpeechEngine.PhoneticTranscription]()1920 if let streetPhonetics = parseStreetPhonetics(phonetics: announcement.messagePhonetics) {21 phonetics.append(streetPhonetics)22 }23 if let signpostPhonetics = parseSignpostPhonetics(phonetics: announcement.messagePhonetics) {24 phonetics.append(signpostPhonetics)25 }26 if let roadNumberPhonetics = parseRoadNumberPhonetics(phonetics: announcement.messagePhonetics) {27 phonetics.append(roadNumberPhonetics)28 }29 let ttsMessage = TTSMessage.tagged(message: announcement.message, phonetics: phonetics)30 let priority = TTSMessagePriority(timeout: messageTimeout)31 ttsProvider.play(message: ttsMessage, priority: priority)32 }3334 // MARK: Private3536 private let messageTimeout: TimeInterval = 10.0 // seconds37 private let ttsProvider: TomTomSDKTextToSpeech.TextToSpeech3839 private func parseStreetPhonetics(phonetics: TomTomSDKRoute.Phonetics?) -> TomTomSDKTextToSpeechEngine40 .PhoneticTranscription? {41 guard let street = phonetics?.street, let languageCode = phonetics?.streetLanguage else {42 return nil43 }44 return PhoneticTranscription(45 transcriptions: [street],46 languages: [languageCode],47 tag: DefaultTags.streetName,48 alphabet: DefaultPhoneticAlphabets.ipa49 )50 }5152 private func parseSignpostPhonetics(phonetics: TomTomSDKRoute.Phonetics?) -> TomTomSDKTextToSpeechEngine53 .PhoneticTranscription? {54 guard let signpost = phonetics?.signpostText, let languageCode = phonetics?.signpostTextLanguage else {55 return nil56 }57 return PhoneticTranscription(58 transcriptions: [signpost],59 languages: [languageCode],60 tag: DefaultTags.signpost,61 alphabet: DefaultPhoneticAlphabets.ipa62 )63 }6465 private func parseRoadNumberPhonetics(phonetics: TomTomSDKRoute.Phonetics?) -> TomTomSDKTextToSpeechEngine66 .PhoneticTranscription? {67 guard let roadNumbers = phonetics?.roadNumbers, let languages = phonetics?.roadNumbersLanguages else {68 return nil69 }70 return PhoneticTranscription(71 transcriptions: roadNumbers,72 languages: languages,73 tag: DefaultTags.streetName,74 alphabet: DefaultPhoneticAlphabets.ipa75 )76 }77}The
TextToSpeech
class configures a text to speech provider using theSystemTextToSpeechEngine
. Phonetic data is parsed from theGuidanceAnnouncement
object and played back by the TTS provider. TheTextToSpeechEngine
protocol also declares functions for stopping playback, changing volume, and changing language.
You can build the app after completing these steps. You will be able to hear voice guidance during the navigation simulation.
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.