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

# はじめに
初投稿なので、お手柔らかに！

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ファイルを作成し、フレームワークを取り込む。

``` 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のどれを利用してもやりたいことが実現できそうですね。

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

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