뭘 하려고 했냐면
Subsonic 기반 음악 스트리밍 앱을 만들고 있었는데요. AVPlayer로 백그라운드 재생까지 잘 되고 있었거든요. 그런데 테스트하다가 이상한 걸 발견했어요.
- 노래 재생하고 앱을 백그라운드로 보냄
- YouTube 열어서 영상 재생 (노래 멈춤 — 여기까진 정상)
- YouTube 영상 닫음
- 앱에 돌아오면 일시정지 버튼이 보이는데 소리가 안 남
버튼을 누르면 재생이 시작되긴 하는데, 한 번 더 눌러야 하는 거예요. UI는 “재생 중"인데 실제론 멈춰있는 상태.
원인
AVPlayer 기반 앱은 다른 앱이 오디오 세션을 가져가면 시스템이 자동으로 AVPlayer를 pause시켜요. 문제는 이 사실을 앱에 알려주지 않는다는 거예요. 정확히는, 알려주긴 하는데 직접 들어야 해요.
AVAudioSession.interruptionNotification이라는 노티피케이션을 쏴주거든요. 이걸 안 듣고 있으면 AVPlayer는 멈췄는데 ViewModel의 isPlaying은 true인 상태가 돼요.
해결
AudioPlayerManager에 인터럽션 옵저버를 추가했어요:
private func setupInterruptionObserver() {
interruptionObserver = NotificationCenter.default.addObserver(
forName: AVAudioSession.interruptionNotification,
object: AVAudioSession.sharedInstance(),
queue: .main
) { [weak self] notification in
guard let self,
let userInfo = notification.userInfo,
let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt,
let type = AVAudioSession.InterruptionType(rawValue: typeValue)
else { return }
switch type {
case .began:
Task { @MainActor in self.onInterruptionBegan?() }
case .ended:
let options = (userInfo[AVAudioSessionInterruptionOptionKey] as? UInt)
.flatMap { AVAudioSession.InterruptionOptions(rawValue: $0) } ?? []
let shouldResume = options.contains(.shouldResume)
Task { @MainActor in self.onInterruptionEnded?(shouldResume) }
@unknown default:
break
}
}
}
핵심은 .ended 이벤트의 shouldResume 플래그예요. 시스템이 “너 다시 재생해도 돼"라고 알려주는 건데, 이게 true면 자동으로 재생을 재개할 수 있어요.
ViewModel 쪽은 간단해요:
func handleInterruptionBegan() {
guard isPlaying else { return }
isPlaying = false
}
func handleInterruptionEnded(shouldResume: Bool) {
if shouldResume {
isPlaying = true
audioManager?.togglePlayPause()
}
}
이렇게 하면:
- YouTube가 오디오를 가져가면 → 버튼이 자동으로 일시정지 상태로 바뀜
- YouTube 영상을 닫으면 → 자동으로 노래가 다시 재생됨
Spotify나 Apple Music이랑 똑같은 동작이에요.
주의할 점
shouldResume이 항상 true인 건 아니에요. 전화가 와서 인터럽션이 걸린 경우엔 통화 종료 후 shouldResume = true가 오지만, 어떤 앱은 오디오 세션을 놓으면서 이 플래그를 안 보내기도 해요. 그래서 shouldResume = false일 때는 일시정지 상태를 유지하는 게 안전해요.
배운 점
AVPlayer를 쓰는 음악/팟캐스트 앱이라면 interruptionNotification은 선택이 아니라 필수예요. 안 넣으면 UI가 거짓말하는 앱이 됩니다.