在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

API客户端实施