Build CodeCov Platforms Swift 6.0

KeyValueCoder

一个 Swift 库,用于将 Codable 类型序列化为 AnyUserDefaults,以及从它们反序列化。

用法

RawRepresentable 类型被编码为其原始值

// "fish"
let any = try KeyValueEncoder().encode(Food(rawValue: "fish"))

Collection 类型被编码为 [Any]

// ["fish", "chips"]
let any = try KeyValueEncoder().encode(["fish", "chips"])

结构体和类被编码为 [String: Any]

struct User: Codable {
   var id: Int
   var name: String
}

// ["id": 1, "name": "Herbert"]
let any = try KeyValueEncoder().encode(User(id: 1, name: "Herbert"))

Any 解码值

let food = try KeyValueDecoder().decode(Food.self, from: "fish")

let meals = try KeyValueDecoder().decode([String].self, from: ["fish", "chips"])

let user = try KeyValueDecoder().decode(User.self, from: ["id": 1, "name": "Herbert"])

当解码失败时,会抛出 DecodingErrorContext 包含指向失败属性的 keyPath。

// throws DecodingError.typeMismatch 'Expected String at SELF[1], found Int'
let meals = try KeyValueDecoder().decode([String].self, from: ["fish", 1])

// throws DecodingError.valueNotFound 'Expected String at SELF[1].name, found nil'
let user = try KeyValueDecoder().decode(User.self, from: [["id": 1, "name": "Herbert"], ["id:" 2])

// throws DecodingError.typeMismatch 'Int at SELF[2], cannot be exactly represented by UInt8'
let ascii = try KeyValueDecoder().decode([UInt8].self, from: [10, 100, 1000])

Nil 编码/解码策略

可以通过设置策略来调整 Optional.none 的编码方式。

默认策略保留 Optional.none

let encoder = KeyValueEncoder()
encoder.nilEncodingStrategy = .default

// [1, 2, nil, 3]
let any = try encoder.encode([1, 2, Int?.none, 3])

使用占位符字符串保留与 PropertyListEncoder 的兼容性

encoder.nilEncodingStrategy = .stringNull

// [1, 2, "$null", 3]
let any = try encoder.encode([1, 2, Int?.none, 3])

使用 NSNull 保留与 JSONSerialization 的兼容性

encoder.nilEncodingStrategy = .nsNull

// [1, 2, NSNull(), 3]
let any = try encoder.encode([1, 2, Int?.none, 3])

Nil 值也可以被完全删除

encoder.nilEncodingStrategy = .removed

// [1, 2, 3]
let any = try encoder.encode([1, 2, Int?.none, 3])

Int 解码策略

可以通过 intDecodingStrategy 调整 BinaryInteger 类型(IntUInt 等)的解码。

默认策略 IntDecodingStrategy.exact 确保源值能够被解码类型精确表示,允许解码没有小数部分的浮点数值

// [10, 20, -30, 50]
let values = try KeyValueDecoder().decode([Int8].self, from: [10, 20.0, -30.0, Int64(50)])

// throws DecodingError.typeMismatch because 1000 cannot be exactly represented by Int8
_ = try KeyValueDecoder().decode(Int8.self, from: 1000])

带有小数部分的值也可以通过使用任何 FloatingPointRoundingRule 进行舍入来解码为整数

let decoder = KeyValueDecoder()
decoder.intDecodingStrategy = .rounding(rule: .toNearestOrAwayFromZero)

// [10, -21, 50]
let values = try decoder.decode([Int].self, from: [10.1, -20.9, 50.00001]),

值也可以被限制在可表示的范围内

let decoder = KeyValueDecoder()
decoder.intDecodingStrategy = .clamping(roundingRule: .toNearestOrAwayFromZero)

// [10, 21, 127, -128]
let values = try decoder.decode([Int8].self, from: [10, 20.5, 1000, -Double.infinity])

UserDefaults

使用 UserDefaults 编码和解码 Codable 类型

try UserDefaults.standard.encode(
  User(id: "1", name: "Herbert"), 
  forKey: "owner"
)

try UserDefaults.standard.encode(
  URL(string: "fish.com"), 
  forKey: "url"
)

try UserDefaults.standard.encode(
  Duration.nanoseconds(1), 
  forKey: "duration"
)

值以 plist 原生类型的友好的表示形式持久化存储

let defaults = UserDefaults.standard.dictionaryRepresentation()

[
  "owner": ["id": 1, "name": "Herbert"],
  "url": URL(string: "fish.com"),
  "duration": [0, 1000000000]
]

从 defaults 中解码值

let owner = try UserDefaults.standard.decode(Person.self, forKey: "owner")

let url = try UserDefaults.standard.decode(URL.self, forKey: "url") 

let duration = try UserDefaults.standard.decode(Duration.self, forKey: "duration")