使用可编码对带有关联值的枚举进行编码和解码(Swift 4)
在下载管理器框架的Swift 4迁移过程中,我重写了模型对象(符合NSCoding
NSObject
子类)。 我的DownloadItem
的状态由带有关联值的枚举表示:
使用Swift 3,我必须编写一个符合NSCoding
的包装对象,以保留该枚举。 使用Codable
不再需要此解决方法,因为我可以直接向枚举添加Codable
一致性。 现在,有不同的方法可以实现此目的。 我仍然不确定最好的解决方案是什么,但是也许有人可以使用我目前的方法。
在查看DownloadState
,让我们看一下一个简单的Value
枚举示例:
在类型和大小写之间存在一对一的映射,因此我们可以设置一个如下所示的容器:
//伪字典
[“ string”:字符串,“ int”:Int,“ data”:数据,“ double”:Double]
毕竟, KeyedDecodingContainer
和KeyedEncodingContainer
只是非常严格的字典,其中下标键是强类型的String
枚举(符合CodingKey
),而getter和setter利用Swift的错误处理系统来提供有意义的错误,说明为什么获取或设置特定键的值可能失败。 因此,至少对我而言,将容器视为具有固定键的字典是有意义的🙂
为Value
实现Codable
可能看起来像这样:
我们使用decodeIfPresent
函数,因为将只设置一个密钥。 缺点是我们不使用switch语句进行解码,因此当我们添加新的大小写而忘记对其进行解码时,编译器不会对我们大喊大叫。
而且,它不适用于DownloadState
示例,因为我们有带有和不带有关联值的案例。 我的方法是使用“基本”案例和关联值作为CodingKey
,然后让一个私有枚举将所有基本案例作为简单String
托管。 让我们看看它的外观:
我真正喜欢的是编译器可以完全自动生成Base
的Codable
实现,因为它只是一个String
枚举。 不幸的是,我们仍然必须为DownloadState
provide提供Codable
实现。
以下伪字典都是DownloadState
所有有效表示形式。
//伪字典
[“基地”:“取消”]
->下载状态。取消
[“ base”:“ paused”,“ pausedReason”:“ waitingForWifi”]
-> DownloadState.paused(原因:.waitingForWifi)
[“基础”:“下载”,“下载进度”:0.5]
-> DownloadState.downloading(进度:0.5]
可以缩放吗? 不……如果涉及多个关联值,则实际上这是一个非常复杂的类型,应像嵌套结构或类一样进行处理。 请考虑以下示例,其中Route
表示带有主页和详细信息页面(用于播放音乐)的音乐应用程序的不同屏幕:
超级简单吧? 但是将每个关联的值添加到CodingKeys
有一些缺点:
- 在与基本情况相同的层次结构上具有关联值在语义上是错误的(就像在
DownloadState
示例中一样,但是更加突出) - 如果枚举得到更多的情况(和更多的关联值),这将变得更加难以阅读和维护
- 如果这是一个类或结构,则您永远不会考虑对父代中嵌套类型的信息进行编码。
那么我们该如何解决呢? 我认为为每个具有关联值的案例添加结构是一种干净的方法,有助于分担责任:
由于我们将Codable
一致性委托给每个关联值结构,因此CodingKeys
每种情况下(与关联值)仅包含一个键。 该代码看起来很重复,但是很简单并且遵循一定的模式。 您也可以使用Sourcery来自动执行此过程。
它也适用于使用Codable
类型作为关联值的枚举:
同样,我们为基本案例提供了一个密钥,为每个案例提供了一个具有关联值的编码密钥。 这些结构自行处理编码和解码,并且结构内的类型本身负责实现Codable
。
祝你开心!