简短的问题是,当满足其中一个条件时,如何使类型有条件地符合具有两个条件的协议(protocol)?
我有一个通用类型 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<Void>
.但是因为 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<Data>' to protocol 'Decodable'; there cannot be more than one conformance, even with different conditional bounds
.
那么我怎样才能创建单独的 init(from:)
Data
时使用的函数是Void
?
最佳答案
我强烈建议您将泛型参数的类型名称从 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?
当您不希望返回任何数据并相应地解码时。
关于json - Swift 条件符合两种情况,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51449163/