iOS11で発表されたMusicKitについて

※ まだ正式リリースされていない情報なので変更の可能性があります⚠️

個人的には一番嬉しいアップデートがこのMusicKitでした😎🎸今までiTunes APIの中からストリーミング可能なものを抜き出して使っていたのがApple Music APIとして切り出されました。それだけでなくユーザーのおすすめカタログなどパーソナライズデータにもアクセスできるようになりました👏

今回の手順やリファレンスは下記のリンクにまとまっているので目を通すといいと思います👀
https://developer.apple.com/musickit/

# What's MusicKit

MusicKitは下記のAPIとフレームワークで構成されている。単体のフレームワークではないのでAPIのみであればWebでも使える👏

* **Apple Music API**: Apple Musicの曲やプレイリストを取得する
* **StoreKit(Framework)**: Apple Musicの使用許可やトークンの取得を行う
* **MediaPlayer(Framework)**: Apple Musicの曲やプレイリストを再生する

# Apple Music API

## Keys and Tokens
Apple Music APIを使用するにはPrivate Keyの取得とDeveloper Token(JWT)の生成が必要
面倒ですがパーソナライズされたデータにアクセスするのでしょうがないです😭

### Music Identifier

下記ページにアクセスしてMusic Identifierを登録する
https://developer.apple.com/account/ios/identifier/musicId

![スクリーンショット 2017-06-07 12.52.43.png](https://qiita-image-store.s3.amazonaws.com/0/83361/d09a4321-876d-824d-d445-422eac066c0a.png)

### MusicKit Private Key

下記ページにアクセスしてMusicKitのプライベートキーを作成する
https://developer.apple.com/account/ios/authkey

![スクリーンショット_2017-06-07_12_55_34.png](https://qiita-image-store.s3.amazonaws.com/0/83361/db42f616-860a-fade-6372-e17958ae75f1.png)

### Developer Token

Developer Token(*Json Web Token*)を作成する

* アルゴリズムは*ES256*を使用する
* カスタムヘッダーに上記で作成したプライベートキーのKey IDを入れる
* *Issuer*にはチームIDを入れる
* *Issued At*にはこのJWTの発行時間を*Unix Time*で入れる
* *Expiration Time*には6ヶ月以内の有効期限を*Unix Time*で入れる

```json

{
  "alg": "ES256",
  "kid": "<MusicKit Key ID>"
}
{
  "iss": "<Team Identifier>",
  "iat": 1437179036,
  "exp": 1493298100
}
```

ライブラリを使ってエンコードしたいが、Swift4に対応しているものがない😭

![スクリーンショット 2017-06-13 7.26.31.png](https://qiita-image-store.s3.amazonaws.com/0/83361/febaaa54-99ac-ba71-a9b7-30c4f58b0d5e.png)

## Storefronts and Localizations

* [`storefronts`](https://developer.apple.com/library/content/documentation/NetworkingInternetWeb/Conceptual/AppleMusicWebServicesReference/Storefront.html): 国や地域ごとに配信コンテンツが異なり、それを変更できる
* `langage_code`: 指定した言語に翻訳される。指定しない場合は`storefronts`のデフォルト言語になる

```
GET https://api.music.apple.com/v1/catalog/{storefronts}/albums/310730204?l={langage_code}
```

### Storefronts

Storefrontsのデータは下記のリクエストで取得できる

* `id`: ISO-Alpha2 Country Code

```
GET https://api.music.apple.com/v1/storefronts/{id}
```

## Request

全てのリクエストのヘッダーに`Authorization`をキーにした上記のDeveloper Tokenを渡す(頭に`Bearer `をつける)

```
curl -v -H 'Authorization: Bearer [developer token]' "https://api.music.apple.com/v1/catalog/us/songs/203709340"
```

## Reference

**Apple Music API Reference**にまとまっている🙆(iTunes Search APIのよりは見やすい)

https://developer.apple.com/library/content/documentation/NetworkingInternetWeb/Conceptual/AppleMusicWebServicesReference/

* Storefrontsの取得
* アルバム/曲/プレイリスト/ミュージックビデオ/Stationsの取得
* アーティスト/Curators/Activities/AppleCuratorsの取得
* チャートの取得
* ジャンルの取得
* Catalogの検索
* ヘビーローテーションの取得※
* 最近聞いた曲/Stationsの取得※
* 評価の管理※
* おすすめの取得※

※ ユーザーのApple Musicコンテンツを取得するにはStoreKitで**Music User Token**を取得する必要がある

# StoreKit

## Accessing the Music Library

* `authorizationStatus()`: ユーザが選択したMusic Library(Apple Music含む)利用許可の状態を確認する
* `requestAuthorization(_:)`: ユーザにMusic Library(Apple Music含む)の利用許可を求める

```swift
// SKCloudServiceController
class func authorizationStatus() -> SKCloudServiceAuthorizationStatus
```

```swift
// SKCloudServiceController
class func requestAuthorization(_ handler: @escaping (SKCloudServiceAuthorizationStatus) -> Void)
```

※ Music Libraryへのアクセスには`Info.plist`に`NSAppleMusicUsageDescription`の記述が必要です⚠️

### SKCloudServiceAuthorizationStatus

* `notDetermined`: まだ利用許可に応答したことがない
* `denied`: 利用を拒否した
* `restricted`: 教育モードなどで利用許可すらできない
* `authorized`: 利用を許可した

## Capabilities

Music Libraryで利用できる機能を確認できる(Apple Musicのメンバーシップがあるか確認できる)

```swift
func requestCapabilities(completionHandler: @escaping (SKCloudServiceCapability, Error?) -> Void)
```

### SKCloudServiceCapability

* `musicCatalogPlayback`: Apple Musicの曲を再生できる
* `musicCatalogSubscriptionEligible`: Apple Musicの曲を取得できる
* `addToCloudMusicLibrary`: 曲をMusic Libraryに追加できる

## Storefronts for Device

デバイスに設定されたリージョンからStorefrontsを取得できる

```swift
func requestStorefrontCountryCode(completionHandler: @escaping (String?, Error?) -> Void)
```

今までは`requestStorefrontIdentifier(completionHandler:)`から自力で[変換](https://affiliate.itunes.apple.com/resources/documentation/linking-to-the-itunes-music-store/#appendix)するしかなくて面倒だった😭

## Music User Token

ユーザーのApple Musicコンテンツを取得する為のトークンを取得できる

```swift
func requestUserToken(forDeveloperToken developerToken: String, completionHandler: @escaping (String?, Error?) -> Void)
```

下記のような`/me`パスで取得できる最近再生した曲やレコメンドなどに使う

```
GET https://api.music.apple.com/v1/me/recent/played
GET https://api.music.apple.com/v1/me/recommendations
GET https://api.music.apple.com/v1/me/ratings/albums/{id}
GET https://api.music.apple.com/v1/me/recommendations
...
```

# MediaPlayer

## Play Apple Music Catalog

Apple Musicの曲やアルバムの再生、プレイリストの作成を行う🎧

### MPMusicPlayerController

* `setQueue(with:storeIDs)`: Apple Music APIで取得したStore Identifier(曲やアルバムごとのID)から再生できる
* `setQueue(with:descriptor)`: キューを指定しながら曲の再生ができる([以前Qiitaに書いた](http://qiita.com/hanawat/items/c900cefcefdadb9b7283)のでどうぞ👏)

```swift
func setQueue(with storeIDs: [String])
```

```swift
func setQueue(with descriptor: MPMusicPlayerQueueDescriptor)
```

ex.) 下記リクエストの場合だとアルバムの`id`を入れるだけで再生ができる
`https://api.music.apple.com/v1/catalog/us/albums/310730204`

```json
{
  "data": [
    {
      "href": "/v1/catalog/us/albums/310730204",
      "id": "310730204",
      "artists": {...},
      "tracks": {...},
      ...
```

### MPMusicPlayerPlayParametersQueueDescriptor

今回iOS11で追加された`MPMusicPlayerQueueDescriptor`の継承クラス。PlayParameterQueueとは🤔
セッションを見た感じApple Music APIのクエリをそのままキューとして使うことができる？

https://developer.apple.com/documentation/mediaplayer/mpmusicplayerplayparametersqueuedescriptor

```swift
init(playParametersQueue: [[String : Any]])
```

### MPMediaLibrary

`UUID`と`MPMediaPlaylistCreationMetadata`で指定した既存または新規でプレイリストを取得する

```swift
// MPMediaLibrary
func getPlaylist(with uuid: UUID, creationMetadata: MPMediaPlaylistCreationMetadata?, completionHandler: @escaping (MPMediaPlaylist?, Error?) -> Void)
```

### MPMediaPlaylist

プレイリストに曲やアルバムを追加する(一曲ずつしか追加できない😢)

```swift
// MPMediaPlaylist
func addItem(withProductID productID: String, completionHandler: ((Error?) -> Void)? = nil)
```

※ `getPlaylist:`で取得してスグにプレイリストを使おうとするとエラーになる時がある。[解決策](https://forums.developer.apple.com/thread/45996)は今の所5秒待つことらしい…😟

---

iOS9の時から少しずつアップデートを繰り返してきたサードパーティへのApple Musicの機能提供ですが、今回のアップデートはかなり大きなものになりました👏これを機会に素晴らしい音楽アプリが増えればいいなと思います🙏

おしまい👋