I was using the following code to detect whether or not AirPlay is the current selected route:
let airPlayActive = AVAudioSession
.sharedInstance()
.currentRoute
.outputs
.first?
.portType == .airPlay
print("AirPlay active: \(airPlayActive)")
However, that doesn't work in a very specific scenario. My use case is knowing that air play is selected before playing a video. I have two view controllers, say A and B. For testing purposes, I have a timer on A that prints if AirPlay is active every second using the code above.
From A, I present modally view controller B, which basically plays a video using AVPlayer
and controls the current selected route using a MPVolumeView
. If I present B, waits for the video to play and then change the route to AirPlay, I can see that change being reflected on A (it will start printing true
). Then, if I close B and then reopens it, it's possible to tell if AirPlay is active even before playing the video, and that's exactly what I want.
But there's a scenario where this doesn't work, though.
The scenario is (as weird as it may seem), if I add targets to remote commands using MPRemoteCommandCenter
, that behavior I described above simply changes! Here's how I'm adding the targets (in view controller B):
let center = MPRemoteCommandCenter.shared()
let pauseCommand = center.pauseCommand
pauseCommand.isEnabled = true
pauseCommand.addTarget(handler: { _ -> MPMPRemoteCommandHandlerStatus in
// ... doing some business logic
return .success
})
I am also removing that target in B's deinit
:
let center = MPRemoteCommandCenter.shared()
let pauseCommand = center.pauseCommand
pauseCommand.isEnabled = false
pauseCommand.addTarget(nil)
Well, this just messes entirely with all AirPlay related stuff:
If I activate AirPlay on view controller B and then dismiss it, AirPlay gets deactivated. Now current portType
becomes .builtInSpeaker
again. Thus, I can not tell if AirPlay is active before reopening B (playing the video).
Even though portType
is .builtInSpeaker
after closing B, I still get true
if I call MPVolumeView().isWirelessRouteActive
, which tells me that a wireless route is active, but that's not necessarily AirPlay (it could be a Bluetooth headphone, for instance).
If I reopen B, portType
goes from .builtInSpeaker
to .airPlay
after the video starts playing.
The audio route change notification also stops working properly. When observing AVAudioSession.routeChangeNotification
, it doesn't trigger sometimes and when it does, the user info at AVAudioSessionRouteChangeReasonKey
most of the times is .unknown
.
For me, this is clearly a bug on Apple side, but would any of you happen to know an alternative? I basically need to know if AirPlay is active before playing the video.
Thanks!
Well, I found a workaround. It's as weird as the issue itself, but it's working...
I just added a target to a remote command right before playing video on view controller B. Then I just wait for a audio route change notification (or a timeout in case is not active). That signals it is "safe" to play the video.