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

iOS9.3から[Apple Musicの曲の再生とプレイリストの作成](http://dev.classmethod.jp/smartphone/iphone/apple-music-ios-9-3/)ができるようになりましたが、下記のように何かと不便なことが多いと感じていました

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

今回のアップデートでは **1.** が解決されました👏 [iOS10.1 APIDiffs - MediaPlayer](https://developer.apple.com/library/prerelease/content/releasenotes/General/iOS101APIDiffs/Swift/MediaPlayer.html)

* Xcode Version 8.1 beta
* iOS Version 10.1 beta

# MPMusicPlayerController.setQueueWith(_: MPMusicPlayerQueueDescriptor)

https://developer.apple.com/reference/mediaplayer/mpmusicplayercontroller/2582423-setqueuewith

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

```swift
player.setQueueWithStoreIDs(storeIds)
```

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

![qiita.001.jpeg](https://qiita-image-store.s3.amazonaws.com/0/83361/6ec1a928-24c1-3ace-2bcb-eff5e03a9b47.jpeg)


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

```swift
// 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)
```

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

![qiita.002.jpeg](https://qiita-image-store.s3.amazonaws.com/0/83361/327d06d6-73ae-b3ba-f4b6-bdc368b1e37d.jpeg)


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

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

## MPMusicPlayerStoreQueueDescriptor

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

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

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

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

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

https://developer.apple.com/reference/mediaplayer/mpmusicplayercontroller/2582424-preparetoplay

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

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

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

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

## MPError

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

```swift
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を使った音楽アプリをどんどん広めていきましょう👋😎