92
98

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 5 years have passed since last update.

スマートデバイス・テクノロジーAdvent Calendar 2015

Day 8

SwiftでAPIクライアントをつくる (Alamofire+ObjectMapper)

Posted at

はじめに

初投稿なので、お手柔らかに!

Swiftは、テケテケ初心者ですが、
業務でAPIをよく使うので、APIクライアントを作ってみます。

ゴール

Alamofire + ObjectMapper を利用して、APIクライアントを作っちゃ。(富山弁)

利用するAPIについて

今回は、iTunesのSearch APIを例に実装してみます。
http://www.apple.com/itunes/affiliates/resources/documentation/itunes-store-web-service-search-api.html#searching

やること

・APIをコールし、レスポンスデータをパースする。

やらないこと

・検索画面
・検索結果画面(レスポンスデータを保存する、UIに反映させる)
・Carthageのインストールや設定

実装手順

  1. AlamofireとObjectMapperをCarthageを利用して取り込む。
  2. DTOクラス(APIのレスポンス)を作成する。
  3. APIクライアントを作成する。
  4. APIクライアントを実装する。

いざ、チャレンジ

1. AlamofireとObjectMapperをCarthage(カルタゴ)を利用して取り込む。

プロジェクト配下に、Cartfileファイルを作成し、フレームワークを取り込む。

github "Alamofire/Alamofire" ~> 3.0
github "Hearst-DD/ObjectMapper" ~> 1.0

フレームワークの取り込み方は、割愛します。
詳しくは、下記の記事が参考になります。
http://qiita.com/yuta-t/items/97fe9bc2bf2e97da7ec1

2. DTOクラス(レスポンス)を作成する。

Search APIのレスポンス(JSON)は、下記のとおりです。
results部分がJSON Array(配列)になっています。

{
    "resultCount": 1, 
    "results": [
        {
            "artistId": 671759255, 
            "artistName": "サザンオールスターズ", 
            "artistViewUrl": "https://itunes.apple.com/jp/artist/sazanorusutazu/id671759255?uo=4", 
            "artworkUrl100": "http://is5.mzstatic.com/image/thumb/Music1/v4/62/cf/41/62cf41fd-beae-b407-6b6b-b6524fb0a824/source/100x100bb.jpg", 
            "artworkUrl30": "http://is5.mzstatic.com/image/thumb/Music1/v4/62/cf/41/62cf41fd-beae-b407-6b6b-b6524fb0a824/source/30x30bb.jpg", 
            "artworkUrl60": "http://is5.mzstatic.com/image/thumb/Music1/v4/62/cf/41/62cf41fd-beae-b407-6b6b-b6524fb0a824/source/60x60bb.jpg", 
            "collectionCensoredName": "東京VICTORY - Single", 
            "collectionExplicitness": "notExplicit", 
            "collectionId": 949250439, 
            "collectionName": "東京VICTORY - Single", 
            "collectionPrice": 750.0, 
            "collectionViewUrl": "https://itunes.apple.com/jp/album/dong-jingvictory/id949250439?i=949250542&uo=4", 
            "country": "JPN", 
            "currency": "JPY", 
            "discCount": 1, 
            "discNumber": 1, 
            "isStreamable": false, 
            "kind": "song", 
            "previewUrl": "http://a455.phobos.apple.com/us/r30/Music1/v4/79/d9/88/79d9882f-8481-c68d-c131-be3f8c01c602/mzaf_6920758322735728543.plus.aac.p.m4a", 
            "primaryGenreName": "J-Pop", 
            "releaseDate": "2014-09-10T07:00:00Z", 
            "trackCensoredName": "東京VICTORY", 
            "trackCount": 3, 
            "trackExplicitness": "notExplicit", 
            "trackId": 949250542, 
            "trackName": "東京VICTORY", 
            "trackNumber": 1, 
            "trackPrice": 250.0, 
            "trackTimeMillis": 309467, 
            "trackViewUrl": "https://itunes.apple.com/jp/album/dong-jingvictory/id949250439?i=949250542&uo=4", 
            "wrapperType": "track"
        }
    ]
}

データ項目の種類が多いため、
今回は、TrackIDとTrackName、artistNameの3つのみパースします。

データ項目 説明
TrackID  トラックID(iTunesが管理しているID)
TrackName  トラック名(曲名)
artistName  アーティスト名

パース処理は、「ObjectMapper」というライブラリを使います。
Swiftの場合、「SwiftyJSON」を利用しているサンプルが多いですが
個人的には、「ObjectMapper」がお気に入りです。

まずは、ソースコードから見てください。
基本的には、JSONのキーと管理する構造体のプロパティを
マッピングするだけです。

import ObjectMapper

struct Results: Mappable {
    var resultCount = 0
    var results: [Track]?
    
    init?(_ map: Map){}
    
    mutating func mapping(map: Map) {
        resultCount <- map["resultCount"]
        results     <- map["results"]
    }
}

struct Track: Mappable {
    var trackName = ""
    var trackId = ""
    var artistName = ""
    
    init?(_ map: Map){}
    
    mutating func mapping(map: Map) {
        trackId     <- map["trackId"]
        trackName   <- map["trackName"]
        artistName  <- map["artistName"]
    }
}

3. APIクライントを作成する。

今回は、APIClient.swiftとします。
通信部分は、定番の「Alamofire」を利用します。
iOS8以上をサポートしているようです。

ちなみに、iOS7でもAlamofire.swiftを足せばいけるらしい
(iOS7はやく消えてくれ!)

3.1. 外枠を作ります。

APIのレスポンスを複数パターンのデータ型に対応するため
ジェネリックスで定義します。。

 APIClient.swift
import Alamofire
import ObjectMapper

enum Result<T> {
    case Success(T)
    case Error(NSError)
}

class APIClient<T> {
}

3.2. リクエストURLの作成

今回利用するSearch APIのリクエストURLは、下記のとおりです。
https://itunes.apple.com/search?parameterkeyvalue」

項目 説明
BaseURL  https://itunes.apple.com
APIパス  search
HTTPメソッド  GET
リクエストパラメタ  オプションがいろいろある

この部分は、enumのAssociated Valueを使ってみます。

 APIClient.swift
enum Router: URLRequestConvertible {
    static let baseURLString = "https://itunes.apple.com"
    
    case API_SEARCH([String: AnyObject])
    
    var URLRequest: NSMutableURLRequest {
        
        let (method, path, parameters) : (String, String, [String: AnyObject]) = {
            
            switch self {
            case .API_SEARCH(let params):
                return ("GET", "/search", params)
            }
        }()
        
        let URL = NSURL(string: Router.baseURLString)
        let URLRequest = NSMutableURLRequest(URL: URL!.URLByAppendingPathComponent(path))
        URLRequest.HTTPMethod = method
        let encoding = Alamofire.ParameterEncoding.URL
        return encoding.encode(URLRequest, parameters: parameters).0
    }
}

APIの種類が増えると、Caseを増やしていくイメージです。

3.3 APIクライントにアクセスメソッドを追加する。

呼び出し元から呼びやすいようにラップしているだけです。

APIClient.swift

    func iTunesSearch(params : [String: AnyObject], completionHandler: (Result<T>) -> () = {_ in}) {
        
        Alamofire.request(Router.API_SEARCH(params))
            .validate()
            .responseJSON { response in
                switch response.result {
                case .Success:                    
                    if let resp = Mapper<Results>().map(response.result.value) {
                        completionHandler(Result<T>.Success((resp as? T)!))
                    }                    
                case .Failure(let error):
                    completionHandler(Result<T>.Error(error))
                }
        }
    }

4. APIクライントを実装する。

APIクライアントをコールしてみます。

リクエストパラメタもいろいろあるため、
必須パラメタだけ指定することとします。

パラメタ 説明
term  検索キーワード
country  国  (例) jp

APIをコールするときは、APIレスポンスのデータ型に合わせて指定します。
今回は、DTOクラスで作ったResults型を指定しています。
(ResultsとResultと分かりづらいですが、データ型はJSONのキーに合わせるのが、定石のようです。)

ViewController.swift
    @IBAction func searchMusic(term: String) {
                
        APIClient<Results>().iTunesSearch(["term" : term, "country" : "jp"]) { (response) -> () in

            switch response {
            case .Success(let itunesSearch):
                
                for result in itunesSearch.results! {

                    //とりあえず、トラック名をコンソールに出力
                    print(result.trackName)
                }
                
                break
            case .Error(let error):
                print(error)
                break
            }
        }
    }

検索キーワードに「サザン」と入れてみました。

無事、トラック名の一覧が取得できました。
やったー

東京VICTORY
TSUNAMI
イヤな事だらけの世の中で
LOVE AFFAIR~秘密のデート
真夏の果実
蛍
アロエ
はっぴいえんど
HOTEL PACIFIC
いとしのエリー
涙のキッス
東京VICTORY
ピースとハイライト
エスとハイライト
エ\343ロティカ・セブン EROTICA SEVEN
栄光の男
蛍
ピースとハイライ蛍
ピースとハイライ\343\203ト
Missing Persons
ミス・ブランニュー・デイ(MISS BRAND-NEW DAY)
真夏の果実
平和の鐘が鳴る
バラ色の人生
勝手にシンドバ生
勝手にシンドバ\343\203ッド
青春番外地
天井棧敷の怪人
ワイングラスに消えたワイングラスに消えた\346恋
彼氏になりたくワイングラスに消えた\346恋
彼氏になりたく\343\201て
みんなのうた
栞のテーマ
天国オン・ザ・ビーチ
道
Bye Bye My Love(U are the one)
愛の言霊 ~Spiritual Message~
マンピーのG★SPOT
太陽は罪な奴
匂艶 THE NIGHT CLUB
あなただけを ~Summer Heartbreak~
夏をあきらめて
C調言葉に御用心
逢いたくなった時に 君はここにいない
海
チャコの海岸物語
そんなヒロシに騙されて
Ya Ya(あに騙されて
Ya Ya(あ\343\201の時代を忘れない)
に騙されて
Ya Ya(あ\343\201の時代を忘れない)
\343\202クリスマス・ラブ(涙のあに騙されて
Ya Ya(あ\343\201の時代を忘れない)
\343\202クリスマス・ラブ(涙のあ\343とには白い雪が降る)
いなせなロコモーション
YOU
鎌倉物語
TSUNAMI
さよならベイビー

まとめ

Swiftは、structやenumなどが拡張され、
class、struct、enumのどれを利用してもやりたいことが実現できそうですね。

ただし、どの場面でどの手法をとるか、エンジニアの技量が試されそうです。
メモリ管理上の観点(値型、参照型)など、メリット・デメリットを整理し、
こうあるべきだという、コーディング規約ができたら、いいなー

有志の方へ
もっとこうしたら良いよなど、
アドバイスをお待ちしております。

92
98
4

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
92
98

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?