Article

Responding to Audio Session Route Changes

Directly observe audio session notifications to ensure that your app responds to interruptions.

Overview

An important responsibility of AVAudioSession is managing audio route changes. A route change occurs when an audio input or output is added to or removed from an iOS device. Route changes occur for a number of reasons, including a user plugging in a pair of headphones, connecting a Bluetooth LE headset, or unplugging a USB audio interface. When these changes occur, AVAudioSession reroutes audio signals accordingly and broadcasts a notification containing the details of the change to any registered observers.

An important behavior related to route changes occurs when a user plugs in or removes a pair of headphones (see Sound in iOS Human Interface Guidelines). When users connect a pair of wired or wireless headphones, they are implicitly indicating that audio playback should continue, but privately. They expect an app that is currently playing media to continue playing without pause. When users unplug their headphones, they don’t want to automatically share what they are listening to with others. Applications should respect this implicit privacy request and automatically pause playback when headphones are removed.

AVPlayer monitors your app’s audio session and responds appropriately to route changes. When headphones are connected, playback continues as expected. When headphones are removed, playback is automatically paused. To observe this AVPlayer behavior, use key-value observing (KVO) on the player’s rate property so that you can update your user interface as the player is paused in response to an audio route change.

Observe for Route Change Notifications

You can also directly observe any route change notifications posted by AVAudioSession. This might be useful if you want to be notified when a user connects or removes headphones so you can present an icon or message in the player interface. To observe audio route changes, begin by registering to observe notifications of type routeChangeNotification.

func setupNotifications() {
    let notificationCenter = NotificationCenter.default
    notificationCenter.addObserver(self,
                                   selector: #selector(handleRouteChange),
                                   name: .AVAudioSessionRouteChange,
                                   object: nil)
}

@objc func handleRouteChange(notification: Notification) {

}

Respond to Route Change Notifications

The posted NSNotification object contains a populated userInfo dictionary providing the details of the route change. Determine the reason for this change by retrieving the AVAudioSession.RouteChangeReason value from the userInfo dictionary. When a new device is connected, the reason is AVAudioSession.RouteChangeReason.newDeviceAvailable, and when a value is removed, the reason is AVAudioSession.RouteChangeReason.oldDeviceUnavailable.

When a new device becomes available, you ask the AVAudioSession for its currentRoute to determine where the audio output is currently routed. This query returns an AVAudioSessionRouteDescription listing all of the audio session’s inputs and outputs. When a device is removed, you retrieve the AVAudioSessionRouteDescription for the previous route from the userInfo dictionary. In both cases, you query the route description for its outputs, which returns an array of AVAudioSessionPortDescription objects providing the details of the audio output routes.

@objc func handleRouteChange(notification: Notification) {
    guard let userInfo = notification.userInfo,
        let reasonValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt,
        let reason = AVAudioSessionRouteChangeReason(rawValue:reasonValue) else {
            return
    }
    switch reason {
    case .newDeviceAvailable:
        let session = AVAudioSession.sharedInstance()
        for output in session.currentRoute.outputs where output.portType == AVAudioSessionPortHeadphones {
            headphonesConnected = true
            break
        }
    case .oldDeviceUnavailable:
        if let previousRoute =
            userInfo[AVAudioSessionRouteChangePreviousRouteKey] as? AVAudioSessionRouteDescription {
            for output in previousRoute.outputs where output.portType == AVAudioSessionPortHeadphones {
                headphonesConnected = false
                break
            }
        }
    default: ()
    }
}

See Also

Responding to Audio Session Notifications

Responding to Audio Session Interruptions

Directly observe audio session notifications to ensure that your app responds to interruptions.

class let interruptionNotification: NSNotification.Name

Posted when an audio interruption occurs.

class let routeChangeNotification: NSNotification.Name

Posted when the system’s audio route changes.

class let silenceSecondaryAudioHintNotification: NSNotification.Name

Posted when the primary audio from other applications starts and stops.

class let mediaServicesWereLostNotification: NSNotification.Name

Posted when the media server is terminated.