Swift でミュージックを取り込むアプリを作る時のノウハウ 5 つ


AWA や LINE Music，Apple Music と，続々と音楽ストリーミング配信サービスが開始されましたね．しかし，データ通信料や金額などのハードルの高さにより，ユーザが iPhone 内にミュージックをダウンロードしなくなる日はまだ遠いように思います．

ということで今回は，iPhone 内のミュージックを取り込んで再生するようなアプリを作る際のノウハウをまとめてみました．ぜひチェックしてみて下さい．

Swift 1.2，Xcode 6.4 で動作確認しています．また，以下の 2 つの framework が import されている前提です．

```swift
import AVFoundation
import MediaPlayer
```

## ミュージック情報の取り方

### アルバム情報 (アルバム名，アーティスト名)

まず，iPhone 内の全アルバム情報を取得してみます．

```swift
let albumsQuery = MPMediaQuery.albumsQuery()
let albums = albumsQuery.collections as! [MPMediaItemCollection]
        
for album in albums {
    let title = album.representativeItem.albumTitle ?? ""  // アルバム名
    let artist = album.representativeItem.albumArtist ?? ""  // アーティスト名
}
```

### シングル情報 (シングル名，アーティスト名，ジャケット)

今度は，各 album (MPMediaItemCollection) に対して，その中の全シングル曲を取得します．

```swift
for single in (album.items as! [MPMediaItem]) {
    let id = single.valueForProperty(MPMediaItemPropertyPersistentID).stringValue  // 一意の id
    let title = single.valueForProperty(MPMediaItemPropertyTitle) as? String  // シングル名
    let artist = single.valueForKey(MPMediaItemPropertyArtist) as? String  // アーティスト名
    let artwork = single.valueForProperty(MPMediaItemPropertyArtwork) as? MPMediaItemArtwork  // ジャケット
}
```

ジャケットに関しては，MPMediaItemArtwork で取得されるので，UIImage に変更する場合は以下のようにするとよいです．

```swift
if let artwork = artwork, artworkImage = artwork.imageWithSize(artwork.bounds.size) {
    // artworkImage に UIImage として保持されている
}
```

## ミュージック検索

MPMediaPropertyPredicate に検索したい key と value を指定し，それを addFilterPredicate にかけてあげることで，検索することができます． 

以下では，id (MPMediaItemPropertyPersistentID) が 8285939641838085690 であるミュージックを取得してみます．

```swift
let property = MPMediaPropertyPredicate(value: 8285939641838085690, forProperty: MPMediaItemPropertyPersistentID)
let query = MPMediaQuery()
query.addFilterPredicate(property)
let singles = query.items as! [MPMediaItem]
```

検索に引っかかったミュージックが singles に格納されています．ちなみに上記のように id で検索した時は，一意なので最大 1 件しか返ってきません．

## ミュージック再生

再生には AVAudioPlayer を用います．まず，AVAudioPlayerDelegate を宣言し，AVAudioPlayer のインスタンス変数を用意します．

```swift
class AudioManager: NSObject, AVAudioPlayerDelegate {
    var audioPlayer: AVAudioPlayer!
    ...
}
```

そして以下のように，取得したシングルを再生します．

```swift
if let url = single.valueForProperty(MPMediaItemPropertyAssetURL) as? NSURL {
    audioPlayer = AVAudioPlayer(contentsOfURL: url, error: nil)
    audioPlayer.delegate = self
    audioPlayer.play()
}
```

if let 必須です．url ないミュージック (MPMediaItem) とか存在しないのでは，と思うかもしれませんが，__iCloud__ __上のミュージックは__ __nil__ __が返ってきます．__ すなわち，現時点では iCloud 上のミュージックをアプリ内で再生することはできません．

### バックグラウンド再生

上記の方法では，アプリがバックグラウンドに移行した時に再生が停止してしまいます．バックグラウンドでも再生し続けたい場合は，以下の手順をおこなうことで可能になります．

1．Info.plist のkey に Required background modes を追加し，Item 0 の value に App plays audio or streams audio/video using AirPlay を指定．

2．audioPlayer.play() の前に以下の 2 行を追加．

```swift
AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord, error: nil)
AVAudioSession.sharedInstance().setActive(true, error: nil)
```

## ロック画面，コントロールセンターでの操作
![](https://qiita-image-store.s3.amazonaws.com/0/7113/160d7a6e-f77a-64b5-5285-c053c33ab41d.png "3.png")

バックグラウンドで再生する時は，ロック画面やコントロールセンターで停止や再生などの操作を受け付けたいですね．以下の手順でこれらの操作を受け付けられます．

1．以下の 1 行を viewDidLoad などに追加し，操作を受け付ける状態にします．

```swift
UIApplication.sharedApplication().beginReceivingRemoteControlEvents()
```

2．それぞれのボタン押下時の処理を記述します．

```
override func remoteControlReceivedWithEvent(event: UIEvent) {
    switch event.subtype {
        case .RemoteControlPlay:  // 再生ボタン
            audioPlayer.play()
        case .RemoteControlPause:  // 停止ボタン
            audioPlayer.pause()
        case .RemoteControlNextTrack:  // 次へボタン
            // ▶▶ 押下時の処理
        case .RemoteControlPreviousTrack:  // 前へボタン
            // ◀◀ 押下時の処理
        default:
            break
    }
}
```

3．最後に，シングル名やアーティスト名などのコンポーネントを追加します．2 の手順までだと，ロック画面で再生停止した際にコントロール画面が (なぜか) 非表示になるので，特に必要ない場合も追加しましょう．

上のスクリーンショットでは，以下の 6 つの設定をしています．

```swift
let defaultCenter = MPNowPlayingInfoCenter.defaultCenter()

defaultCenter.nowPlayingInfo[MPMediaItemPropertyTitle] = title  // シングル名
defaultCenter.nowPlayingInfo[MPMediaItemPropertyArtist] = artist  // アーティスト名
defaultCenter.nowPlayingInfo[MPMediaItemPropertyArtwork] = artwork  // ジャケット (MPMediaItemArtwork)
defaultCenter.nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = 1.0
defaultCenter.nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = audioPlayer.duration  // ミュージックの長さ
defaultCenter.nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = audioPlayer.currentTime  // ミュージックの再生時点
```

## シングルトン実装

アプリ全体を通してミュージックを利用する場合，シングルトンにすることが多いと思いますが，1.2 から以下のように，とても簡単に記述できるようになりました．

```swift
class AudioManager: NSObject, AVAudioPlayerDelegate {
    static let sharedManager = AudioManager()  // singleton
    var audioPlayer: AVAudioPlayer!
    ...
}

AudioManager.sharedManager.audioPlayer.play()  // 再生
```