Ios Swift可与泛型编码,保存来自响应的公共数据
我会尽可能用最好的方式解释我想做什么,就像我在谷歌搜索前几天所做的那样 <>我的应用程序与几个不同的API通信,但是我们首先考虑来自一个API的响应。来自每个端点的响应包含一些“公共参数”,例如状态或错误消息,以及我们最感兴趣的单个对象或对象数组,它带来重要数据,我们可能希望对其进行编码、存储、将其放入领域、核心数据等 例如,单个对象的响应:Ios Swift可与泛型编码,保存来自响应的公共数据,ios,swift,generics,codable,jsondecoder,Ios,Swift,Generics,Codable,Jsondecoder,我会尽可能用最好的方式解释我想做什么,就像我在谷歌搜索前几天所做的那样 我的应用程序与几个不同的API通信,但是我们首先考虑来自一个API的响应。来自每个端点的响应包含一些“公共参数”,例如状态或错误消息,以及我们最感兴趣的单个对象或对象数组,它带来重要数据,我们可能希望对其进行编码、存储、将其放入领域、核心数据等 例如,单个对象的响应: { "status": "success", "response_code": 200, "messages": [
{
"status": "success",
"response_code": 200,
"messages": [
"message1",
"message2"
]
"data": {
OBJECT we're interested in.
}
}
或者,使用对象数组进行响应:
{
"status": "success",
"response_code": 200,
"messages": [
"message1",
"message2"
]
"data": [
{
OBJECT we're interested in.
},
{
OBJECT we're interested in.
}
]
}
好的。这很简单,很容易理解
现在,我想编写一个“根”对象,它将保存“公共参数”,或status
、response\u code
和messages
,并为我们感兴趣的特定对象(或对象数组)创建另一个属性
继承
第一种方法是创建根对象,如下所示:
类根:可编码{
let状态:字符串
让我们回复代码:Int
让消息:[字符串]?
私有枚举编码键:字符串,编码键{
案例状态、响应代码、消息
}
必需的公共初始化(来自解码器:解码器)抛出{
let container=try?decoder.container(keyedBy:CodingKeys.self)
状态=try container?.decodeIfPresent(String.self,forKey:.code)?“”
response_code=try container?.decodeIfPresent(Int.self,forKey:.错误)??0
messages=try container?.decodeIfPresent([String].self,forKey:.key)
}
public func encode(到编码器:编码器)抛出{}
}
一旦我有了这个根对象,我就可以创建从这个根对象继承的特定对象,并在JSONDecoder中传递我的特定对象,在那里,我有了一个很好的解决方案。但是,此解决方案对于阵列失败。
也许对某些人来说,它不是,,但我无法强调我多么不想制作额外的“复数”对象,它只存在于容纳一系列对象的地方,比如:
类对象:根{
让对象:[对象]
//从“数据”键解码“对象”数组的代码
}
结构对象:可编码{
出租物业1
出租物业2
出租物业3
//对对象的所有属性进行解码的代码
}
它看起来并不干净,它需要单独的对象来保存一个数组,在某些情况下,由于继承,它会产生存储到领域的问题,最重要的是,它会产生可读性较差的代码
仿制药
我的第二个想法是尝试使用泛型,因此我提出:
struct Root:可编码{
let状态:字符串
让我们回复代码:Int
让消息:[字符串]?
让数据:T?
私有枚举编码键:字符串,编码键{
案例状态、响应代码、消息、数据
}
必需的公共初始化(来自解码器:解码器)抛出{
let container=try?decoder.container(keyedBy:CodingKeys.self)
状态=try container?.decodeIfPresent(String.self,forKey:.code)?“”
response_code=try container?.decodeIfPresent(Int.self,forKey:.错误)??0
messages=try container?.decodeIfPresent([String].self,forKey:.key)
data=try container.decodeIfPresent(T.self,forKey:.data)
}
public func encode(到编码器:编码器)抛出{}
}
有了它,我可以像这样将单个对象和对象数组传递给JSONDecoder:
let decodedValue=try JSONDecoder().decode(Root.self,from:data)
//或
让decodedValue=try JSONDecoder().decode(Root.self,from:data)
这真是太好了。我可以在根结构的.data属性中获取所需的结构,并将其用作单个对象或对象数组。我可以轻松地存储它,不受限制地任意操作继承带来了上面的示例。在我的案例中,这个想法失败的地方是当我想在某个不确定t设置为什么的地方访问“公共属性”时。
这是对我的应用程序中实际发生的事情的一个简化解释,我将对它进行一些扩展,以解释这种通用解决方案在哪里不适用于我,最后问我的问题
问题和疑问
正如上面提到的,应用程序使用3个API,所有3个API都有不同的
根结构,当然,还有很多不同的“子结构”——可以这样命名。我在应用程序中有一个单一的位置,单一的APIResponse
对象,它返回到应用程序的UI部分,在UI部分我从解码值
,解码值
中提取1个可读错误,作为“子结构”,作为我的任何“特定对象”,汽车
,狗
,房子
,电话
通过继承解决方案,我可以做如下事情:
struct api响应{
var值:T{
迪塞特{
extractErrorDescription()
}
}
var errorDescription:String?=“Oops。”
func extractErrorDescription(){
如果让responseValue=值为?Root1,则让error=responseValue.errors.first{
self.errorDescription=错误
}
否则,如果让responseValue=值为?Root2{
self.errorDescription=responseValue.error
}
否则,如果让responseValue=值为?Root3{
self.errorDescription=responseValue.message
}
}
}
但是使用泛型解决方案,我无法做到这一点。如果我试图用Root1
或Root2
或Root3
编写相同的代码,如泛型示例所示:
func extractErrorDescription(){
如果让responseValue=值为?Root1,则让error=responseValue.errors.first{
自我错误
protocol ErrorProviding {
var error: String? { get }
}
struct APIResponse<T: ErrorProviding> {
var value: T?
var error: String? { value?.error }
}
extension Root1: ErrorProviding {
var error: String? { errors.first }
}
extension Root2: ErrorProviding {}