Ios Swift可与泛型编码,保存来自响应的公共数据

Ios Swift可与泛型编码,保存来自响应的公共数据,ios,swift,generics,codable,jsondecoder,Ios,Swift,Generics,Codable,Jsondecoder,我会尽可能用最好的方式解释我想做什么,就像我在谷歌搜索前几天所做的那样 我的应用程序与几个不同的API通信,但是我们首先考虑来自一个API的响应。来自每个端点的响应包含一些“公共参数”,例如状态或错误消息,以及我们最感兴趣的单个对象或对象数组,它带来重要数据,我们可能希望对其进行编码、存储、将其放入领域、核心数据等 例如,单个对象的响应: { "status": "success", "response_code": 200, "messages": [

我会尽可能用最好的方式解释我想做什么,就像我在谷歌搜索前几天所做的那样

<>我的应用程序与几个不同的API通信,但是我们首先考虑来自一个API的响应。来自每个端点的响应包含一些“公共参数”,例如状态或错误消息,以及我们最感兴趣的单个对象或对象数组,它带来重要数据,我们可能希望对其进行编码、存储、将其放入领域、核心数据等

例如,单个对象的响应:

{
    "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 {}