LoginSignup
0
0

More than 3 years have passed since last update.

用 APIKit 的 DataParser 組出 Codable 的資料物件(筆記)

Posted at

在繼承 APIKit 必須實作的 Request 方法,通常會在這個地方做 mapping 成 model 物件這件事:

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

雖然在 iOS 12.* 這樣寫是可行的,但是在 iOS 11 會報錯,就來看看解決辦法:

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

    throw DataCastingError // 丟自訂轉型的錯誤
}

APIKit.DataParser

找了一些文章發現有這個 protocol 可以用 (官方程式碼),自訂義完之後,可以設定到 Request#dataParser 去。

預設值

先來看這個 dataParser 屬性的預設值,預設是用了 JSONDataParser 這個類別的物件

// Request.swift#L88-L90
// https://github.com/ishkawa/APIKit/blob/master/Sources/APIKit/Request.swift#L88-L90
var dataParser: DataParser {
    return JSONDataParser(readingOptions: [])
}

parse(data:) 可以看出來是用 JSONSerialization 來處理,回傳值可能會是 Array 或是 Dictionary

// JSONDataParser.swift#L22-L29
// https://github.com/ishkawa/APIKit/blob/master/Sources/APIKit/DataParser/JSONDataParser.swift#L22-L29
public func parse(data: Data) throws -> Any {
    guard data.count > 0 else {
        return [:]
    }

    return try JSONSerialization.jsonObject(with: data, options: readingOptions)
}

原理

在 DataParser 裡面可以直接拿到原始的 Data 物件,接著在 parse 裡面可以預先處理這個資料
處理完之後作為返回值回傳,接著在 Request#response 這個方法就可以拿到了

動手做 DataParser

想做的事
寫一次、所有的 Response 是 Codable 的 Request 都可以套用這個 DataParser

DataParser

想要做到 解耦合 ,所以就把 decoder 另外宣告成一個屬性:

ModelDataParser.swift
import Foundation
import APIKit

struct ModelDataParser<T: Codable>: APIKit.DataParser {
    var decoder: JSONDecoder

    // MARK: APIKit.DataParser

    var contentType: String? {
        return "application/json"
    }

    func parse(data: Data) throws -> Any {
        return try decoder.decode(T.self, from: data)
    }
}

Request 的設定

條件是「Response 是 Codable 的 Request 就必須採用這個 DataParser」
因此可以透過 extension 和條件判斷來設定:

import APIKit

extension Request where Response: Codable {
    var decoder: JSONDecoder {
        // 如果有客製化的 Decoder 可以直接放在這邊
        return JSONDecoder()
    }

    var dataParser: DataParser {
        return ModelDataParser<Response>(decoder: decoder)
    }
}

使用方法

回到 Request 的 response 方法,寫法如下:

func response(from object: Any, urlResponse: HTTPURLResponse) throws -> Response {
    if let result = object as? Response {
        return result
    }

    throw DataParsingError // 丟符合情境的錯誤
}

因為這邊資料出口都是 Any 這個型別,所以還需要再 as ? 轉型一次才有辦法回傳

參考資料

0
0
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
0
0