I am developing an app for Vehicle owners with a built-in map navigation feature with voice navigation support. The app works fine without voice navigation but when I use voice navigation it occasionally crashes and it crashes while voice navigation is not in progress.
What makes it impossible to diagnose is that even though it crashed 10 times on the flight, I don't see any crash reports in 'Apple Connect'. I tried running it in a simulator and it didn't crash there! but on a real device, when I drive with the app navigating me I crashes abruptly after a few minutes and it's not while the voice navigation is speaking! I also ran the app without AVFoundation and it did not crash then. So I am 100% sure it is something with this AVFoundation framework.
If anyone can help find the problem in my following code it would be really helpful.
import SwiftUI
import AVFoundation
struct DirectionHeaderView: View {
@Environment(\.colorScheme) var bgMode: ColorScheme
var directionSign: String?
var nextStepDistance: String
var instruction: String
@Binding var showDirectionsList: Bool
@Binding var height: CGFloat
@StateObject var locationDataManager: LocationDataManager
@State private var synthesizer = AVSpeechSynthesizer()
@State private var audioSession = AVAudioSession.sharedInstance()
@State private var lastInstruction: String = ""
@State private var utteranceDistance: String = ""
@State private var isStepExited = false
@State private var range = 20.0
var body: some View {
VStack {
HStack {
VStack {
if let directionSign = directionSign {
Image(systemName: directionSign)
}
if !instruction.contains("Re-calculating the route...") {
Text("\(nextStepDistance)")
.onChange(of: nextStepDistance) {
let distance = getDistanceInNumber(distance: nextStepDistance)
if distance <= range && !isStepExited {
startVoiceNavigation(with: instruction)
isStepExited = true
}
}
}
}
Spacer()
Text(instruction)
.onAppear {
isStepExited = false
utteranceDistance = nextStepDistance
range = nextStepRange(distance: utteranceDistance)
startVoiceNavigation(with: "In \(utteranceDistance), \(instruction)")
}
.onChange(of: instruction) {
isStepExited = false
utteranceDistance = nextStepDistance
range = nextStepRange(distance: utteranceDistance)
startVoiceNavigation(with: "In \(utteranceDistance), \(instruction)")
}
.padding(10)
Spacer()
}
}
.padding(.horizontal,10)
.background(bgMode == .dark ? Color.black.gradient : Color.white.gradient)
}
func startVoiceNavigation(with utterance: String) {
if instruction.isEmpty || utterance.isEmpty {
return
}
if instruction.contains("Re-calculating the route...") {
synthesizer.stopSpeaking(at: AVSpeechBoundary.immediate)
return
}
let thisUttarance = AVSpeechUtterance(string: utterance)
lastInstruction = instruction
if audioSession.category == .playback && audioSession.categoryOptions == .mixWithOthers {
DispatchQueue.main.async {
synthesizer.speak(thisUttarance)
}
}
else {
setupAudioSession()
DispatchQueue.main.async {
synthesizer.speak(thisUttarance)
}
}
}
func setupAudioSession() {
do {
try audioSession.setCategory(AVAudioSession.Category.playback, options: AVAudioSession.CategoryOptions.mixWithOthers)
try audioSession.setActive(true)
}
catch {
print("error:\(error.localizedDescription)")
}
}
func nextStepRange(distance: String) -> Double {
var thisStepDistance = getDistanceInNumber(distance: distance)
if thisStepDistance != 0 {
switch thisStepDistance {
case 0...200:
if locationDataManager.speed >= 90 {
return thisStepDistance/1.5
}
else {
return thisStepDistance/2
}
case 201...300:
if locationDataManager.speed >= 90 {
return 120
}
else {
return 100
}
case 301...500:
if locationDataManager.speed >= 90 {
return 150
}
else {
return 125
}
case 501...1000:
if locationDataManager.speed >= 90 {
return 250
}
else {
return 200
}
case 1001...10000:
if locationDataManager.speed >= 90 {
return 250
}
else {
return 200
}
default:
if locationDataManager.speed >= 90 {
return 250
}
else {
return 200
}
}
}
return 200
}
func getDistanceInNumber(distance: String) -> Double {
var thisStepDistance = 0.0
if distance.contains("km") {
let stepDistanceSplits = distance.split(separator: " ")
let stepDistanceText = String(stepDistanceSplits[0])
if let dist = Double(stepDistanceText) {
thisStepDistance = dist * 1000
}
}
else {
var stepDistanceSplits = distance.split(separator: " ")
var stepDistanceText = String(stepDistanceSplits[0])
if let dist = Double(stepDistanceText) {
thisStepDistance = dist
}
}
return thisStepDistance
}
}
#Preview {
DirectionHeaderView(directionSign: "", nextStepDistance: "", instruction: "", showDirectionsList: .constant(false), height: .constant(0), locationDataManager: LocationDataManager())
}