LoginSignup
41
26

More than 5 years have passed since last update.

APIKitとCodableとの連携

Posted at

たとえば、以下のようなJSONを返却するAPIをAPIKitSwift4で新しく追加されたCodableを組み合わせて実装する。

users.json
{
    "id": 1,
    "login": "starwars",
    "url": "https://example.com/starwars",
}

APIKitとCodableを組み合わせる場合の注意

JSONDecoder#decodeData型を引数にとる。

JSONをパースするAPIKit組み込みのJSONDataParserは内部でJSONSerialization.jsonObjectを利用しており、戻り値はルートオブジェクトがDictionaryArrayとなる。

したがってパースされた値をモデルオブジェクトに変換する

func response(from object: Any, urlResponse: HTTPURLResponse) throws -> Response

の引数objectData型ではなくなる。

Data型の値を返すJSONDataParserの作成

JSONDecoder#decodeの引数にレスポンスのJSONを引き渡せるようにData型の値を返すDataParserプロトコルに準拠したパーサオブジェクトを作成する。

  • DataParserプロトコル
DataParser.swift
public protocol DataParser {
    var contentType: String? { get }
    func parse(data: Data) throws -> Any
}

Data型の値をそのまま返すDataParserを作成する。

JSONDataParser.swift
class JSONDataParser: APIKit.DataParser {
    var contentType: String? {
        return "application/json"
    }

    func parse(data: Data) throws -> Any {
        // ここではデコードせずにそのまま返す
        return data
    }
}

Requestプロトコルのfunc response(from object: Any, urlResponse: HTTPURLResponse) throws -> Responseを実装する。

Codableを適用したUserモデルを作成。

User.swift
struct User: Codable {
    let id: Int
    let login: String
    let url: String
}

これまでの内容を踏まえRequestプロトコルを適用したリクエストオブジェクトを作成する。

UserRequest.swift
struct UserRequest: APIKit.Request {
    typealias Response = User

    // MARK: - APIKit.Request

    var baseURL: URL { ... }
    var method: APIKit.HTTPMethod { ... }

    ...省略...

    // 作成したJSONDataParserをパーサとして適用する
    // (APIKit.JSONDataParserではない)
    var dataParser: DataParser {
        return JSONDataParser()
    }

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

もしくは個々のリクエストに実装せずに、型制約つきprotocol extensionsを利用する。

extension APIKit.Request where Response: Decodable {
    // 作成したJSONDataParserをパーサとして適用する
    var dataParser: DataParser {
        return JSONDataParser()
    }

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

リクエストの実行

リクエストを実行するとJSONDecoderCodableによってマッピングされたモデルオブジェクトを手軽に取得できる。

let request = UserRequest(name: "starwars")

Session.send(request) { result in
    switch result {
    case .success(let response):
        // responseはUserオブジェクト
    case .failure(let error):
        ...
    }
}
41
26
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
41
26