はじめに
こんにちは、はじめです。
今回は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 | importUIKit classViewController:UIViewController,UITableViewDelegate,UITableViewDataSource,UISearchBarDelegate{ variTunesItems:ItunesItems? @IBOutlet weakvarmyTableView:UITableView! @IBOutlet weakvarmySearchBar:UISearchBar! overridefuncviewDidLoad(){ 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)inprint(error)}) } overridefuncdidReceiveMemoryWarning(){ super.didReceiveMemoryWarning() } functableView(_tableView:UITableView,numberOfRowsInSection section:Int)->Int{ varcount:Int=0 ifiTunesItems!=nil{ count=(iTunesItems?.results.count)! } returncount } functableView(_tableView:UITableView,cellForRowAt indexPath:IndexPath)->UITableViewCell{ letcell=UITableViewCell(style:.default,reuseIdentifier:"cell") // セルのテキストに楽曲名を代入する cell.textLabel?.text=iTunesItems?.results[indexPath.row].trackName returncell } // 検索ボタンが押された時に実行される処理 funcsearchBarSearchButtonClicked(_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)inprint(error)}) } // キャンセルボタンが押された時に実行される funcsearchBarCancelButtonClicked(_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)inprint(error)}) } // SearchBarへの入力が開始された時に実行される funcsearchBarShouldEndEditing(_searchBar:UISearchBar)->Bool{ mySearchBar.showsCancelButton=true returntrue } } |
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 | importFoundation importUnbox structItunesItems{ letresults:[ItunesItem] } structItunesItem{ letartistName:String lettrackName:String lettrackPrice:Int } extensionItunesItems: Unboxable{ init(unboxer:Unboxer)throws{ self.results=tryunboxer.unbox(key:"results") } } extensionItunesItem: Unboxable{ init(unboxer:Unboxer)throws{ // key:"xxx"に該当する値を作成したモデルのパラメータに割り当てます self.artistName=tryunboxer.unbox(key:"artistName") self.trackName=tryunboxer.unbox(key:"trackName") self.trackPrice=tryunboxer.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 | importAlamofire importUnbox classApiItunes{ letbaseString:String="http://itunes.apple.com/search?" letparameters:String= "country=JP&lang=ja_jp&media=music&entity=song&attribute=artistTerm&limit=30" varurlString:String="" init(artistName:String){ urlString=baseString+parameters ifartistName!=""{ urlString+="&term="+artistName } } funcgetItunesItems(success:@escaping(_data:ItunesItems)->Void,fail:@escaping(_error:Error)->Void) { Alamofire.request(urlString).responseJSON{response in letresponseItems=response.result.valueas!Dictionary<String,Any> do{ // APIの返却値をUnboxでパースします letiTunesItems:ItunesItems=tryunbox(dictionary:responseItems) success(iTunesItems) }catch{ print(Error.self) } } } } |
モデルの作成
Unboxを使ってモデルの作成を行います。
今回はiTunesの楽曲情報の中からアーティスト名、楽曲名、楽曲の金額を取得します。
(表示に使用しているのは楽曲名だけです。)
1 2 3 4 5 6 7 8 9 | structItunesItems{ letresults:[ItunesItem] } structItunesItem{ letartistName:String lettrackName:String lettrackPrice:Int } |
次にUnboxでJSONのパースを行います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | extensionItunesItems: Unboxable{ init(unboxer:Unboxer)throws{ self.results=tryunboxer.unbox(key:"results") } } extensionItunesItem: Unboxable{ init(unboxer:Unboxer)throws{ // key:"xxx"に該当する値を作成したモデルのパラメータに割り当てます self.artistName=tryunboxer.unbox(key:"artistName") self.trackName=tryunboxer.unbox(key:"trackName") self.trackPrice=tryunboxer.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" varurlString:String="" init(artistName:String){ urlString=baseString+parameters ifartistName!=""{ urlString+="&term="+artistName } } |
実際にAPIを実行する処理は以下になります。
1 2 3 4 5 6 7 8 9 10 11 12 | funcgetItunesItems(success:@escaping(_data:ItunesItems)->Void,fail:@escaping(_error:Error)->Void) { Alamofire.request(urlString).responseJSON{response in letresponseItems=response.result.valueas!Dictionary<String,Any> do{ // APIの返却値をUnboxでパースします letiTunesItems:ItunesItems=tryunbox(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 | classViewController:UIViewController,UITableViewDelegate,UITableViewDataSource,UISearchBarDelegate{ variTunesItems:ItunesItems? @IBOutlet weakvarmyTableView:UITableView! @IBOutlet weakvarmySearchBar:UISearchBar! overridefuncviewDidLoad(){ 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 | classViewController:UIViewController...{ variTunesItems:ItunesItems? overridefuncviewDidLoad(){ ... ApiItunes(artistName:"").getItunesItems(success:{getItunesItems in self.iTunesItems=getItunesItems // alamofireから結果が返ってきた際にテーブル情報を更新する self.myTableView.reloadData() },fail:{(error:Error)inprint(error)}) } functableView(_tableView:UITableView,numberOfRowsInSection section:Int)->Int{ varcount:Int=0 ifiTunesItems!=nil{ count=(iTunesItems?.results.count)! } returncount } functableView(_tableView:UITableView,cellForRowAt indexPath:IndexPath)->UITableViewCell{ letcell=UITableViewCell(style:.default,reuseIdentifier:"cell") // セルのテキストに楽曲名を代入する cell.textLabel?.text=iTunesItems?.results[indexPath.row].trackName returncell } } |
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 | // 検索ボタンが押された時に実行される処理 funcsearchBarSearchButtonClicked(_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)inprint(error)}) } // キャンセルボタンが押された時に実行される funcsearchBarCancelButtonClicked(_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)inprint(error)}) } // SearchBarへの入力が開始された時に実行される funcsearchBarShouldEndEditing(_searchBar:UISearchBar)->Bool{ mySearchBar.showsCancelButton=true returntrue } |
さいごに
このままだと日本語での検索に失敗してしまいました。
今後の課題として修正して行こうと思っております。
解決した際また記事にしてみようと思います。