David Cordero
Supporting AirPods in your iOS and tvOS media Apps
Published on 19 Oct 2020
The AirPods are equipped with sensors that allow users to trigger some gestures like double-tap or automatic ear detection, to control media playback.
The behavior of each of these gestures can be configured in the Settings of the operating system among the options: Siri, Play/Pause, Next Track, Previous Track, or Off.
By default, if you are using AVPlayerViewController, some of these gestures (like Play/Pause) will work out of the box.
The good news
If you are using your custom player, or you still need to extend the basic support provided by AVPlayerViewController, the good news is that the AirPods behave like any other Remote Control. In terms of development, that means that we can define our custom handlers for each gesture via MPRemoteCommandCenter.
Here you have an example of a simple player defining custom handlers for the gestures: Play/Pause, Next Track and Previous Track.
import UIKit
import AVFoundation
import AVKit
import MediaPlayer
class ViewController: AVPlayerViewController {
override func viewDidLoad() {
super.viewDidLoad()
play(stream: URL(string: "https://devstreaming-cdn.apple.com/videos/streaming/examples/img_bipbop_adv_example_ts/master.m3u8")!)
setUpRemoteCommandCenter()
}
// MARK: - Private
private func play(stream: URL) {
let asset = AVAsset(url: stream)
let playetItem = AVPlayerItem(asset: asset)
player = AVPlayer(playerItem: playetItem)
player?.play()
}
private func setUpRemoteCommandCenter() {
let remoteCommandCenter = MPRemoteCommandCenter.shared()
remoteCommandCenter.previousTrackCommand.isEnabled = true
remoteCommandCenter.previousTrackCommand.addTarget(
handler: {
[weak self ] (_) -> MPRemoteCommandHandlerStatus in
self?.previousTrackWasPressed()
return .success
})
remoteCommandCenter.nextTrackCommand.isEnabled = true
remoteCommandCenter.nextTrackCommand.addTarget(
handler: {
[weak self ] (_) -> MPRemoteCommandHandlerStatus in
self?.nextTrackWasPressed()
return .success
})
[remoteCommandCenter.playCommand,
remoteCommandCenter.pauseCommand].forEach {
$0.isEnabled = true
$0.addTarget(handler: {
[weak self] (_) -> MPRemoteCommandHandlerStatus in
self?.playPauseTrackWasPressed()
return .success
})
}
}
private func previousTrackWasPressed() {
// TODO: Add here your logic for the previousTrack handler
}
private func nextTrackWasPressed() {
// TODO: Add here your logic for the nextTrack handler
}
private func playPauseTrackWasPressed() {
// TODO: Add here your logic for the playPause handler
}
}
The bad news
The bad news (for tvOS) is that, since tvOS 14.0, MPRemoteCommandCenter is buggy and it does not longer work as expected. Hopefully Apple will eventually fix this topic.
You can find more information about this topic in this thread in the Apple Developer Forums