LastMile 1.0.0

LastMile 1.0.0

Josh Elkins 维护。



LastMile 1.0.0

  • Josh Elkins

LastMile CircleCI

模型对象的强大解码,特别适用于使用 JSON API

  • 从 Swift 中对 JSON 进行类型安全访问
  • 解码模型对象的简单、干净的语法
  • 收集详细描述接收到的 JSON 异常的错误
  • 内置对常见 Swift 类型的解码

LastMile 与 Swift Codable 的比较

LastMile 旨在专门解决从您 API 的 JSON 中轻松且一致地构建模型对象的问题。LastMile 不是一个 Swift Codable 的替代品,Swift Codable 也不是 LastMile 的替代品。相反,它们可以一起用于相同的模型对象,但用于不同的目的。

  • _和谐共存。 在同一类型中,您可以独立使用 LastMile 和 Swift Codable。使用 LastMile 解码 API 响应允许您仅使用 Codable 进行内部序列化需求,从而实现更干净、更简单、更专注的代码。
  • _灵活性。 LastMile 可以轻松访问多个级别的 JSON。如果您需要深入嵌套的 JSON 以获取 ["items"][0]["values"][0]["name"],只需指定即可。Swift Decodable 在解码使用 Encodable 在同一类型上编码的数据方面表现出色,但不适用于从与内部模型结构不匹配的 JSON 中获取值。
  • _弹性。 当 Swift Decodable 遇到意外的值时,它会引发错误并停止解码,因为解码内部编码的数据时此类错误不太可能发生。LastMile会继续解码,从具有丢失或误类型字段的 JSON 中恢复尽可能多的内容。如果您解码一个包含100个值的数组,其中一个值是损坏的,LastMile将提供一个包含其他99个值和错误对象的数组,描述了为什么该元素失败。Codable将引发错误并返回响应的任何部分。
  • 责任归属。LastMile 将您的API响应中意外缺失或类型不符的所有内容都记录下来,并以错误对象集合的形式返回所有错误信息,无论是否返回响应。错误对象中的信息可以帮助您更快地找到问题,从而节省调试和生产停机时间。相比之下,Swift Decodable将为每个API响应提供一个错误:导致解码终止的那个错误。

使用LastMile

(查看项目中的测试文件PersonTests.swift,以查看运行中的示例代码。)

以下是样本模型对象

struct Person {
    let id: Int
    let firstName: String
    let lastName: String
    let phoneNumber: String?
    let height: Double?
}

idfirstNamelastName 是必填字段。phoneNumberheight 是可选的。

以下是我们将从其中解码该对象的JSON

{
    "person_id": 8675309,
    "first_name": "Mary",
    "last_name": "Smith",
    "phone_number": "(312) 555-1212",
    "height": "really tall"
}

(注意,"height" 的值意外地是字符串而不是数字,与预期相反。)

以下是为 Person 创建新实例的 APIDecodable 扩展

extension Person: APIDecodable {
    static var idKey: String? { return "person_id" }

    init?(from decoder: APIDecoder) {
        // 1a
        let id =          decoder["person_id"]    --> Int.self
        let firstName =   decoder["first_name"]   --> String.self
        let lastName =    decoder["last_name"]    --> String.self

        // 1b
        let phoneNumber = decoder["phone_number"] --> String?.self
        let height =      decoder["height"]       --> Double?.self

        // 2
        guard decoder.succeeded else { return nil }

        // 3
        self.init(id: id!, firstName: firstName!, lastName: lastName!, phoneNumber: phoneNumber, height: height)
    }
}

初始化器有三个步骤

  1. 使用解码器创建初始化 DemoStruct 所需的所有值。使用解析器的子脚本访问JSON子元素是安全的,然后您可以使用(1a)APIDecodable 或(1b)可选的 APIDecodable 的类型,通过使用-->运算符。

  2. 如果解码失败(即decoder.succeeded为false),初始化器将返回nil。这将在任何非可选值缺失时发生。

  3. 最后,通过调用成员初始化方法来初始化结构。如果decoder.succeeded为true,可以安全地强制解包必填值idfirstNamelastName

由于上面的JSON中的"height"值意外地是字符串而不是数字,将生成一个描述问题和位置的错误对象。

解码

从包含通过HTTP接收到的JSON的数据对象中解码一个 Person

let decodeResult = APIDataDecoder().decode(data: data, to: Person.self)
print(decodeResult.value ?? "nil")
// prints:
// Person(id: 8675309, firstName: "Mary", lastName: "Smith", phoneNumber: Optional("(312) 555-1212"), height: nil)

decodeResult.errors.forEach { print($0) }
// prints:
// root(Person person_id=8675309) > "height"(Double) : Unexpectedly found a string