What is the AVAudioSession, how do categories and modes decide behaviour, and how do you handle interruptions and route changes?

18.2.1 · L3 · Interview

Predict first

What are the default category and mode, and how do they read after changing them?

import AVFoundation

let s = AVAudioSession.sharedInstance()
print(s.category.rawValue, s.mode.rawValue)
try s.setCategory(.playback, mode: .moviePlayback, options: [])
print(s.category.rawValue, s.mode.rawValue)
try s.setCategory(.playAndRecord, mode: .default, options: [.defaultToSpeaker, .allowBluetooth])
print(s.category.rawValue, s.categoryOptions.contains(.defaultToSpeaker))
  • AVAudioSessionCategorySoloAmbient AVAudioSessionModeDefault, then AVAudioSessionCategoryPlayback AVAudioSessionModeMoviePlayback, then AVAudioSessionCategoryPlayAndRecord true
  • AVAudioSessionCategoryPlayback AVAudioSessionModeDefault, then AVAudioSessionCategoryPlayback AVAudioSessionModeMoviePlayback, then AVAudioSessionCategoryPlayAndRecord false
  • AVAudioSessionCategorySoloAmbient AVAudioSessionModeDefault, then the same, then the same

Predict first

What are the raw values of the interruption types and the unplugged-headphones route reason?

import AVFoundation

print(AVAudioSession.InterruptionType.began.rawValue, AVAudioSession.InterruptionType.ended.rawValue, AVAudioSession.RouteChangeReason.oldDeviceUnavailable.rawValue)
  • 1 0 2
  • 0 1 1
  • 2 1 0

Mental model. The audio session is telling the building manager how you use the shared sound system: “background music, ignore the quiet-hours switch” or “a phone call, everyone else mute”. When the fire alarm sounds (an interruption) you stop, and you only restart if the manager says so.

The audio session (AVAudioSession.sharedInstance()) is your app’s contract with the system about audio. Its category declares intent and decides defaults: .soloAmbient (the default: plays, respects the silent switch, silences other apps’ audio, and stops when the screen locks), .ambient (mixes with others, respects silent), .playback (music and video: ignores the silent switch, can play in the background with the Audio background mode, interrupts others unless you add .mixWithOthers), .record and .playAndRecord (calls and voice chat: with options such as .defaultToSpeaker and .allowBluetooth) and .multiRoute. A mode refines it for the use case (.moviePlayback, .videoChat, .voiceChat, .spokenAudio, .measurement which turns off processing), and options tune mixing and ducking (.duckOthers lowers other apps while you speak, .interruptSpokenAudioAndMixWithOthers). Set the category once early, then activate the session (setActive(true)) when you start playing and deactivate (with .notifyOthersOnDeactivation) when done so a paused music app can resume. The common bug: a playback app that never sets .playback stays on .soloAmbient, so it is silent when the ringer switch is off and stops in the background.

Interruptions (a phone call, an alarm, Siri, another app taking over) arrive as AVAudioSession.interruptionNotification with a type began (1) or ended (0): on began your audio is already paused or silenced (update the UI and state, do not assume playback continues); on ended check the shouldResume option and resume only if it says so, and re-activate the session. Route changes (routeChangeNotification) report why the output changed: the important reason is oldDeviceUnavailable (2: headphones unplugged or a Bluetooth device disconnected), where the convention is to pause so audio does not suddenly blast from the speaker; newDeviceAvailable and category changes usually need no action. Inspect currentRoute to show the output device, and use AVRoutePickerView for the AirPlay and route picker. Also handle mediaServicesWereReset (rebuild your audio objects), secondaryAudioShouldSilence, Bluetooth latency and sample-rate differences, and background audio requirements (the capability plus an active session). Test on a device: the simulator does not model calls, routes or the silent switch faithfully. For media apps, pair the session with Now Playing info and remote commands so the system and headphones can control playback.

An app starts in solo-ambient and default mode. Setting a category and mode changes them, and options such as default-to-speaker can be read back.

An interruption begins with value 1 and ends with 0, and a device that went away is route-change reason 2.

Common traps

  • Leaving the default .soloAmbient category in a media app. Audio is silenced by the ringer switch and stops in the background; use .playback.
  • Not pausing on oldDeviceUnavailable. Unplugging headphones moves audio to the speaker; pause by convention.

They might follow up with

  • When do you resume after an interruption? When the ended notification’s options include shouldResume.
  • What does .duckOthers do? Lowers other apps’ audio while yours plays, such as a navigation prompt over music.

Related questions

  • 18.1.1 How do AVAsset, AVPlayerItem and AVPlayer fit together, what is CMTime, and why does playback “not start”?
  • 18.2.2 How does AVAudioEngine work: nodes, formats, buffers, and when do you use it instead of AVAudioPlayer?
  • 18.4.2 How do Now Playing info, remote commands, Picture in Picture and AirPlay integrate a player with the system?

How does AVAudioEngine work: nodes, formats, buffers, and when do you… →