I'm using Apple's example of an Observable wrapper around SFSpeechRecognizer
as follows:
class SpeechRecognizer: ObservableObject {
@Published var transcript: String
func transcribe() {}
}
The goal is to use a ViewModel to both consume the transcript as it is generated, as well as passing on the value to a SwiftUI View for visual debugging:
class ViewModel : ObservableObject {
@Published var SpeechText: String = ""
@ObservedObject var speech: SpeechRecognizer = SpeechRecognizer()
public init() {
speech.transcribe()
speech.transcript.publisher
.map { $0 as! String? ?? "" }
.sink(receiveCompletion: {
print ($0) },
receiveValue: {
self.SpeechText = $0
self.doStuff(transcript: $0)
})
}
private void doStuffWithText(transcript: String) {
//Process the output as commands in the application
}
}
I can confirm that if I observe transcript
directly in a SwiftUI view, that the data is flowing through. My problem is receiving the values as they change, and then assigning that data to my own published variable.
How do I make this work?
Subscription should be stored otherwise it is canceled immediately, also you need to make subscription before actual usage (and some other memory related modifications made). So I assume you wanted something like:
class ViewModel : ObservableObject {
@Published var SpeechText: String = ""
var speech: SpeechRecognizer = SpeechRecognizer() // << here !!
private var subscription: AnyCancellable? = nil // << here !!
public init() {
self.subscription = speech.transcript.publisher // << here !!
.map { $0 as! String? ?? "" }
.sink(receiveCompletion: {
print ($0) },
receiveValue: { [weak self] value in
self?.SpeechText = value
self?.doStuffWithText(transcript: value)
})
self.speech.transcribe() // << here !!
}
private func doStuffWithText(transcript: String) {
//Process the output as commands in the application
}
}
Tested with Xcode 13.2