mapbox / mapbox/mapbox-navigation-ios
RouteVoiceController is silent when starting navigation on CarPlay without using phone
Nobody has claimed this yet.
- Dominant language
- Swift
- Stars
- 918
- Forks
- 326
- Avg merge
- 1h 16m
- Merged PRs (30d)
- 3
Description
A RouteVoiceController needs to be present for the lifetime of not only a NavigationViewController but also a CarPlay `navigation` activity.
## Problem
If the user opens the application and starts navigation on the CarPlay screen while the application isn’t visible on the connected iPhone screen, then the application runs in background mode, and the NavigationViewController’s RouteVoiceController apparently doesn’t produce any audible sound.
## Diagnosis
I’m not 100% clear on the reason the voice controller doesn’t work in this scenario, but we had been assuming that the phone’s UI would always drive voice instructions while connected to CarPlay. The audio would get routed through the car speakers, which the user would perceive as the CarPlay UI making those announcements. But starting navigation while the phone is locked seems to break that assumption.
## Workaround
The workaround is to silence the built-in voice controller in favor of one managed by the application.
`NavigationViewController.voiceController` and `RouteVoiceController.speechSynthesizer` are non-optional, but the default speech synthesizer is multiplexed (to allow a fallback to VoiceOver when the Mapbox Voice API is unavailable). The multiplexed speech synthesizer can fall back to any number of component speech synthesizers or none at all. So you can effectively take away its ability to say anything by emptying the array of component speech synthesizers:
```swift
if let synthesizer = navigationViewController.voiceController.speechSynthesizer as? MultiplexedSpeechSynthesizer {
synthesizer.speechSynthesizers = []
}
```
Then your application delegate can initialize and own a RouteVoiceController instance:
```swift
let credentials = navigationService.directions.credentials
voiceController = RouteVoiceController(navigationService: navigationService, accessToken: credentials.accessToken, host: credentials.host.absoluteString)
```
You’d be responsible for managing this voice controller and ensuring that it’s present as long as either CarPlayManager or NavigationViewController is around.
## Proposed solution
This workaround suggests that we should allow CarPlayNavigationViewController (or CarPlayManager?) and NavigationViewController to share a single RouteVoiceController, just as they share a single NavigationService. I don’t think this necessarily means RouteVoiceController needs to be a singleton – though that would certainly prevent a chorus of voices when there’s a bug. Rather, we should consider the voice controller to be just one more resource shared between two screens within a given trip.
/cc @mapbox/navigation-ios @OttyLab
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing RouteVoiceController ownership across NavigationViewController, CarPlayNavigationViewController, and CarPlayManager, including the existing MultiplexedSpeechSynthesizer workaround. Determine how the shared NavigationService is managed during a CarPlay navigation activity, then verify that voice instructions remain available when the phone UI is in the background.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- ios, swift
- Domain
- mobile-dev
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100