アプリを作っていると WebAPI を叩く機会が多いと思いますが、今回は Swift で書いてみました。
これはなに
- 便利ライブラリの紹介
- あえて NSURLSession を使って叩く
- テスト書く
便利ライブラリ
後で紹介する NSURLSession を使ってもよいのですがいろいろと骨が折れるのでライブラリを使いましょう。
GET, POST, PUT, DELETE に対応していれば大体の API が叩けると思います。
Alamofire
みんな大好き Alamofire 。いろんなところで紹介されているので「またか」という人もいるのではないでしょうか。
高機能で美しくレスポンスを受け取れるのがいいですね。
個人的には
Alamofire.request(.GET, "http://httpbin.org/get", parameters: ["foo": "bar"])
.response { (request, response, data, error) in
println(request)
println(response)
println(error)
}
と request, response をメソッドチェイン的に書けるのが美しいですね。
Net
Alamofire 一強みたいな雰囲気がありますが、 Net というライブラリもあります。
ぼくはこっちを先に知ったのでよく使っています。素直に NSURLSession をラップしている、という感じで Objective-C 書いてたエンジニアは親しみやすいと思います(今となっては Alamofire の方がいいなあ…と思っている)。また、実装自体も追いやすいので拡張させたりできます。
let url = "http://www.puqiz.com/get_path"
net.GET(absoluteUrl: url, params: params, successHandler: { responseData in
let result = responseData.json(error: nil)
NSLog("result \(result)")
}, failureHandler: { error in
NSLog("Error")
})
コードはそれぞれの README から拝借しました。
ライブラリを導入してみる
- git-submodule
- cocoapods
git-submodule で入れる
後述の cocoapods が正式に対応していない以上、git-submodule で入れるのが一般的なのかなあと思います。みなさんはどうですか。
よく README に書いてあるのが
hoge.swift を直接 Project に入れてね
というようなことですね。 git clone
して入れてもいいですが Swift はすぐ仕様が変わるし、どのライブラリもアクティブにコミットされているのが多いので git submodule
で導入するのがいいと思います。
コマンドの使い方などは http://qiita.com/sotarok/items/0d525e568a6088f6f6bb このへんを読めばいいです。
悪い点
- 管理すべきものが git-submodule と cocoapods で分散する
ただでさえ pod update
するのもめんどくさいのに git submodule update
を打たないといけなくなり大変めんどくさくなります。
cocoapods で入れる
現在、cocoapods は Swift に対応していませんが、 swift branch に checkout すればなんとか使うことができます。
詳しくは Web で: http://swiftwala.com/cocoapods-is-ready-for-swift/
プロジェクトルートに Gemfile を作成
source 'https://rubygems.org'
gem 'cocoapods', :git => 'https://github.com/CocoaPods/CocoaPods.git', :branch => 'swift'
gem 'cocoapods-core', :git => 'https://github.com/CocoaPods/Core.git'
gem 'xcodeproj', :git => 'https://github.com/CocoaPods/Xcodeproj.git'
gem 'claide', :git => 'https://github.com/CocoaPods/CLAide.git'
bundle install する
$ bundle install
Podfile を編集
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
pod 'SwiftyJSON', :git => "https://github.com/orta/SwiftyJSON", :branch => "podspec"
pod 'Alamofire', :git => "https://github.com/mrackwitz/Alamofire.git", :branch => "podspec"
この例だと SwiftyJSON と Alamofire を導入しています。
pod install する
$ bundle exec pod install
毎回 bundle exec
が必要なようです。
NSURLSession で叩いてみる
ライブラリを紹介したあとで蛇足感ありますが、Swift で書いてみます。
今回は tiqav の API を叩いてみました。
import Foundation
class APIRequest {
init() {
// 初期化
}
class func getRandom() {
let url = NSURL(string: "http://api.tiqav.com/search/random.json")
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let request = NSURLRequest(URL: url!)
let task = session.dataTaskWithRequest(request, completionHandler: {
(data, response, error) in
switch (data, response, error) {
case (_, _, .Some(error)):
// エラー処理
println(error.localizedDescription)
case (.Some(data), .Some(response), _):
if (response as NSHTTPURLResponse).statusCode == 200 {
// なんか処理
var error: NSError?
let jsonArray = NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments, error: &error) as NSArray
println(jsonArray)
} else {
// 処理
}
default:
break
}
})
task.resume()
}
}
Swift の良いところは tuple にあると思います。
request の結果が (data, response, error)
というタプルで返ってくるのでエラー処理などを if 文書かずに switch で分けて書くことができます。
Swift の Optional には .Some
と .None
があるのでそれで nil かどうかを調べることができます。
それについては この記事 が詳しいです。
tuple で処理するところ以外は Objective-C と同じなので説明する必要はないでしょう。
というかこの switch 文の場合分けの記事をどこかで見た気がしたんだが忘れてしまったのでご存知の方教えていただけると助かります。
テスト書く
API 叩くあたりのクラスはちゃんとテストを書きたいですね。
というと、
- レスポンスのモックを用意する
- HTTPRequest をフックしてモックを返すようにする
ということが必要になりそうですね。
OHHTTPStubs
そのようなライブラリはたくさんありますが、一応これを紹介しておきます。
ただ、Objective-C で書かれており、cocoapods で導入したところうまく動きませんでした。
原因としては Objective-C のマクロを Swift がうまく処理できないのかなあというエラーがでていましたがイマイチ不明です。
aerogear-ios-httpstub
こちらは OHHTTPStubs の Swift 移植のものです。これはうまく動きました。
StubsManager.stubRequestsPassingTest({ (request: NSURLRequest!) -> Bool in
return true
}, withStubResponse:( { (request: NSURLRequest!) -> StubResponse in
return StubResponse(filename: "mystubbedjson.json", location:.Bundle(NSBundle(forClass: AGURLSessionStubsTests.self)), statusCode: 200, headers: ["Content-Type" : "text/json"])
}))
というようなコードを HTTP リクエストを送る前に呼んでおくとフックして、この例だと mystubbedjson.json
を返してくれるというわけです。
これでいつでも同じレスポンスを得ることができ、API のテストが書けるというわけです。
まとめ
- メソッドチェインで書くとかっこいい
- タプル使うと見やすく書ける
- cocoapods の Swift 対応を求む
- Swift 移植のライブラリも登場しはじめており非常に期待がもてる
長くなってしまいましたが終わりです。 Swift は見やすいコードが書けるのでとてもいいですね。