0
1

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.

iTunesSearchAPIをSwiftで使って、一覧表示させてみた

Last updated at Posted at 2021-09-07

初めに

SwiftでiTunesSearchAPIを使って楽曲一覧アプリを作ってみたいと思います。
初心者にもわかりやすく、AutoLayoutの設定、デザインパターン、コードの可読性もしっかり守っているので、APIの入門記事としてはぴったりかなと。
まず完成形はこちら!

Videotogif (3).gif

UIの設計

このように配置していきます。

スクリーンショット 2021-09-07 0.45.15.png
スクリーンショット 2021-09-07 0.44.02.png

制約をつけていきます。

スクリーンショット 2021-09-07 0.45.15.png
スクリーンショット 2021-09-07 0.44.02.png

SearchViewController,ListTableViewCellを作り、IBOutlet,IBAction接続します。

SearchViewController.swift
import UIKit

class SearchViewController: UIViewController{
    
    @IBOutlet weak var searchBar: UISearchBar!{
        didSet{
            searchBar.delegate = self
        }
    }
    @IBOutlet weak var tableView: UITableView!{
        didSet{
            tableView.delegate = self
            tableView.dataSource = self
        }
    }
}
ListTableViewCell.swift
import UIKit

class ListTableViewCell: UITableViewCell {
    @IBOutlet weak var artistImageView: UIImageView!
    @IBOutlet private weak var songNameLabel: UILabel!
    @IBOutlet private weak var artistNameLabel: UILabel!
}

全体設計

UIができた後に、今回のアプリの設計を行なっていく。
スクリーンショット 2021-09-07 11.23.33.png
スクリーンショット 2021-09-07 11.18.33.png

APIの取得

まず、APIの取得からやっていきたいと思います。
iTunesAPIを使います。

今回は,https://itunes.apple.com/search?term=\(text)&entity=song&country=jpを使っていきます。

こちらでこのように(https://itunes.apple.com/search?term=西野カナ&entity=song&country=jp)APIを叩くと、JSONデータを変換してくれます。
スクリーンショット 2021-09-07 11.34.03.png

これらのデータをうまく使い今回はアプリを作成していきます。

iTunesAPI

今回のAPIにおいてのロジックを管理するiTunesAPIを書いていきます。

iTunesAPI.swift
import Foundation
import Reachability

class iTunesAPI {
    
    private static var task: URLSessionTask?
    
    enum SearchRepositoryError: Error {
        case wrong
        case network
        case parse
    }
    
    static func searchRepository(text: String, completionHandler: @escaping (Result<[Song], SearchRepositoryError>) -> Void) {
        if !text.isEmpty {
            
            let reachability = try! Reachability()
            if reachability.connection == .unavailable {
                completionHandler(.failure(SearchRepositoryError.network))
                return
            }
            let urlString = "https://itunes.apple.com/search?term=\(text)&entity=song&country=jp".addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)!
            
            guard let url = URL(string: urlString) else {
                completionHandler(.failure(SearchRepositoryError.wrong))
                return
            }
            
            let task = URLSession.shared.dataTask(with: url) { (data, res, err) in
                if err != nil {
                    completionHandler(.failure(SearchRepositoryError.network))
                    return
                }
                
                guard let date = data else {return}
                
                if let result = try? jsonStrategyDecoder.decode(Songs.self, from: date) {
                    completionHandler(.success(result.results))
                } else {
                    completionHandler(.failure(SearchRepositoryError.parse))
                }
            }
            task.resume()
        }
    }
    
    static private var jsonStrategyDecoder: JSONDecoder {
        let decoder = JSONDecoder()
        decoder.keyDecodingStrategy = .convertFromSnakeCase
        return decoder
    }
    
    static func taskCancel() {
        task?.cancel()
    }
}

Songs

レスポンスしたデータをデコードするためSongsを作ります。

Songs.swift
import Foundation

struct Songs :Codable{
    let results:[Song]
}

struct Song :Codable{
    //アーティスト名
    let artistName :String?   
    //楽曲名
    let trackCensoredName:String?
   //音源URL
    let previewUrl:String?
    //ジャケ写URL
    let artworkUrl100:String
    
    var artworkImageUrl100:URL?{
        return URL(string: artworkUrl100)
    }
}

SearchViewController

取得したデータをViewに反映させる、またTableView,SearchBarの操作のためにSearchViewControllerを作っていきます。

SearchViewController.swift
import UIKit
import JGProgressHUD
import AVFoundation

class SearchViewController: UIViewController{
    
    @IBOutlet weak var searchBar: UISearchBar!{
        didSet{
            searchBar.delegate = self
        }
    }
    @IBOutlet weak var tableView: UITableView!{
        didSet{
            tableView.delegate = self
            tableView.dataSource = self
        }
    }
 
    private var songs: [Song] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()

        let nib = UINib(nibName: ListTableViewCell.cellIdentifier, bundle: nil)
        tableView.register(nib, forCellReuseIdentifier: ListTableViewCell.cellIdentifier)
        
        tableView.rowHeight = UITableView.automaticDimension
    }
}

extension SearchViewController: UITableViewDelegate, UITableViewDataSource {
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return songs.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: ListTableViewCell.cellIdentifier, for: indexPath) as! ListTableViewCell

        let song = songs[indexPath.row]
        cell.setup(song:song)
        return cell
    }
}

extension SearchViewController: UISearchBarDelegate{
    func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
        return true
    }
    
    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
        iTunesAPI.taskCancel()
    }
    
    func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
        guard !(searchBar.text?.isEmpty ?? true) else { return }
        searchBar.resignFirstResponder()
        let word = searchBar.text!
        
        let progressHUD = JGProgressHUD()
        progressHUD.show(in: self.view)
        
        iTunesAPI.searchRepository(text: word) { result in
            DispatchQueue.main.async {
                progressHUD.dismiss()
            }
            
            switch result {
            case .success(let results):
                self.songs = results
                DispatchQueue.main.async {
                    self.tableView.reloadData()
                }
            case .failure(let error):
                DispatchQueue.main.async {
                    switch error {
                    case .wrong :
                        let alert = ErrorAlert.wrongWordError()
                        self.present(alert, animated: true, completion: nil)
                        return
                    case .network:
                        let alert = ErrorAlert.networkError()
                        self.present(alert, animated: true, completion: nil)
                        return
                    case .parse:
                        let alert = ErrorAlert.parseError()
                        self.present(alert, animated: true, completion: nil)
                        return
                    }
                }
            }
        }
        return
    }
}

ListTableViewCell

tableViewCellの配置を行うListTableViewCellを作っていきます。
その前に画像のキャッシュのために便利なSDWebImageというライブラリを使いたいと思います。
SDWebImageの詳しい説明、導入の仕方などはこれらの記事を見るとわかると思います。

ListTableViewCell.swift
import UIKit
import SDWebImage

class ListTableViewCell: UITableViewCell {
    @IBOutlet private weak var artistImageView: UIImageView!
    @IBOutlet private weak var songNameLabel: UILabel!
    @IBOutlet private weak var artistNameLabel: UILabel!
    
    static let cellIdentifier = String(describing: ListTableViewCell.self)
    
    func setup(song: Song) {
        
        songNameLabel.text = song.trackCensoredName ?? ""
        artistNameLabel.text = song.artistName ?? ""
        
        if let url = song.artworkImageUrl100 {
            artistImageView.sd_setImage(with: url, completed: nil)
        } else {
            artistImageView.image = nil
        }
    }
}

ErrorAlert

最後に、様々なエラー処理のためのクラスを作っていきたいと思います。

ErrorAlert.swift
import UIKit

class ErrorAlert {
    
    private static func errorAlert(title: String, message: String = "") -> UIAlertController {
        let alert: UIAlertController = UIAlertController(title: title, message : message, preferredStyle: UIAlertController.Style.alert)
        let defaultAction = UIAlertAction(title: "OK", style: UIAlertAction.Style.default)
        alert.addAction(defaultAction)
        return alert
    }
    
    static func wrongWordError() -> UIAlertController {
        return errorAlert(title: "不正なワードが入力されました", message: "検索ワードを確認してください")
    }
    
    static func networkError() -> UIAlertController {
        return errorAlert(title: "インターネットに接続されていません", message: "接続状況を確認してください")
    }
    
    static func parseError() -> UIAlertController {
        return errorAlert(title: "データの表示に失敗しました")
    }
}

終わりに

以上でこのようなアプリが作成できました。
Videotogif (3).gif

0
1
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?