一个简短的问题是,当满足两个条件中的任何一个条件时,我该如何使该类型有条件地符合协议?
我有一个通用类型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
函数?
答案 0 :(得分:0)
我强烈建议您将通用参数的类型名称从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?
。