Posted at

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

More than 3 years have passed since last update.


はじめに

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

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

ただし、どの場面でどの手法をとるか、エンジニアの技量が試されそうです。

メモリ管理上の観点(値型、参照型)など、メリット・デメリットを整理し、

こうあるべきだという、コーディング規約ができたら、いいなー

有志の方へ

もっとこうしたら良いよなど、

アドバイスをお待ちしております。