Mark
Mark

Reputation: 103

Why is NSSpeechSynthesizer not calling its delegate methods?

When I call the startSpeaking(String) method on a NSSpeechSynthesizer instance, with a delegate set, speaking occurs but the following delegate methods never fire:

speechSynthesizer(_ sender: NSSpeechSynthesizer, 
              willSpeakWord characterRange: NSRange, 
                         of string: String)

func speechSynthesizer(_ sender: NSSpeechSynthesizer, 
          didFinishSpeaking finishedSpeaking: Bool)

Why? I'm using Swift 4; XCode 9.1; MacOS 10.13.1

Steps to Reproduce:

Compile and run the following:

import AppKit

class SynthDelegate: NSObject, NSSpeechSynthesizerDelegate {

    var str = ""

    func speechSynthesizer(_ sender: NSSpeechSynthesizer, willSpeakWord characterRange: NSRange, of string: String) {
        str = "spoke a word"
    }

    func speechSynthesizer(_ sender: NSSpeechSynthesizer, didFinishSpeaking finishedSpeaking: Bool) {
        str = "finished speaking"
    }

}

let mySpeaker = NSSpeechSynthesizer()

let myDelegate = SynthDelegate()

mySpeaker.delegate = myDelegate

mySpeaker.startSpeaking("test string to read aloud")

sleep(5) // keep alive while speaking

print(myDelegate.str)

Expected Results:

"finished speaking"

Actual Results:

""

Update

Building on OOPers excellent answer, the following WORKS in a new test project:

import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {



    func applicationDidFinishLaunching(_ aNotification: Notification) {
        // Insert code here to initialize your application

        let mySpeaker = NSSpeechSynthesizer()
        let myDelegate = SynthDelegate()
        mySpeaker.delegate = myDelegate
        mySpeaker.startSpeaking("test string to read aloud")


        Timer.scheduledTimer(withTimeInterval: 5.0, repeats: false) {_ in
            print(myDelegate.str)

        }
    }

    func applicationWillTerminate(_ aNotification: Notification) {
        // Insert code here to tear down your application
    }


}


class SynthDelegate: NSObject, NSSpeechSynthesizerDelegate {
    var str = ""

    func speechSynthesizer(_ sender: NSSpeechSynthesizer, willSpeakWord characterRange: NSRange, of string: String) {
        str = "spoke a word"
        print(str)
    }

    func speechSynthesizer(_ sender: NSSpeechSynthesizer, didFinishSpeaking finishedSpeaking: Bool) {
        str = "finished speaking"
        print(str)
    }
}

Upvotes: 0

Views: 597

Answers (1)

OOPer
OOPer

Reputation: 47886

Are you testing your code in the Playground? Anyway, you should never call sleep() in your apps including Playground code.

It prevents many activities needed to make frameworks work such as calling delegate methods. You can use Timer when you need to execute some code after a delay.

Testable in the Playground:

import AppKit

class SynthDelegate: NSObject, NSSpeechSynthesizerDelegate {
    var str = ""

    func speechSynthesizer(_ sender: NSSpeechSynthesizer, willSpeakWord characterRange: NSRange, of string: String) {
        str = "spoke a word"
        print(str)
    }

    func speechSynthesizer(_ sender: NSSpeechSynthesizer, didFinishSpeaking finishedSpeaking: Bool) {
        str = "finished speaking"
        print(str)
    }
}

let mySpeaker = NSSpeechSynthesizer()
let myDelegate = SynthDelegate()
mySpeaker.delegate = myDelegate
mySpeaker.startSpeaking("test string to read aloud")

import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

Timer.scheduledTimer(withTimeInterval: 5.0, repeats: false) {_ in
    print(myDelegate.str)
    PlaygroundPage.current.finishExecution()
}

Upvotes: 2

Related Questions