模型对象的强大解码,特别适用于使用 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?
}
id
,firstName
和 lastName
是必填字段。phoneNumber
和 height
是可选的。
以下是我们将从其中解码该对象的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)
}
}
初始化器有三个步骤
-
使用解码器创建初始化
DemoStruct
所需的所有值。使用解析器的子脚本访问JSON子元素是安全的,然后您可以使用(1a)APIDecodable
或(1b)可选的APIDecodable
的类型,通过使用-->
运算符。 -
如果解码失败(即decoder.succeeded为false),初始化器将返回
nil
。这将在任何非可选值缺失时发生。 -
最后,通过调用成员初始化方法来初始化结构。如果decoder.succeeded为true,可以安全地强制解包必填值
id
,firstName
和lastName
。
由于上面的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