この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
CX事業本部の中安です。まいどです。
前記事では「シンプルなオーディオプレイヤーを作ってみよう」という記事の続きとして、 「イヤホン(ヘッドホン)が抜けて音声が外に漏れる事故がないように、接続状態をきちんと監視しよう」という記事にしたためました。
今回は、さらにオーディオプレイヤーアプリに見つかった "ある欠陥" への対策となります。 それはイヤホンで音楽を聞いているときに、PayPayなどで支払いをすると音が止まってしまうことでした。
アプリをバックグラウンド再生可能にしていると、他のアプリが音声を再生したりするとセッションが取られてしまいます。 また、音声の中断理由としては電話の入電なども考えられます。
その度に毎回アプリに戻って音声を再生し直すという、ユーザにとってなんとも面倒臭いアプリになってしまうので、 その対策を入れていきましょう。
イベントを取る
実件方法は、前回と同じように
AVFoundation
をインポートし、AVAudioSession.interruptionNotification
を NotificationCenter
経由で監視してやります。
実装例を以下のような感じです。
// ビューコントローラであれば viewDidLoad。その他クラスであれば init() とかに書く
NotificationCenter.default.addObserver(
self,
selector: #selector(didAudioSessionInterruption(_:)),
name: AVAudioSession.interruptionNotification,
object: nil
)
セレクタとして定義したメソッドも実装します。
@objc private func didAudioSessionInterruption(_ notification: Notification) {
// 後述します
)
今定義した didAudioSessionInterruption(_:)
は、音声再生の中断が「始まった時」も「終わった時」も同じ箇所が呼ばれます。
そのため、それを判別するためにはイベント通知の中身を見る必要があります。
@objc private func didAudioSessionInterruption(_ notification: Notification) {
guard
let userInfo = notification.userInfo,
let interruptionTypeRawValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt,
let interruptionType = AVAudioSession.InterruptionType(rawValue: interruptionTypeRawValue)
else {
return
}
:
上記ソースコードのように IntegerLiteralType
という列挙型のオブジェクトを userInfo
から取得します。
そして、それをswitch文でケース分けしてやります。
switch interruptionType {
// 音声の中断が始まった時
case .began:
// 音声の中断が終わった時
case .ended:
@unknown default:
fatalError()
}
音声中断後の挙動を考える
今回は、PayPayアプリなどを使って支払いするときに「ペイペイっ」と音が鳴ってしまうことで、音声の再生が中断されてしまうことを想定しています。
「ペイペイっ」と鳴った後に自動的に音声再生が再開されるために、以下のように実装をします。
イヤホンが抜かれた時は、userInfo
から AVAudioSessionRouteDescription
というオブジェクトを抜き出し、
その中から先ほど同様にAVAudioSessionPortDescription
を取得することができます。
switch interruptionType {
case .began:
// 割愛
case .ended:
var shouldResume = false
if let interruptionOptionRawValue = userInfo[AVAudioSessionInterruptionOptionKey] as? UInt {
let interruptionOptions = AVAudioSession.InterruptionOptions(rawValue: interruptionOptionRawValue)
shouldResume = interruptionOptions.contains(.shouldResume)
}
if shouldResume {
player.unpause()
}
@unknown default:
fatalError()
}
「音声再開の必要があるかどうか」(shouldResume
)という情報の有無を確認するのですが、
InterruptionOptions
はOptionSet
なので、contains
メソッドで調べてあげる必要があります。
こうすることによって、他のアプリで中断されたあとも自動的に音声が再生されることになります。
おわりに
AVFoundation
を使用した音声を扱うアプリであれば、音声の中断とその後に関するユーザのケアも必要になってくると思いますので、
ぜひご参考になさってください。
それではまたー