Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
This a singleton class that lets dev start, pause, stop an speech in a correct way. Because is a singleton, you can share the instance throw the app. Also, because we set an AVAudioSession, we are able to keep listening the audio when the app goes to the background. To work correctly, you must to set the Capability 'Audio, AirPlay, and Picture i…
//
// SpeechManager.swift
//
// Created by Marcelo Perretta on 22/04/2022.
//
import Foundation
import AVFoundation
// We need this because the delegate doesnt works correctly
enum SpeechState {
case reading, paused, stopped
}
final class SpeechManager {
private let synthesizer: AVSpeechSynthesizer = AVSpeechSynthesizer()
private let audioSession = AVAudioSession.sharedInstance()
private var speechState: SpeechState = .stopped
var utterance: AVSpeechUtterance?
// init() {
// synthesizer.usesApplicationAudioSession = true
// }
static var shared = SpeechManager()
deinit {
speechState = .stopped
do {
try audioSession.setActive(false, options: .notifyOthersOnDeactivation)
} catch {
print("audioSession properties weren't disable.")
}
}
func playContent(_ content: String, withLanguge language: String?) {
utterance = AVSpeechUtterance(string: content)
if let language = language {
utterance?.voice = AVSpeechSynthesisVoice(language: language)
}
guard let utterance = utterance else { return }
playContent(utterance)
}
func playContent(_ content: AVSpeechUtterance) {
if synthesizer.isSpeaking {
synthesizer.stopSpeaking(at: .immediate)
}
// This script let us to keep playing the audio speech on background
do {
try audioSession.setCategory(
AVAudioSession.Category.playback,
options: AVAudioSession.CategoryOptions.duckOthers
)
} catch {
print("ERRR CREATING AUDIO SESSION: \(error)")
}
synthesizer.speak(content)
speechState = .reading
}
func pauseContent() {
guard speechState == .reading else { return }
synthesizer.pauseSpeaking(at: .immediate)
speechState = .paused
}
func resumeContent() {
guard speechState == .paused else { return }
synthesizer.continueSpeaking()
speechState = .reading
}
func stopContent() {
synthesizer.stopSpeaking(at: .immediate)
utterance = nil
speechState = .stopped
}
func toggleContent() {
if speechState == .reading {
pauseContent()
} else if speechState == .paused {
resumeContent()
} else {
assertionFailure("NEEDS CREATE A CONTENT")
stopContent()
guard let utterance = utterance else { return }
playContent(utterance)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment