はじめに
こんにちは、はじめです。
今回はAlamofireとUnboxを使ってJSONのパースをしてみようと思います。
サンプルコードではiTunesのAPIでアーティスト検索を行っています。
前提条件
・Swift 3.1
・Xcode 8.3.2
・Carthage 0.23.0
・Alamofire 4.4.0
・Unbox 2.4.0
完成形
完成形は以下の画像のようにSearchBarに入力をし検索を行うと、
検索結果がテーブルに表示される形になります。
ソースは以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | import UIKit class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate { var iTunesItems: ItunesItems? @IBOutlet weak var myTableView: UITableView! @IBOutlet weak var mySearchBar: UISearchBar! override func viewDidLoad() { super.viewDidLoad() myTableView.delegate = self myTableView.dataSource = self mySearchBar.delegate = self ApiItunes(artistName: "").getItunesItems(success: { getItunesItems in self.iTunesItems = getItunesItems // alamofireから結果が返ってきた際にテーブル情報を更新する self.myTableView.reloadData() }, fail: {(error: Error) in print(error)}) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { var count: Int = 0 if iTunesItems != nil { count = (iTunesItems?.results.count)! } return count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = UITableViewCell(style: .default, reuseIdentifier: "cell") // セルのテキストに楽曲名を代入する cell.textLabel?.text = iTunesItems?.results[indexPath.row].trackName return cell } // 検索ボタンが押された時に実行される処理 func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { view.endEditing(true) mySearchBar.showsCancelButton = true // SearchBarに入力されている値でイニシャライズしてAPIを実行する ApiItunes(artistName: mySearchBar.text!).getItunesItems(success: { getItunesItems in // 検索結果をTableViewにセットしてリロードする self.iTunesItems = getItunesItems self.myTableView.reloadData() }, fail: {(error: Error) in print(error)}) } // キャンセルボタンが押された時に実行される func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { view.endEditing(true) mySearchBar.showsCancelButton = false mySearchBar.text = "" ApiItunes(artistName: "").getItunesItems(success: { getItunesItems in self.iTunesItems = getItunesItems self.myTableView.reloadData() }, fail: {(error: Error) in print(error)}) } // SearchBarへの入力が開始された時に実行される func searchBarShouldEndEditing(_ searchBar: UISearchBar) -> Bool { mySearchBar.showsCancelButton = true return true } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | import Foundation import Unbox struct ItunesItems { let results: [ItunesItem] } struct ItunesItem { let artistName: String let trackName: String let trackPrice: Int } extension ItunesItems: Unboxable { init(unboxer: Unboxer) throws { self.results = try unboxer.unbox(key: "results") } } extension ItunesItem: Unboxable { init(unboxer: Unboxer) throws { // key:"xxx"に該当する値を作成したモデルのパラメータに割り当てます self.artistName = try unboxer.unbox(key: "artistName") self.trackName = try unboxer.unbox(key: "trackName") self.trackPrice = try unboxer.unbox(key: "trackPrice") } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | import Alamofire import Unbox class ApiItunes { let baseString: String = "http://itunes.apple.com/search?" let parameters: String = "country=JP&lang=ja_jp&media=music&entity=song&attribute=artistTerm&limit=30" var urlString: String = "" init(artistName: String) { urlString = baseString + parameters if artistName != "" { urlString += "&term=" + artistName } } func getItunesItems(success: @escaping (_ data: ItunesItems)-> Void, fail: @escaping (_ error: Error)-> Void) { Alamofire.request(urlString).responseJSON { response in let responseItems = response.result.value as! Dictionary<String, Any> do { // APIの返却値をUnboxでパースします let iTunesItems: ItunesItems = try unbox(dictionary: responseItems) success(iTunesItems) } catch { print(Error.self) } } } } |
モデルの作成
Unboxを使ってモデルの作成を行います。
今回はiTunesの楽曲情報の中からアーティスト名、楽曲名、楽曲の金額を取得します。
(表示に使用しているのは楽曲名だけです。)
1 2 3 4 5 6 7 8 9 | struct ItunesItems { let results: [ItunesItem] } struct ItunesItem { let artistName: String let trackName: String let trackPrice: Int } |
次にUnboxでJSONのパースを行います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | extension ItunesItems: Unboxable { init(unboxer: Unboxer) throws { self.results = try unboxer.unbox(key: "results") } } extension ItunesItem: Unboxable { init(unboxer: Unboxer) throws { // key:"xxx"に該当する値を作成したモデルのパラメータに割り当てます self.artistName = try unboxer.unbox(key: "artistName") self.trackName = try unboxer.unbox(key: "trackName") self.trackPrice = try unboxer.unbox(key: "trackPrice") } } |
APIクラスの作成
Alamofireに渡すためのURLを作成します。
今回はアーティスト名でのみ検索を行うので検索する文字列以外は固定となっております。
APIクラスを実行する際に検索する文字列をURLに追加する処理をイニシャライザとして用意しておきます。
1 2 3 4 5 6 7 8 9 10 11 | let baseString: String = "http://itunes.apple.com/search?" let parameters: String = "country=JP&lang=ja_jp&media=music&entity=song&attribute=artistTerm&limit=30" var urlString: String = "" init(artistName: String) { urlString = baseString + parameters if artistName != "" { urlString += "&term=" + artistName } } |
実際にAPIを実行する処理は以下になります。
1 2 3 4 5 6 7 8 9 10 11 12 | func getItunesItems(success: @escaping (_ data: ItunesItems)-> Void, fail: @escaping (_ error: Error)-> Void) { Alamofire.request(urlString).responseJSON { response in let responseItems = response.result.value as! Dictionary<String, Any> do { // APIの返却値をUnboxでパースします let iTunesItems: ItunesItems = try unbox(dictionary: responseItems) success(iTunesItems) } catch { print(Error.self) } } } |
ViewControllerの作成
はじめにstoryboard上でSearchBarとTableViewを追加し、outlet接続しておきます。
Delegateの指定
SearchBarとTableViewのデリゲートを指定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate { var iTunesItems: ItunesItems? @IBOutlet weak var myTableView: UITableView! @IBOutlet weak var mySearchBar: UISearchBar! override func viewDidLoad() { super.viewDidLoad() myTableView.delegate = self myTableView.dataSource = self mySearchBar.delegate = self } } |
TableViewの設定
TableViewの表示に必要なコードを記述します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | class ViewController: UIViewController...{ var iTunesItems: ItunesItems? override func viewDidLoad() { ... ApiItunes(artistName: "").getItunesItems(success: { getItunesItems in self.iTunesItems = getItunesItems // alamofireから結果が返ってきた際にテーブル情報を更新する self.myTableView.reloadData() }, fail: {(error: Error) in print(error)}) } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { var count: Int = 0 if iTunesItems != nil { count = (iTunesItems?.results.count)! } return count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = UITableViewCell(style: .default, reuseIdentifier: "cell") // セルのテキストに楽曲名を代入する cell.textLabel?.text = iTunesItems?.results[indexPath.row].trackName return cell } } |
SearchBarの設定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | // 検索ボタンが押された時に実行される処理 func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { view.endEditing(true) mySearchBar.showsCancelButton = true // SearchBarに入力されている値でイニシャライズしてAPIを実行する ApiItunes(artistName: mySearchBar.text!).getItunesItems(success: { getItunesItems in // 検索結果をTableViewにセットしてリロードする self.iTunesItems = getItunesItems self.myTableView.reloadData() }, fail: {(error: Error) in print(error)}) } // キャンセルボタンが押された時に実行される func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { view.endEditing(true) mySearchBar.showsCancelButton = false mySearchBar.text = "" ApiItunes(artistName: "").getItunesItems(success: { getItunesItems in self.iTunesItems = getItunesItems self.myTableView.reloadData() }, fail: {(error: Error) in print(error)}) } // SearchBarへの入力が開始された時に実行される func searchBarShouldEndEditing(_ searchBar: UISearchBar) -> Bool { mySearchBar.showsCancelButton = true return true } |
さいごに
このままだと日本語での検索に失敗してしまいました。
今後の課題として修正して行こうと思っております。
解決した際また記事にしてみようと思います。