はじめに
これはiOSでサーバーからダウンロードした音声ファイルをロック画面で再生するまでの設定を行った際の備忘録です。
当初はiOS9.2のシュミレーターを利用して検証していましたが、コントロールセンターでボタンが表示されない等の現象が見られたので実機を想定しています。
環境
- Xcode7.2
- iOS9系 実機
やったこと
Background Audio&AirPlayのチェックボックスをONに
Targets名 => Capabilities => Background Modes => Audio&AirPlay
音声ファイルのパーミッションの設定
今回はAVAudioPlayerを利用して楽曲再生を行いました。
ダウンロードしてストレージに保存した音声ファイルをそのままロック画面で再生しようとすると、パーミッションの関係でAVAudioPlayer play error and the error is Code=-54
エラーが発生します。
NSFileManager.defaultManager.setAttributes
で保存先のパーミッションをNSFileProtectionNone
に変更し上記のエラーに対応しました。
// 保存先ディレクトリパス取得
func exportDirectoryPath() -> String {
guard let path = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first else {
fatalError()
}
return path + "/" + "Clips"
}
...省略
// 保存先ディレクトリを作成
let dirPath = DownloadFilePathAgent.exportDirectoryPath() + "/\(self.task.uuid)"
let fileManager:NSFileManager = NSFileManager.defaultManager()
do {
if fileManager.fileExistsAtPath(dirPath) {
try fileManager.removeItemAtPath(dirPath)
}
try fileManager.createDirectoryAtPath(dirPath, withIntermediateDirectories: true, attributes: nil)
// ロック時にも再生できるようにパーミッション変更
try fileManager.setAttributes([NSFileProtectionKey: NSFileProtectionNone], ofItemAtPath: dirPath)
}
catch let error as NSError {
//エラー対応
}
...省略
// ファイルダウンロード完了時にもパーミッション変更
try! NSFileManager.defaultManager().setAttributes([NSFileProtectionKey: NSFileProtectionNone], ofItemAtPath: filePath)
AVAudioPlayerとAVAudioSessionの設定
以下を再生や停止を担当するクラスに実装していきます。
// initあたりで
UIApplication.sharedApplication().beginReceivingRemoteControlEvents()
// AVAudioPlayer#play()実行前に呼び出して即座に再生開始するように
audioPlayer?.prepareToPlay()
ロック画面でも再生しますよー、の設定です。
プロジェクト設定からBackground Audio&AirPlayをONにします。
Project Navigator -> Project -> target -> Capabilities -> Background Modes
AVAudioSession の設定
session = AVAudioSession.sharedInstance()
//ロック時も再生のカテゴリを指定
do {
try session?.setCategory(AVAudioSessionCategoryPlayback)
}
catch let error as NSError {
print(error.description)
}
do {
//オーディオセッションを有効化
try session?.setActive(true)
}
catch let error as NSError {
print(error.description)
}
再生ボタン等の表示設定
iOS 7.1以降はremoteControlReceivedWithEvent
をオーバライドしてロック画面のハンドリングに対応する方法でなくMPRemoteCommandCenter
を利用するように推奨されています。
MPRemoteCommand Class Reference
上記のAVAudioSessionを実装したクラスに追記していきます。
表示するボタンと対応する処理の設定
let commandCenter = MPRemoteCommandCenter.sharedCommandCenter()
commandCenter.playCommand.addTarget(self, action: "play")
commandCenter.playCommand.enabled = true
commandCenter.pauseCommand.addTarget(self, action: "pause")
commandCenter.pauseCommand.enabled = true
commandCenter.skipForwardCommand.addTarget(self, action: "skipForward")
commandCenter.skipForwardCommand.enabled = true
commandCenter.skipBackwardCommand.addTarget(self, action: "skipBackward")
commandCenter.skipBackwardCommand.enabled = true
仕上がり
再生している楽曲情報の設定
MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo = [
MPMediaItemPropertyTitle: "タイトル",
MPMediaItemPropertyArtist : "アーティスト名",
MPNowPlayingInfoPropertyPlaybackRate : NSNumber(float: 1.0), //再生レート
MPMediaItemPropertyPlaybackDuration : NSNumber(double: self.duration) //シークバー
]
ついでに再生中の電話割り込み/復帰対応
必須機能かと思いますのでついでに実装しておきましょう。
AVAudioPlayerDelegateに準拠しているなら以下の2つのデリゲートメソッドで割り込みに対応できます。
// 割り込み開始時にプレーヤーを制御するデリゲートメソッド
func audioPlayerBeginInterruption(player: AVAudioPlayer) {
// プレーヤー/オーディオセッションはすでに一時停止状態
}
// 割り込み終了時にプレーヤーを制御するデリゲートメソッド
func audioPlayerEndInterruption(player: AVAudioPlayer) {
// 復帰処理
}
上記以外にAVAudioSessionInterruptionNotification
を利用する方法がありました。
// 通知登録
NSNotificationCenter.defaultCenter().addObserver(self, selector: "sessionDidInterrupt", name: AVAudioSessionInterruptionNotification, object: nil)
/**
電話が鳴って割り込まれた / 電話終わった 際のハンドリング
*/
func sessionDidInterrupt(notification:NSNotification) {
guard let typeNumber = notification.userInfo?[AVAudioSessionInterruptionTypeKey],
let type = AVAudioSessionInterruptionType(rawValue: typeNumber.unsignedLongValue)
else {
return
}
switch type {
case .Began:
dispatch_async(dispatch_get_main_queue(), { [weak self] in
self?.pause()
})
case .Ended:
dispatch_async(dispatch_get_main_queue(), { [weak self] in
self?.play()
})
}
}
備考
デフォルトの「ミュージック」のアプリはロック画面のシークバーの把手で再生をコントロールできますがリファレンス等を確認してもその機能は提供されていない模様です。
MixCloudやSoundCloud等のアプリにもなかったので多分そうなのでしょう。