Handling Audio Interruptions Without Losing Your Place
Spoken reading should survive the small interruptions that happen on a phone. The reader should not make someone start a long document again after a call, Siri request, or another audio app takes focus.
The problem
The first version could play and pause, but it did not own an audio session. An interruption could leave playback paused without a useful state or a reliable place to continue from.
// Before: playback had no interruption state
func play(_ utterance: AVSpeechUtterance) {
synthesizer.speak(utterance)
}
// A call or Siri request could stop this without a saved resume path.
The change
SpeechService now configures a spoken playbackAVAudioSession and listens for iOS interruption notifications. When an interruption begins, it pauses at a word boundary, keeps the live character offset, and exposes anInterrupted state. It resumes only when iOS sends theshouldResume hint.
// After: pause at a word and resume only when iOS allows it
case .began:
_ = synthesizer.pauseSpeaking(at: .word)
isPaused = true
isInterrupted = true
case .ended where options.contains(.shouldResume):
activateAudioSession()
_ = synthesizer.continueSpeaking()
isInterrupted = false
Result
The normal playback controls stay the same. The extra work appears only when the system takes over, which is the right outcome for this kind of reliability work. Background playback also has an explicit audio mode, as long as the app process is still alive.