Swift 通用可序列化协议

kol*_*leS 1 generics swift swift-protocols

我注意到我有很多重复的代码来获取/发送 JSON 到我的应用程序的 API,但唯一不同的是正在(反)序列化的实体。所以我想出了以下设计(为简洁起见,只有 GET 方法):

HTTPClient.swift

func getEntityJSON<T: JSONSerializable>(
    type: T.Type,
    url: String,
    completionHandler: @escaping (_ result: T?,
                                  _ headers: [String: String]?,
                                  _ statusCode: Int?,
                                  _ error: Error?) -> Void) {
    HttpClient.sharedInstance().getJSON(url, completionHandler: { (jsonData, headers, statusCode, error) in
        if let error = error {
            completionHandler(nil, headers, statusCode, error)
        } else {
            if let entityData = jsonData {
                completionHandler(T.fromJSON(data: entityData), headers, statusCode, nil)
            }
        }
    })
}
Run Code Online (Sandbox Code Playgroud)

打算像这样使用它:

HTTPClient.sharedInstance().getEntity(type: Translation, url: url) { (translation, _, _, _) in
    // do stuff with Translation instance
}
Run Code Online (Sandbox Code Playgroud)

这是JSONSerializable协议:

import Foundation
import SwiftyJSON


protocol Serializable: Codable {
    static func deserialize<T: Codable>(data: Data) -> T?
    func serialize() -> Data?
}

extension Serializable {
    static func deserialize<T: Decodable>(data: Data) -> T? {
        let decoder = JSONDecoder()
        decoder.keyDecodingStrategy = .convertFromSnakeCase
        decoder.dateDecodingStrategy = .formatted(DateFormatter.iso8601Full)
        return try? decoder.decode(T.self, from: data)
    }
    
    func serialize() -> Data? {
        let encoder = JSONEncoder()
        encoder.keyEncodingStrategy = .convertToSnakeCase
        encoder.dateEncodingStrategy = .formatted(DateFormatter.iso8601Full)
        return try? encoder.encode(self)
    }
}

protocol JSONSerializable: Serializable {
    func toJSON() -> JSON?
    static func fromJSON<T>(data: JSON) -> T?
}


extension JSONSerializable {
    func toJSON() -> JSON? {
        if let data = self.serialize() {
            return try? JSON(data: data)
        } else {
            return nil
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这允许我定义这样的结构:

翻译.swift


struct Translation: Hashable, Identifiable, JSONSerializable {
    var id: UUID
    ...
    
    static func fromJSON(data: JSON) -> Translation? {
Run Code Online (Sandbox Code Playgroud)

我会得到serialize, deserialize, toJSON功能。但编译器抱怨Translation不符合JSONSerializable,由于缺乏:

static func fromJSON<T>(data: JSON) -> T?. Translation在这种情况下,我认为我可以使用具体类型来实现该功能。

我希望既能做到Translations.fromJSON(data: data),也能做到T.fromJSON(data: data)。我怎样才能做到这一点?

Swe*_*per 5

泛型 ( <T>) 意味着您的方法可以使用调用者(不是被调用者,不是实现者,不是其他任何人)指定的任何类型。在 的情况下fromJSON,情况显然不是这样。的任何具体实现fromJSON仅适用T于封闭类型,因此fromJSON不适合泛型。

当一个方法“仅适用T于封闭类型”时,它是Self类型的用例:

protocol JSONSerializable: Serializable {
    func toJSON() -> JSON?
    static func fromJSON(data: JSON) -> Self?
}
Run Code Online (Sandbox Code Playgroud)

这样,您的Translation实现将符合协议。

同样,deserialise也应声明为:

static func deserialize(data: Data) -> Self?
Run Code Online (Sandbox Code Playgroud)

如果你真的想要 aJSONSerialisable可以变成JSON调用者想要的任何类型,那么它不是 a JSONSerialisable,更多的是 a JSONSerialiserCodable在这种情况下继承没有多大意义。