11
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

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

前提

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クライアントを開発できました。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
11
Help us understand the problem. What are the problem?