カテゴリー: iOS

Swift3 + Alamofire4 + Unbox2.4を使ってJSONをパースする

はじめに

こんにちは、はじめです。

 

今回は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に入力をし検索を行うと、

検索結果がテーブルに表示される形になります。

ソースは以下のようになります。

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
    }
}
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")
    }
}
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の楽曲情報の中からアーティスト名、楽曲名、楽曲の金額を取得します。

(表示に使用しているのは楽曲名だけです。)

struct ItunesItems {
    let results: [ItunesItem]
}

struct ItunesItem {
    let artistName: String
    let trackName: String
    let trackPrice: Int
}

 

次にUnboxでJSONのパースを行います。

 

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に追加する処理をイニシャライザとして用意しておきます。

    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を実行する処理は以下になります。

    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のデリゲートを指定します。

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の表示に必要なコードを記述します。

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の設定

    // 検索ボタンが押された時に実行される処理
    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
    }

 

さいごに

このままだと日本語での検索に失敗してしまいました。

今後の課題として修正して行こうと思っております。

解決した際また記事にしてみようと思います。

hajimenagasawa

シェア
執筆者:
hajimenagasawa
タグ: Swift

最近の投稿

フロントエンドで動画デコレーション&レンダリング

はじめに 今回は、以下のように…

2週間 前

Goのクエリビルダー goqu を使ってみる

はじめに 最近携わっているとあ…

4週間 前

【Xcode15】プライバシーマニフェスト対応に備えて

はじめに こんにちは、suzu…

2か月 前

FSMを使った状態管理をGoで実装する

はじめに 一般的なアプリケーシ…

3か月 前