天哪,Codable太棒了

在上一篇文章中,我用滚动视图描述了我的短暂尝试,其中包括以下部分:

为了帮助您了解所见,我在Intertubes上寻找了引人注目的数据馈送示例,并最终选择了USGS地震馈送。 只是。 因为。 为了整理提要,我认为我可以快速给新的Swift Codable协议打转,那时候我就知道了Frickin 很棒的 Codable是什么。 但这是下一篇文章。 抓紧。

您现在可以停止收紧。 这是那篇文章

我们将深入研究Xcode 9和Swift 4的Codable功能。 我们将从我在滚动视图文章中提到的GeoJSON feed示例开始,然后使用RolePlayingCore Github存储库更深入地研究更多示例。

首先:简单地遵循Codable

我从一个仅包含几个项目的基本结构开始,然后迅速使用嵌套结构和枚举来构建它。 GeoJSON提要足够简单( 请参阅此处的链接 ),Swift提供的自动生成的构造函数和编码键非常有用。 我总结了一下,涵盖了大约一半的属性,大部分是有趣的属性:

现在,我没有尝试解决使用JSON可能引起的一些棘手的问题。 但这主要是因为该示例不需要它,无论如何,我花更多的真实代码(例如RolePlayingCore GitHub存储库)来揭示有关Codable的更深层真相的旅程很快就会花光。

“所有软件在编写后即会成为旧版。”-实用程序设计师Andrew Hunt和David Thomas

好的, 很好 。 那也不是真实的代码,但是由于我已经花了一些时间在JSON解码上,所以我在六个月的令人震惊的时间内建立了相当多的“旧版”代码,并且为后续工作提供了便利更深入到Codable。 所以。

CodingKeys,解码和编码,天哪!

当您必须处理带有下划线或空格,拼合或不拼合嵌套类型的名称,或转换非本地JSON类型(例如发给子级Date)的名称时 ,您突然不得不为Codables添加样板。 但是,这非常简单,我几乎想跳过它,并继续处理更棘手的问题。 但。 让我们。

让我们从使用序列化已经可以与JSON一起使用的现有类型开始。 所以。 假设某些D&D 特征,我们可能具有:

注意TODO。 好。 呼吁采取行动! 为这项开源计划做出贡献! 帮助我致富! 帮助我舒适地退休! 好,好,回到现实…

相应的JSON格式可能如下所示:

在Swift 3中,我可能已经定义了JSON属性键,并且该结构可能具有一个初始化器,该初始化器接受[String: Any]一些JSON反序列化字典:

好多警卫。 这么多焦虑。

在上面的代码中, Trait (上面未声明)是String ,并且提供了静态字符串以在小写的以空格分隔的JSON属性名称和它们的camelCaps对应名称之间进行转换。 您已经知道演练是否已经在执行这样的操作,以键入 密钥

因此,由于我在JSON属性名称中放置了空格,因此对于Swift 4 Codable,我需要明确了解CodingKeysTraits等效。 而且,由于我想使用后备默认值来选择性地读取其中的一些内容,因此我还需要init(from:)encode(to:)

干净得多。 许多博客,文章和网站已经涵盖了以上内容的基础知识。 这不是很有趣。 除了…… 打勾

注意在ClassTraits的结构中,有Dice和有Ability 。 等待! 骰子是一种协议! (您看过我关于Dice的文章吗?)而且,能力是……等等,哦,我还没有介绍。 好。 我现在将介绍它:这只是一个持有String的结构。 你知道那个演习。

但是,骰子! 请注意,在上面的Codable中,Dice是如何可解码的,但是在编码时需要以某种方式进行字符串化。 那是怎么回事?

让我们备份一个步骤。 这是重要的一步。

编码和解码字符串化类型

在介绍骰子之前,我想介绍体重身高货币 。 好的,暂时还不使用Currency (也许是关于它的另一篇文章)。 因为有关如何处理Dice的故事始于那些黑暗的地方。 但是要去那里,我们必须访问RacialTraits。 真是的 好。 我忘了讲。 种族特质

首先,结构,我最终得到的结果(不完全是可编码前版本的结构,但这已经足够接近了):

好的,所以有AbilityScoresAlignmentHeightWeight ,当然还有Dice 。 在进入Dice之前,我必须先了解其他人才能解码。

对齐很容易,因为它是枚举和双精度的结构。 如果您真的很好奇,请仔细阅读Alignment的源代码。 除非您错过了有关Codable的5000万篇文章或本文的前几段 ,否则它并不是特别有趣。 当您简单地使结构和枚举符合Codable时,它们就起作用

身高体重较难,因为它们分别是UnitLengthUnitMass的基础测量类型,而它们是NSCoding的类型。

啊。

NSCoding尚未与Codable对帐; 更重要的是,在上述情况下,我希望身高和体重像JSON Date一样 ,因为它们以字符串格式编码,例如“ 2 ft”和“ 3.2 kg”。 我将着重于Height进行说明。 高度是单位长度的度量,可以从字符串中解析出来,以英尺,英寸和公制为单位(以D&D的比例):

好。 那么,如何编码和解码呢? 好吧,这比我想的要难。 我不能轻易使Height Codable成为可能 ,因为它已经是NSCoding了 ,如果尝试,会发生各种伤害。 另外,我也不想这样做,因为我希望height是JSON中的格式化字符串,例如“ 3 ft 2 in”。 我知道如果我有一个字符串解析器可以转换为Height,我可以编码为字符串。 所以。 如何解码? 而且,如果我想支持解码未格式化的高度整数或双精度数(以基本单位为英尺),该怎么办?

唉! 从KeyedDecodingContainer解码该类型。 因此,如果我们提供用于decode的实现(并且不要忘了decodeIfPresent ),那么对于Height.Type ,它可能只是由编译器找到的:

确实,这可行。

我没有尝试实现等效的encode ,但是用“\(instance)”包装实例很容易。 因此,这就是为什么解码直接适用于该类型,而编码使用字符串的原因。

好。 因此,起泡沫,冲洗并重复进行“减肥”。

字符串化协议

现在,骰子。 尝试对Dice做同样的事情是行不通的。 问题的一部分是,它是一个协议 ,而不是类型 。 但是,如果我们做同样的事情,替换上面代码中出现Type的 Protocol ,它实际上也可以工作:

好! 因此,如果我依靠编码的字符串化,那么我可以依靠通过解码扩展将字符串(或其他形式)解码为特定类型。