ResilientDecoding 1.1.0

ResilientDecoding 1.1.0

由以下人员维护:Dan FedermanFrancisco DiazGeorgeMichael Bachand



  • 作者:
  • George Leontiev

Resilient Decoding

Swift Package Manager compatible Carthage compatible Version License Platform

简介

本包定义了在解码 Decodable 类型时从错误中部分恢复的机制。它还旨在提供可 ergonomics 的 API,用于在开发过程中检查解码错误并在生产中报告它们。

更多详情见下,但以下是本包能实现的功能一瞥:

struct Foo: Decodable {
  @Resilient var array: [Int]
  @Resilient var value: Int?
}
let foo = try JSONDecoder().decode(Foo.self, from: """
  {
    "array": [1, "2", 3],
    "value": "invalid",
  }
  """.data(using: .utf8)!)

运行此代码后,foo 将成为 Foo,其中 foo.array == [1, 3]foo.value == nil。在 DEBUG 中,foo.$array.results 将是 [.success(1), .failure(DecodingError.dataCorrupted(…), .success(3)],而 foo.$value.error 将是 DecodingError.dataCorrupted(…)。该功能仅为 DEBUG 所用,这样我们就可以在发布构建中保持 无开销

设置

Swift 包管理器

在您的Package.swift文件中

  dependencies: [
    .package(name: "ResilientDecoding", url: "https://github.com/airbnb/ResilientDecoding.git", from: "1.0.0"),
  ]

CocoaPods

在您的Podfile文件中

platform :ios, '12.0'
pod 'ResilientDecoding', '~> 1.0'

Decoding

此包的主要接口是@Resilient属性包装器。它可以应用于四种类型的属性:OptionalArrayDictionary以及遵循本包提供的ResilientRawRepresentable协议的自定义类型。

Optional

Optional是最简单的属性类型之一,可以被转换为Resilient。写出@Resilient var foo: Int?形式的属性将被初始化为nil,如果在解码过程中遇到错误(例如,如果foo键的值是),则不会抛出错误。

Array

Resilient也可以应用于数组或可选数组([T]?)。如果属性以@Resilient var foo: [Int]的形式编写,当foo键不存在或者值不是预期的(例如,String)时,会初始化一个空数组。同样,如果此数组的任何元素在解码过程中失败,将省略该元素。这个可选数组变体会将值设置为nil(如果键不存在或具有null值),否则为空数组。

Dictionary

“Resilient” 也可以应用于(字符串键)字典或可选字典([String: T]?)。以 @Resilient var foo: [String: Int] 形式编写的属性,如果 foo 键不存在或值为意外值(如 String),则初始化为空字典。同样,如果字典中任何 解码失败,该值将被省略。此可选字典的变体将在键不存在或具有 null 值时将值设置为 nil,否则为空数组。

ResilientRawRepresentable

自定义类型可以实现 ResilientRawRepresentable 协议,允许它们自定义 在被解码为 Resilient 属性时的行为(否则没有影响)。ResilientRawRepresentable 继承自 RawRepresentable,主要用于具有原始值的 enum 的适配。它有两个静态属性:decodingFallbackisFrozen

decodingFallback

ResilientRawRepresentable 类型可以定义可选的 decodingFallback,允许其在无需包裹在可选值的情况下进行弹性解码。例如,以下枚举可用于属性编写 @Resilient var myEnum: MyEnum

enum MyEnum: String, ResilientRawRepresentable {
  case existing
  case unknown
  static var decodingFallback: Self { .unknown }
}

注意: ArrayDictionaryResilientRawRepresentable 总是省略元素,而不是使用 decodingFallback

isFrozen

isFrozen 控制是否将新 RawValues 报告错误到 ResilientDecodingErrorReporter。默认情况下,isFrozen 设置为 false,这意味着返回 nilinit(rawValue:)RawValue不会 报告错误。当你希望旧版本的代码在不报告错误的情况下支持新的代码时,这很有用,例如,当演变支持 iOS 应用程序的后端 API。以这种方式,该属性类似于 Swift 的 @frozen 属性,尽管它们实现不同的目标。isFrozen 对属性级错误没有影响。

检查错误

Resilient 提供了两种检查错误的机制,一种用于开发期间,另一种用于在生产中报告意外错误。

属性级别错误

DEBUG 构建中,Resilient 属性提供了一个包含在解码过程中遇到错误信息的 projectedValue。可以使用 $property.outcome 属性检查此信息,该属性是一个包含 keyNotFoundvalueWasNil 等情况的枚举。这与错误不同,因为当属性值为 Optional 时,上述两种情况实际上是错误的,例如。标量类型,如 OptionalResilientRawRepresentable,还提供了一个 error 属性。开发人员可以通过访问 $foo.error 来确定解码属性时是否发生了错误,其中属性写入 @Resilient var foo: Int?@Resilient 数组属性提供两个额外的字段: errorsresultserrors 是解码数组时从错误中恢复的所有错误列表。results 将这些错误与成功解码的数组元素交织在一起。例如,当解码具有 @Resilient var baz: [Int] 的属性时,JSON片段 [1, 2, "3"]results 将是两个 .success 值,后面跟着一个 .failure

ResilientDecodingErrorReporter

在生产中,可以使用 ResilientDecodingErrorReporter 来汇总在解码具有 Resilient 属性的类型时遇到的全部错误。JSONDecoder 提供了一个方便的 decode(_:from:reportResilientDecodingErrors:) API,如果遇到错误,它将返回解码值和错误摘要。更复杂的用例需要将 ResilientDecodingErrorReporter 添加到 DecoderuserInfo 中,作为 .resilientDecodingErrorReporter 用户信息键的值。解码类型后,您可以调用 flushReportedErrors,如果遇到错误,它将返回一个 ErrorDigest。摘要可以用来访问底层错误(errorDigest.errors)或在 DEBUG 中格式化打印(debugPrint(errorDigest))。

格式化打印的摘要看起来是这样的:

resilientArrayProperty
  Index 1
    - Could not decode as `Int`
  Index 3
    - Could not decode as `Int`
resilientRawRepresentableProperty
  - Unknown novel value "novel" (this error is not reported by default)

注意:属性包装器上可用的错误与报告给 ResilientDecodingErrorReporter 的错误有一个区别:后者默认 报告 UnknownNovelValueError(当非冻结的 ResilientRawRepresentableinit(rawValue:) 返回 nil 时抛出 UnknownNovelValueError)。您可以通过在错误摘要上调用 errors(includeUnknownNovelValueErrors: true) 来改变这种行为。

常见问题

当封装类型为泛型参数时,Resilient能否按预期工作?

不。如果你有一个泛型类型,其泛型参数为<T>,并指定@Resilient var someResilient: T,无论T是一个数组还是字典,它都会被视为单个值。

更多信息

有关特定Resilient字段遇到特定错误时如何表现的更多信息,建议查阅单元测试。