Article

Responding to Audio Session Route Changes

Observe audio session notifications to ensure that your app responds appropriately to route changes.

Overview

An important responsibility of AVAudioSession is managing audio route changes. A route change occurs when the system adds or removes an audio input or output. Route changes occur for several 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, the audio session 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 Audio in iOS Human Interface Guidelines). When users connect a pair of wired or wireless headphones, they’re implicitly indicating that audio playback should continue, but privately. They expect an app that’s currently playing media to continue playing without pause. However, when users disconnect their headphones, they don’t want to automatically share what they’re listening to with others. Applications should respect this implicit privacy request and automatically pause playback when users disconnect their headphones.

Observe Route Changes

You can directly observe route change notifications posted by the audio session. This might be useful if you want the system to notify you when a user connects 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() {
    // Get the default notification center instance.
    let nc = NotificationCenter.default
    nc.addObserver(self,
                   selector: #selector(handleRouteChange),
                   name: AVAudioSession.routeChangeNotification,
                   object: nil)
}

@objc func handleRouteChange(notification: Notification) {
    // To be implemented.
}

Respond to Route Changes

The posted Notification object contains a populated user-information dictionary providing the details of the route change. Determine the reason for this change by retrieving the AVAudioSession.RouteChangeReason value from the dictionary. When a user connects a new device, the reason is AVAudioSession.RouteChangeReason.newDeviceAvailable, and when a user removes a device, the reason is AVAudioSession.RouteChangeReason.oldDeviceUnavailable.

When a new device becomes available, you ask the audio session for its currentRoute to determine where the audio output is currently routed. This query returns an AVAudioSessionRouteDescription object that lists all of the audio session’s inputs and outputs. When the user removes a device, you retrieve the route description for the previous route from the user-information dictionary. In both cases, you query the route description for its outputs, which returns an array of port description 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 = AVAudioSession.RouteChangeReason(rawValue: reasonValue) else {
            return
    }
    
    // Switch over the route change reason.
    switch reason {

    case .newDeviceAvailable: // New device found.
        let session = AVAudioSession.sharedInstance()
        headphonesConnected = hasHeadphones(in: session.currentRoute)
    
    case .oldDeviceUnavailable: // Old device removed.
        if let previousRoute =
            userInfo[AVAudioSessionRouteChangePreviousRouteKey] as? AVAudioSessionRouteDescription {
            headphonesConnected = hasHeadphones(in: previousRoute)
        }
    
    default: ()
    }
}

func hasHeadphones(in routeDescription: AVAudioSessionRouteDescription) -> Bool {
    // Filter the outputs to only those with a port type of headphones.
    return !routeDescription.outputs.filter({$0.portType == .headphones}).isEmpty
}

See Also

Responding to Audio Session Notifications

Responding to Audio Session Interruptions

Observe audio session notifications to ensure that your app responds appropriately to interruptions.

class let interruptionNotification: NSNotification.Name

A notification that’s posted when an audio interruption occurs.

class let routeChangeNotification: NSNotification.Name

A notification that’s posted when the system’s audio route changes.

class let silenceSecondaryAudioHintNotification: NSNotification.Name

A notification that’s posted when the primary audio from other applications starts and stops.

class let mediaServicesWereLostNotification: NSNotification.Name

A notification that’s posted when the system terminates the media server.

class let mediaServicesWereResetNotification: NSNotification.Name

A notification that’s posted when the media server restarts.