在Swift 5中编写可扩展的API客户端
对于本文,我们决定为Marvel API编写一个客户端。 在深入研究代码之前,让我们解释一下为什么选择它:
- 它是开放的 。 去获得一个API密钥!
- 它很大 。 否则,为什么要建立一个可扩展的客户端?
- 是真的 。 迫使我们处理丑陋的现实细节。
- 这是一致的 。 好的,我们不需要太多丑陋的细节,我们也需要系统的实现。
- 它有一个很棒的API测试器 。 文档非常丰富,此在线工具使测试我们的代码变得非常容易。
- 太酷了 。 😎
在本文的其余部分,我们将使用Swift Playgrounds对该API进行试验。 如果您真的想对它有用,请推荐使用Karumi的MarvelApiClient。
功能性无状态核心周围的有状态外壳
定义组件接口时,一个很好的规则是使您的设计基于无状态类型,且其值语义不会产生任何副作用。 拥有该核心后,您可以创建另一个将这些类型映射到实际副作用的类型。
我们的API客户端组件将围绕三种类型构建:
-
APIRequest
:将创建JSON请求的值类型。 -
APIResponse
:将从JSON响应中创建的值类型。 -
APIClient
:将接收请求,将其发送到服务器,然后通过回调通知调用方。
最后一种似乎比其他类型更混乱! 好吧,这就是我们的有状态外壳,它将是处理这种混乱状态的外壳。 实际上,该组件将与大多数细节无关,而您几乎不需要更改它。
APIClient初始接口
基于先前的想法,我们可能会从以下内容开始:
让我们从更改APIRequest
协议开始:
Decodable
协议为我们做了很多工作! 您是否注意到ID是非可选的? 因此,如果我们遇到格式不正确的ComicCharacter
,则会引发错误,并且我们将能够以我们认为合适的任何方式对其进行处理。
但是,那里还有另一种类型。 查看Image
类型? 在任何Apple框架中都没有定义,这是我们定义的自定义类型。 让我们看一下它的代码:
由于某种原因,Marvel API决定发送分为路径和扩展名的图像URL。 我们不希望将此解析细节公开给我们的业务逻辑层,因此我们尝试从该输入中构建正确的URL。
由于我们的输入和输出不匹配,因此我们必须走很长的路,并完全实现Decodable 。 这意味着定义一个CodingKey
枚举和一个init
。 对于这种情况,这似乎不是一个坏的权衡,我们一定会在所有地方重用此类型。
但这绝对是Codable
的苦涩药丸之一:全有或全无。 您不能仅为其中一个属性定义次要细节,如果默认实现不适合您,则必须从头开始实现所有内容。
在实际使用中,当您使用nil以外的默认值时,这种情况很常见。 例如,您是否要避免公开可为空的数组,而只是将它们转换为空数组? 开始输入数十行无聊的代码。
最后一个细节:看到MarvelError
吗? 为简便起见,我们没有使用标准库中定义的DecodingError
类型。 但是经过实践检验的实现肯定应该使用它来帮助破译程序员错误。
发送请求
使用之前的代码,这是我们最简单的请求:
看到? 我们所有的辛勤工作意味着什么 !
该请求是简洁的 , 响应是键入的 ,我们只需要使用标准的Swift构造即可 。 所有这些令人讨厌的实现细节都隐藏在我们的API客户端后面,并且公开的界面很好看。
但是我们还没有讨论重要的缺失部分:实际的API客户端组件,将这些无辜的值类型发送给Dark Dimension 。