LoginSignup
16
11

More than 5 years have passed since last update.

APIKit+Codableにおけるエラーレスポンスの取得方法

Last updated at Posted at 2018-02-19

前提

APIKitとCodableとの連携という素晴らしい記事を参考に、Codableとの連携を終えていること

したいこと

ほとんどのWebAPIでは、エラー時のレスポンスが定義されています。
こんな感じに。

{
   "status": 400,
   "message": "hogehoge"
}

それをこの中で補足したい! ということ

Session.send(request) { result in
    switch result {
    case .success(let response):
        print(response)
    case .failure(let err):
        print(err)        
    }
}

参考

APIKit作者様のブログ「APIKit: レスポンスに応じた独自のエラーを投げる」
↑はCodable非対応なので、これをCodable対応にしましょう。

いざ、実装!

Requestオブジェクトに追加

現在はおそらくこんな感じだと思います。

APIKit_extension.swift
extension APIKit.Request where Response: Decodable {
    var dataParser: DataParser {
        return JSONDataParser() as DataParser
    }

    func response(from object: Any, urlResponse: HTTPURLResponse) throws -> Response {
        let decoder = JSONDecoder()
        return try decoder.decode(Response.self, from: object as! Data)
    }
}

ここにメソッドとエラーを定義します。(エラーは単純化したサンプル)

APIKit_extension.swift
extension APIKit.Request where Response: Decodable {
    var dataParser: DataParser {
        return JSONDataParser() as DataParser
    }

    func response(from object: Any, urlResponse: HTTPURLResponse) throws -> Response {
        let decoder = JSONDecoder()
        return try decoder.decode(Response.self, from: object as! Data)
    }

    func intercept(object: Any, urlResponse: HTTPURLResponse) throws -> Any {
        // 200番台でない場合は、MyErrorを投げる
        guard (200..<300).contains(urlResponse.statusCode) else {
            // ひとまず、Decodableな構造体に値を埋める
            let decoder = JSONDecoder()
            let obj_decoded = try decoder.decode(TmpMyError.self, from: object as! Data)
            // それもとにMyErrorを生成して投げる
            throw MyError(object: obj_decoded)
        }

        return object
    }
}

struct TmpMyError: Decodable {
    let status: Int
    let message: String
}

struct MyError: Error {
    let status: Int
    let message: String

    init(object: TmpMyError) {
        status = object.status
        message = object.message
    }
}

呼び出し側にcaseを追加

Session.send(request) { result in
    switch result {
    case .success(let response):
        print(response)
    case .failure(.responseError(let err as MyError)):
        print("MyServiceError Message", err.message)
    case .failure(let err):
        print("Unknown error", err)        
    }
}

おわりに

これが最適解かは分かりませんが、快適なAPIクライアントを開発できました。

16
11
0

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
16
11