SwiftでMusicKitを使いApple Music APIを叩く【UIKit】

# 概要

SwiftでMusicKitを利用してApple Music APIを叩く。

# Developer Token発行のための準備

公式ドキュメント

https://developer.apple.com/documentation/applemusicapi/generating_developer_tokens

> To make requests to the Apple Music API, you need to authorize yourself as a trusted developer and member of the Apple Developer Program. The header of every Apple Music API request requires a signed developer token.

Apple Music APIを使用するためにはDeveloper Tokenが必要であると書かれているため、先ずはDeveloper Tokenを発行する。（※Tokenの発行には99$/年のApple Developer Programに加入しているアカウントが必要）

### Media IDの登録

以下のURLからApple Developer Programに加入しているアカウントでサインインし、サイドバーの`Identifiers`をクリックする。

https://developer.apple.com/account/resources

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2671591/bbf838ac-69f8-b6d5-8046-e369e65d6f25.png)

太字の`Identifiers`の右横にあるプラスボタンをクリックしたのち、下の方にある`Media IDs`を選択。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2671591/93239181-edf4-dac8-9385-1fcc8bb279fa.png)

画面上部にある`Continue`ボタンをクリックして次に進み、
- `Description`には概要（アプリの名前）
- `Identifier`には"media."+逆ドメイン（APIを叩きたいXcodeプロジェクトのBundle Identifier）

を入力する。

画面の下部の`MusicKit`にチェックを入れると画面右上の`Continue`ボタンが青く押せるようになるので押して次に進む。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2671591/6ba72e13-25b8-aefc-ba48-6ebe2e7e788e.png)

確認画面になるので問題なければ画面右上の`Register`を押す。これでIdentifierの登録は完了。

https://developer.apple.com/documentation/applemusicapi/generating_developer_tokens

> If you’re developing an app for Apple platforms (iOS, tvOS, watchOS or macOS), the recommended way to integrate with Apple Music is to use MusicKit for Swift, following the steps provided in Using Automatic Developer Token Generation for Apple Music API.

今回はAppleのプラットフォーム（iOS）上でAPI通信を行うため、以上の手順のみで認証を通過することができる。

# MusicKit使用のための準備

プロジェクトのinfo.plistにApple Musicへのアクセスを許可するための設定を追加する。Keyは`Privacy - Media Library Usage Description`で、Valueは任意の文言。

![Screenshot 2023-07-26 at 6.41.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2671591/96981d45-539a-fd0d-3c77-141a777e4d1f.png)

次に、アプリの起動時にApple Musicへのアクセスをリクエストするために、`AppDelegate`を以下のコードを追加する。

```diff_swift:AppDelegate
import UIKit
+ import MusicKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
+    Task {
+      await MusicAuthorization.request()
+    }
    return true
  }
```
  
これによって、アプリ起動時にApple Musicへのアクセスを許可するかどうかの確認が出る。許可を押すと、これ以降のAPI通信を行うことができる。

# Apple Music APIとの通信

https://developer.apple.com/documentation/MusicKit

MusikKitにはAPI通信を簡単にするためのメソッドが用意されている。

### MusicCatalogSearchRequest()

#### ワードで検索

一番普通のリクエスト。"Bump of Chicken"という検索ワードに引っ掛かる曲をレスポンスとして返す。

```diff_swift:ViewController
class ViewController: UIViewController {
  override func viewDidLoad() {
    super.viewDidLoad()
+    Task {
+      do {
+        let request = MusicCatalogSearchRequest(term: "Bump of Chicken", types: [Song.self])
+        let response = try await request.response()
+        print(response.songs)
+      } catch {
+        print("error")
+      }
+    }
  }
}
```
出力結果
```
MusicItemCollection<Song>(
  items: [
    Song(id: "1644656901", title: "Souvenir", artistName: "BUMP OF CHICKEN"),
    Song(id: "1532379727", title: "Acacia", artistName: "BUMP OF CHICKEN"),
    Song(id: "1464634457", title: "Tentaikansoku", artistName: "BUMP OF CHICKEN"),
    Song(id: "1678512925", title: "Madononakakara", artistName: "BUMP OF CHICKEN"),
    Song(id: "1564243529", title: "Nanairo", artistName: "BUMP OF CHICKEN")
  ],
  hasNextBatch: true
)
```
#### 出力件数を指定
```swift
var request = MusicCatalogSearchRequest(term: "Bump of Chicken", types: [Song.self])
request.limit = 10
```
出力結果
```
MusicItemCollection<Song>(
  items: [
    Song(id: "1644656901", title: "Souvenir", artistName: "BUMP OF CHICKEN"),
    Song(id: "1532379727", title: "Acacia", artistName: "BUMP OF CHICKEN"),
    Song(id: "1464634457", title: "Tentaikansoku", artistName: "BUMP OF CHICKEN"),
    Song(id: "1678512925", title: "Madononakakara", artistName: "BUMP OF CHICKEN"),
    Song(id: "1564243529", title: "Nanairo", artistName: "BUMP OF CHICKEN"),
    Song(id: "1464635122", title: "Ray", artistName: "BUMP OF CHICKEN"),
    Song(id: "1617255367", title: "Chronostasis", artistName: "BUMP OF CHICKEN"),
    Song(id: "1464635751", title: "Karuma", artistName: "BUMP OF CHICKEN"),
    Song(id: "1610683351", title: "Shinsekai", artistName: "BUMP OF CHICKEN"),
    Song(id: "1464634874", title: "Sharinnouta", artistName: "BUMP OF CHICKEN")
  ],
  hasNextBatch: true
)
```
#### 曲一件一件を取得する
```swift
let response = try await request.response()
let songs = response.songs
songs.forEach({ song in
  print(song)
})
```
出力結果
```
Song(id: "1644656901", title: "Souvenir", artistName: "BUMP OF CHICKEN")
Song(id: "1532379727", title: "Acacia", artistName: "BUMP OF CHICKEN")
Song(id: "1464634457", title: "Tentaikansoku", artistName: "BUMP OF CHICKEN")
Song(id: "1678512925", title: "Madononakakara", artistName: "BUMP OF CHICKEN")
Song(id: "1564243529", title: "Nanairo", artistName: "BUMP OF CHICKEN")
```
#### 曲の長さを取得する
```swift
songs.forEach({ song in
  print(song.duration!)
})
```
出力結果
```
264.127
262.107
263.6
399.418
259.878
```