iOS10.1でApple MusicのためのAPIが追加されたので試す


iOS9.3からApple Musicの曲の再生とプレイリストの作成ができるようになりましたが、下記のように何かと不便なことが多いと感じていました

  1. 再生する曲の順番が指定しにくい
  2. ローカルにDLしていないアートワーク(MPMediaItemArtwork)が表示されない
  3. 再生中の曲のStore ID(Apple Musicを再生するための曲ごとのID)が拾えない
  4. プレイリストを取得した後にすぐ曲の追加ができない(前にそのことについて発表したスライド)
  5. プレイリストの削除や変更ができない

今回のアップデートでは 1. が解決されました👏 iOS10.1 APIDiffs - MediaPlayer

  • Xcode Version 8.1 beta
  • iOS Version 10.1 beta

MPMusicPlayerController.setQueueWith(_: MPMusicPlayerQueueDescriptor)

今まで使っていたのは下記のメソッドで、ただ単にMPMusicPlayerControllerにApple Music用のStore IDの配列を渡すだけものでした

player.setQueueWithStoreIDs(storeIds)

今までのメソッドでは何が問題かというと、複数の曲を最初から再生するには問題がないのですが、2曲目とか3曲目などの途中から再生する場合は自前で実装するしかありませんでした

ちなみに私のアプリではこのような酷い実装をしていました😌(スライシングでもできます)

// collectionView(_:didSelectItemAt:)
let selectedStoreIds = trackIds.enumerated().filter { $0.offset >=  indexPath.row }.map { $0.element } 
    + trackIds.enumerated().filter { $0.offset < indexPath.row }.map { $0.element }

player.repeatMode = .all
player.setQueueWithStoreIDs(selectedStoreIds)

何をやっているかというと、タップした曲から前の曲を後ろにつなげて再生モードをリピートにすることで選択した曲の前の曲も戻るボタンなどで聴けるようにしていた感じです…

しかし今回のメソッドではMPMusicPlayerQueueDescriptorという再生する曲を管理するクラスが渡せるようになりました👏

上記のクラスは直接使用せずにサブクラスのMPMusicPlayerMediaItemQueueDescriptor(ローカル用)かMPMusicPlayerStoreQueueDescriptor(Apple Music用)を使います

MPMusicPlayerStoreQueueDescriptor

使い方は簡単で初期化をStore IDの配列で行い、その後に再生したいStore IDを指定するだけです

// collectionView(_:didSelectItemAt:)
let descriptor = MPMusicPlayerStoreQueueDescriptor(storeIDs: trackIds)
descriptor.startItemID = trackIds[indexPath.row]
player.setQueueWith(descriptor)

他にも再生や終了位置も指定できるみたいです(どんな時に使うのかわからないですが…)

descriptor.setStartTime(10.0, forItemWithStoreID: storeId)
descriptor.setEndTime(30.0, forItemWithStoreID: storeId)

MPMusicPlayerController.prepareToPlay(completionHandler: (Error?) -> Swift.Void)

次は再生が可能になるまで曲データの準備(ダウンロード)を行ってくれるメソッドです

今までのprepareToPlay()はもともと(たぶん)ローカルの曲の再生準備をするために用意されたメソッドで、そこまで時間のかかる処理ではないからかそのまま使うと同期処理で行われてしまいます

しかしApple Musicはオンライン再生なのでDLに時間がかかり、その前にnowPlayingItemのアートワークを取り出したりすると表示されなかったり、なぜ再生されないのか原因が分からなかったりとっても不便でしたが、今回から非同期かつエラーもハンドリングできるようになりました👏

playerViewController.player.prepareToPlay(completionHandler: { error in
    if error == nil {
        player.play()
    } else {
        print(error?.localizedDescription)
    }
})

MPError

ちゃんとメディアプレイヤー用のエラーが定義されているようです

struct MPError {
    init(_nsError _nsError: NSError)
    static var _nsErrorDomain: String { get }
    enum Code : Int {
        typealias _ErrorType = MPError
        case unknown
        case permissionDenied
        case cloudServiceCapabilityMissing
        case networkConnectionFailed
        case notFound
        case notSupported
    }
    static var unknown: MPError.Code { get }
    static var permissionDenied: MPError.Code { get }
    static var cloudServiceCapabilityMissing: MPError.Code { get }
    static var networkConnectionFailed: MPError.Code { get }
    static var notFound: MPError.Code { get }
    static var notSupported: MPError.Code { get }
}

ということで、まだまだ課題はたくさんありますがみなさんでApple Musicを使った音楽アプリをどんどん広めていきましょう👋😎