【问题标题】:Swift conditional conformance with two casesSwift 条件符合两种情况
【发布时间】:2018-07-20 19:24:51
【问题描述】:

简短的问题是,当满足其中任何一个条件时,如何使类型有条件地符合具有两个条件的协议?

我有一个泛型类型NetworkResponse<Data>。它代表服务器响应。这是它的定义方式:

enum NetworkResponse<Data> {
    case success(Data)
    case error(ServerError)
}

我想让NetworkResponse 符合Decodable。这是我的服务器响应格式:

{
    "data": {
        "someKey": "someValue",
        "anotherKey": 15
    },
    "meta": {
        "returnCode": 0,
        "returnMessage": "operation is successful"
    }
}

data 部分取决于发出的请求。 meta 部分代表一些关于响应的元数据。就像它是否成功,或者如果不是什么是错误。

这就是我实现Decodable的方法:

extension NetworkResponse: Decodable where Data: Decodable {
    enum CodingKeys: CodingKey {
        case meta
        case data
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let metaValue = try container.decode(ServerError.self, forKey: .meta)

        if metaValue.code != 0 {
            self = .error(metaValue)
        } else {
            self = .success(try container.decode(Data.self, forKey: .data))
        }
    }
}

到目前为止一切顺利。但这是我的问题。对于一些不需要返回任何数据的 API,响应中的data 部分被省略。在这种情况下,我的响应如下所示:

{
    "meta": {
        "returnCode": 0,
        "returnMessage": "operation is successful"
    }
}

在这种情况下,我想将响应 json 解码为NetworkResponse&lt;Void&gt;。但是由于Void 不能符合Decodable(因为它是非标称类型)编译器给出错误。

为了克服这个问题,我尝试创建更专业的Decodable 扩展,其中Data 是Void,如下所示:

extension NetworkResponse: Decodable where Data == Void {
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let metaValue = try container.decode(AppErrors.Server.self, forKey: CodingKeys.meta)

        if metaValue.code != 0 {
            self = .error(metaValue)
        } else {
            self = .success(())
        }
    }
}

但编译器仍然编译如:Conflicting conformance of 'NetworkResponse&lt;Data&gt;' to protocol 'Decodable'; there cannot be more than one conformance, even with different conditional bounds。

那么当Data 是Void 时,如何创建单独的init(from:) 函数?

【问题讨论】:

  • 我相信Data 应该是可选的?其他选项可以是添加另一个成功的枚举案例。

标签: json swift generics protocols decodable


【解决方案1】:

我强烈建议您将泛型参数的类型名称从 Data 更改为远离Data,因为它很容易与在整个 Swift 中广泛使用的 Foundation.Data 混淆。

就问题本身而言,您可以创建一个空结构来表示“void”,并向您的NetworkResponse 添加一个新案例:

struct EmptyData: Decodable {}

enum NetworkResponse<T> {
    case success(T)
    case successWithEmptyData
    case error(ServerError)
}

extension NetworkResponse: Decodable where T: Decodable {
    private enum CodingKeys: CodingKey {
        case meta
        case data
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let metaValue = try container.decode(ServerError.self, forKey: .meta)

        if metaValue.code != 0 {
            self = .error(metaValue)
        } else if T.self == EmptyData.self {
            self = .successWithEmptyData
        } else {
            self = .success(try container.decode(T.self, forKey: .data))
        }
    }
}

let response = try! JSONDecoder().decode(NetworkResponse<EmptyData>.self, from: jsonData)

或者,您可以让您的.success 案例包含T?,当您期望没有数据返回并相应地解码时。

【讨论】:

    猜你喜欢
    • 2022-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-16
    • 1970-01-01
    • 2012-01-21
    相关资源
    最近更新 更多