swiftswiftuiavspeechsynthesizer

AVSpeechSynthesizer isSpeaking not working in Swift


So after updating to Xcode 12.0.1 AVSpeechSynthesizer is now working on simulator (it hasn't been working for me for a while). Now, the isSpeaking variable is always false regardless of whether the synthesizer is speaking. I would like to trigger a change in my view as a function of whether the synthesizer is speaking. Simple version below, any ideas?

import SwiftUI
import AVFoundation

struct ContentView: View {

var synthesizer = AVSpeechSynthesizer()
var utterance = AVSpeechUtterance(string: "Hello World")

var body: some View {
    VStack {
        Text(synthesizer.isSpeaking ? "Speaking" : "Not Speaking")
        Button(action: {synthesizer.speak(utterance)}) {
            Text("Speak To Me")
        }
    }
}
}

Solution

  • It is just not updated in your case, because synthesiser.isSpeaking is not observable for SwiftUI. You have to use view model class and delegate callbacks to handle this state changes

    Here is initial demo (you can add stop/pause/continue actions by yourself)

    class SpeachViewModel: NSObject, ObservableObject, AVSpeechSynthesizerDelegate {
        @Published var isSpeaking = false
    
        private var synthesizer = AVSpeechSynthesizer()
        override init() {
            super.init()
            synthesizer.delegate = self
        }
    
        deinit {
            synthesizer.delegate = nil
        }
    
        func speak(_ utterance: AVSpeechUtterance) {
            self.synthesizer.speak(utterance)
        }
    
        // MARK: AVSpeechSynthesizerDelegate
        internal func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didStart utterance: AVSpeechUtterance) {
            self.isSpeaking = true
        }
    
        internal func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
            self.isSpeaking = false
        }
    
        internal func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didPause utterance: AVSpeechUtterance) {
            self.isSpeaking = false
        }
    
        internal func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didCancel utterance: AVSpeechUtterance) {
            self.isSpeaking = false
        }
    
        internal func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didContinue utterance: AVSpeechUtterance) {
            self.isSpeaking = true
        }
    }
    
    struct ContentView: View {
    
        @ObservedObject var vm = SpeachViewModel()
        var utterance = AVSpeechUtterance(string: "Hello World")
    
        var body: some View {
            VStack {
                Text(vm.isSpeaking ? "Speaking" : "Not Speaking")
                Button(action: {vm.speak(utterance)}) {
                    Text("Speak To Me")
                }
            }
        }
    }