1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[Swift] iTunes APIを叩いて音楽視聴アプリを作ってみた

Last updated at Posted at 2021-09-26

#iTunesAPI
iTunesAPIを利用することでiTunes内の本、映画、ポッドキャスト、音楽、ミュージックビデオ、オーディオブック、テレビ番組などの情報を取得できます。今回はその中でも音楽の視聴音源を用いて音楽視聴アプリを作ってみました。

iTunesAPIのドキュメントに関して

##リクエスト例
例としてoneokrockの情報を取得してみます。
リクエストURLは以下のようになります。

https://itunes.apple.com/search?term=\oneokrock&entity=song&contry=jp

##レスポンス
上記のURLを入力すると以下のような.txtファイルがダウンロードされる
(少々長いのでresultCount2つまでを表示)
JSON Pretty Linterを用いて整形しました。

{
resultCount:50
results:[
0:{
wrapperType:track
kind:song
artistId:252239625
collectionId:605688804
trackId:605689011
artistName:ONE OK ROCK
collectionName:Jinsei × Boku =
trackName:Clock Strikes
collectionCensoredName:Jinsei × Boku =
trackCensoredName:Clock Strikes
artistViewUrl:https://music.apple.com/us/artist/one-ok-rock/252239625?uo=4
collectionViewUrl:https://music.apple.com/us/album/clock-strikes/605688804?i=605689011&uo=4
trackViewUrl:https://music.apple.com/us/album/clock-strikes/605688804?i=605689011&uo=4
previewUrl:https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview125/v4/af/6f/df/af6fdfd5-a32c-e794-ed20-e2e20c043981/mzaf_14716866606323246628.plus.aac.p.m4a
artworkUrl30:https://is4-ssl.mzstatic.com/image/thumb/Music125/v4/db/43/0a/db430aad-9319-2f7b-005c-8646e7f388a8/source/30x30bb.jpg
artworkUrl60:https://is4-ssl.mzstatic.com/image/thumb/Music125/v4/db/43/0a/db430aad-9319-2f7b-005c-8646e7f388a8/source/60x60bb.jpg
artworkUrl100:https://is4-ssl.mzstatic.com/image/thumb/Music125/v4/db/43/0a/db430aad-9319-2f7b-005c-8646e7f388a8/source/100x100bb.jpg
collectionPrice:13.99
trackPrice:1.29
releaseDate:2013-03-06T12:00:00Z
collectionExplicitness:notExplicit
trackExplicitness:notExplicit
discCount:1
discNumber:1
trackCount:13
trackNumber:5
trackTimeMillis:235347
country:USA
currency:USD
primaryGenreName:Rock
isStreamable:true
}
1:{
wrapperType:track
kind:song
artistId:252239625
collectionId:1176157333
trackId:1176157730
artistName:ONE OK ROCK
collectionName:Ambitions
trackName:Take What You Want (feat. 5 Seconds of Summer)
collectionCensoredName:Ambitions
trackCensoredName:Take What You Want (feat. 5 Seconds of Summer)
artistViewUrl:https://music.apple.com/us/artist/one-ok-rock/252239625?uo=4
collectionViewUrl:https://music.apple.com/us/album/take-what-you-want-feat-5-seconds-of-summer/1176157333?i=1176157730&uo=4
trackViewUrl:https://music.apple.com/us/album/take-what-you-want-feat-5-seconds-of-summer/1176157333?i=1176157730&uo=4
previewUrl:https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview115/v4/4e/cc/30/4ecc300e-a281-2d0c-1fb6-72886e3f9bc5/mzaf_6734311677989074101.plus.aac.p.m4a
artworkUrl30:https://is3-ssl.mzstatic.com/image/thumb/Music125/v4/ca/fc/a8/cafca8f8-f563-ab15-5873-974092810637/source/30x30bb.jpg
artworkUrl60:https://is3-ssl.mzstatic.com/image/thumb/Music125/v4/ca/fc/a8/cafca8f8-f563-ab15-5873-974092810637/source/60x60bb.jpg
artworkUrl100:https://is3-ssl.mzstatic.com/image/thumb/Music125/v4/ca/fc/a8/cafca8f8-f563-ab15-5873-974092810637/source/100x100bb.jpg
collectionPrice:9.99
trackPrice:1.29
releaseDate:2017-01-11T12:00:00Z
collectionExplicitness:notExplicit
trackExplicitness:notExplicit
discCount:1
discNumber:1
trackCount:14
trackNumber:14
trackTimeMillis:243274
country:USA
currency:USD
primaryGenreName:Rock
isStreamable:true
}
//以下省略

##今回アプリで用いる情報とライブラリ
###artistName
アーティスト名

###trackCensoredName
曲名

###previewUrl
視聴音源URL

###artworkUrl100
アルバムジャケット

###ライブラリの導入
Podfileに以下をインストール

pod 'SwiftyJSON',
pod 'Alamofire',
pod 'SDWebImage

##コーディング
役割ごとにModel,View,Controllerに分けてコーディングを行う

##View

スクリーンショット 2021-09-26 16.06.35.png

##Model

model.swift
import Foundation
import SwiftyJSON
import Alamofire
protocol MusicProtocol {
    func catchData(count:Int)
}
class MusicModel{
    var artistNameArray = [String]()
    var trackCensoredNameArray = [String]()
    var previewUrlArray = [String]()
    var artworkUrl100Array = [String]()
    var musicDelegate:MusicProtocol?
    //JSON解析
    func setData(resultCount:Int,encodeUrlString:String){
        //Alamfireによる通信
        AF.request(encodeUrlString, method: .get, parameters: nil, encoding: JSONEncoding.default).responseJSON { (response) in
            //responseの値表示
            print("response:",response)
            //一旦配列に入っているものすべてを削除する(蓄積防止)
            self.artistNameArray.removeAll()
            self.trackCensoredNameArray.removeAll()
            self.previewUrlArray.removeAll()
            self.artworkUrl100Array.removeAll()
            switch response.result{
            case .success:
                do {
                    let json:JSON = try JSON(data: response.data!)
                    for i in 0...resultCount-1{
                        //もしartistNameがnilだったら
                        if json["results"][i]["artistName"].string == nil{
                            print("ヒットしませんでした")
                            return
                        }
                        self.artistNameArray.append(json["results"][i]["artistName"].string!)
                        self.trackCensoredNameArray.append(json["results"][i]["trackCensoredName"].string!)
                        self.previewUrlArray.append(json["results"][i]["previewUrl"].string!)
                        self.artworkUrl100Array.append(json["results"][i]["artworkUrl100"].string!)
                    }
                    //全てのデータを取得している状態
                    self.musicDelegate?.catchData(count:1)
                } catch  {
                }
                break
            case .failure(_):break
            }
        }
    }
}

##Controller

controller.swift
import UIKit
import SDWebImage
import AVFoundation
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource,UITextFieldDelegate,MusicProtocol {
    @IBOutlet weak var searchTextField: UITextField!
    
    @IBOutlet weak var musicTableView: UITableView!
    var musicModel = MusicModel()
    var player:AVAudioPlayer?

    override func viewDidLoad() {
        super.viewDidLoad()
        musicTableView.delegate = self
        musicTableView.dataSource = self
        searchTextField.delegate = self
    }
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    //tableViewDelegateによるデリゲートメソッド
    //returnカウントの数だけcellForRowAtが呼ばれる
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return musicModel.artistNameArray.count
    }
    //tableViewDelegateによるデリゲートメソッド
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = musicTableView.dequeueReusableCell(withIdentifier: "musicCell", for: indexPath)
        let artWorkImageView = cell.contentView.viewWithTag(1) as! UIImageView
        let musicNameLabel = cell.contentView.viewWithTag(2) as! UILabel
        let artistNameLabel = cell.contentView.viewWithTag(3) as! UILabel
        //写真、音楽名、アーティスト名の格納
        artWorkImageView.sd_setImage(with: URL(string: musicModel.artworkUrl100Array[indexPath.row]), completed: nil)
        musicNameLabel.text = musicModel.trackCensoredNameArray[indexPath.row]
        artistNameLabel.text = musicModel.artistNameArray[indexPath.row]
        //楽曲再生ボタンの作成
        let musicPlayButton = UIButton(frame: CGRect(x: 35, y: 21, width: 130, height: 130))
        musicPlayButton.setImage(UIImage(named:"play"), for: .normal)
        //ボタンを押したとき
        musicPlayButton.addTarget(self, action: #selector(playButtonTap(_:)), for:.touchUpInside)
        musicPlayButton.tag = indexPath.row
        //セルにmusicButtonを追加(StoryBoardにないから)
        cell.contentView.addSubview(musicPlayButton)
        return cell
    }
    //ボタンをタップした時
    @objc func playButtonTap(_ sender:UIButton){
        //音楽を止める
        if player?.isPlaying == true{
            player?.stop()
        }
        //sender.tagはindexPath.rowすなわちplayButton.tagとい一緒
        let url = URL(string: musicModel.previewUrlArray[sender.tag])
        downLoadMusicURL(url: url!)
    }
    //リターンが押されたときにキーボードを閉じる
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        //キーボードを閉じる
        refleshData()
        textField.resignFirstResponder()
        return true
    }
    //セルの高さ
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 170
    }
    //ダウンロードメソッド
    func downLoadMusicURL(url:URL){
      var downloadTask:URLSessionDownloadTask
      downloadTask = URLSession.shared.downloadTask(with: url, completionHandler: { (url, response, error) in
        self.play(url: url!)
      })
      downloadTask.resume()
    }
    //音楽再生メソッド
    func play(url:URL){
      do {
        player = try AVAudioPlayer(contentsOf: url)
        player?.prepareToPlay()
        player?.volume = 1.0
        player?.play()
      } catch let error as NSError {
        print(error.description)
      }
    }
    //MusicProtocolによるデリゲートメソッド
    func catchData(count: Int) {
        if count == 1{
            musicTableView.reloadData()
        }
    }
    func refleshData(){
        //テキストフィールドの中にアーティスト名が入ってたらアーティスト名を用いてitunesAPIを用いる
        if searchTextField.text?.isEmpty != nil{
            let urlString = "https://itunes.apple.com/search?term=\(String(describing:searchTextField.text!))&entity=song&contry=jp"
            //urlStringをエンコードする
            let encodeUrlString:String = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
            //委任
            musicModel.musicDelegate = self
            musicModel.setData(resultCount: 50, encodeUrlString: encodeUrlString)
            //キーボードを閉じる
            searchTextField.resignFirstResponder()
        }
    }
    
    @IBAction func searchAction(_ sender: Any) {
        refleshData()
    }
}

デモ動画

アルバムジャケットの上に配置した再生ボタンを押せば音源が流れる仕様になっています
demo2.gif

参考記事

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?