Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

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

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

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

import AVFoundation
import MediaPlayer

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

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

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

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) に対して,その中の全シングル曲を取得します.

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 に変更する場合は以下のようにするとよいです.

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

ミュージック検索

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

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

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 のインスタンス変数を用意します.

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

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

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 行を追加.

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

ロック画面,コントロールセンターでの操作

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

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

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 つの設定をしています.

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 から以下のように,とても簡単に記述できるようになりました.

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

AudioManager.sharedManager.audioPlayer.play()  // 再生
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした