APIKitのエラーハンドリング

どうも。

Swiftのお話です。

今回はAPIKitのエラーハンドリングについて書こうと思います。APIKitは、Swiftっぽさを前面に出した素晴らしいAPIクライアントライブラリです。非常に簡単に使えます。バージョンアップによってエラーハンドリングが少し強化されたっぽいので、まとめてみたいとおもいます。

APIのエラーのハンドリングについて

APIを叩いて様々なことをするアプリを開発することは多いと思いますが、問題となるのがエラーのハンドリングです。経験があまりないので詳しいことはわからないのですが、ステータスコード見たり、レスポンスの内容を見たりして、適切な処理をしないといけないですよね・・・。APIから返却されるエラーの情報に統一性があったり、エラーハンドリングの仕様がある程度統一されていれば、苦労はしないと思うのですが、アプリで色々なAPIを叩いて、APIごとにエラーの内容が違ったり、処理内容が違ったりすると少し面倒くさいです。

しかし、APIKitを使えば、簡単なはず・・・。

interceptObjectとresponseFromObject

APIKitでは、レスポンスを処理するフローが、RequestTypeプロトコルのExtensionで定義されています。デフォルトでは、

public func parseData(data: NSData, URLResponse: NSHTTPURLResponse) throws -> Response {
    let parsedObject = try dataParser.parseData(data)
    let passedObject = try interceptObject(parsedObject, URLResponse: URLResponse)
    return try responseFromObject(passedObject, URLResponse: URLResponse)
}

こんな感じです。

  1. Parserでパースできるかチェック(デフォルトのパーサーはJSONDataParserです)
  2. レスポンスとObjectの状態をチェック
  3. Objectをレスポンスクラスに変換

という流れになっていることが分かると思います。

まず、「try dataParser.parseData(data)」ですが、DataParserTypeに適合しているクラス・構造体ならば自分でカスタマイズして実装可能です。

DataParserTypeですが、

public protocol DataParserType {
    var contentType: String? { get }
    func parseData(data: NSData) throws -> AnyObject
}

こんな感じで定義されています。contentTypeは、HTTPのAcceptヘッダーに指定する文字列です。クライアントで処理するデータのタイプをリクエストします。parseDataは、データをパースする処理です。パースできなければErrorTypeに適合した何かを投げてください。

次に、「interceptObject」です。interceptObjectは、APIKit 2より追加されたメソッドだそうで、レスポンスを横取りして、エラーのハンドリングが可能です。

参考:http://blog.ishkawa.org/2016/05/23/1463585002/

HTTPのステータスコードごとに、異なるエラーを投げることが可能になります。ライブラリ作者さんの例では、Github APIのエラーハンドリングで、ステータスコードが400と422のときはGitHubErrorをthrowするようにしていますね。

ちなみに、interceptObjectは、Protocol Extensionによるデフォルトの実装でステータスコードが200..<300以外の時は「ResponseError.UnacceptableStatusCode」を投げるように実装されてます。

次に「responseFromObject」です。responseFromObjectは、もともとあるメソッドなのですが、バージョン2から例外をthrowする方式に変更されました。Swift2.0からthrowやcatchができるようになったので、それに合わせるようなエラー処理方式に書き換えたのだと思います。

responseFromObjectでは、interceptObjectをパスしてきたオブジェクトをRequestTypeのResponseに指定された型に変換します。変換できなかった場合は、エラーを投げるようにします。また、変換はできたが、レスポンスの中身に問題があった場合もエラーを投げるといいかもしれません。この実装は、ステータスコードは常に200を返すので、JSONの内容を見てエラーかどうか判断しなければいけない時に活用できます。

ちなみに、JSONのパーサーに「Himotoki」を使っている場合は、Himotokiのデコードメソッドがエラーを投げてくれるので、実装が非常に楽です。

RequestTypeの継承

RequestTypeを継承して、色々なデフォルト実装を持ったRequestTypeが作ろうというお話です。先ほど、紹介した「interceptObject」「responseFromObject」ですが、両方ともRequestTypeのメソッドです。つまるところ、RequestTypeに適合したクラス・構造体に実装するか、デフォルトの実装を使うことになります。おそらく、リクエストごとに実装するのは面倒臭いので、ほとんどの場合デフォルトの実装を使うことになります。

デフォルトの実装を使う場合、何のデフォルト実装にするかということが重要になります。RequestTypeを拡張しても良いですが、それだと複数の種類のエラーハンドリングができなくなってしまうので、RequestTypeを継承したプロトコルのデフォルト実装すると良いです。

例えば、AというサービスのAPIとBというサービスのAPIがあったとして、RequestTypeAとRequestTypeBを作って、両者ともRequestTypeを継承させます。そして、それぞれのextensionでbaseURLやエラーハンドリングのメソッドを実装するのです。

まとめ

APIKitはいいぞ。

※ Alamofireもいいけど、API叩くだけだったらAPIKitでいいよね。RequestTypeに適合させたクラス・構造体作っててきとーにほげほげするだけで簡単にAPIが叩けるのでよい。あと、ライブラリの実装が複雑じゃないので困ったときに調べやすい。
※ 今回取り上げたAPIKitのバージョンは2.0.1です。