ExCodable
En | 中文
ExCodable - Swift Codable
的键映射扩展。
内容
特性
- 扩展 Swift
Codable
- 支持编码和解码功能; - 支持通过
KeyPath
和编码键进行键映射;- ExCodable 不会通过不安全的指针读写内存;
- 无需逐个编码/解码属性;
- 只需要使用
var
声明属性并提供默认值; - 在大多数情况下,不再需要
CodingKey
类型,因为它只使用一次,因此字符串字面量可能更好。
- 支持对不同数据源进行多个键映射;
- 支持通过
Array
进行解码,支持多个替代键; - 支持使用点语法通过
String
进行嵌套键映射; - 支持使用下标进行自定义编码/解码;
- 支持内置和自定义类型转换;
- 支持
struct
、class
和子类; - 默认使用 JSON 编码器/解码器,并支持 PList;
- 使用类型推断,支持 JSON
Data
、String
和Object
; - 返回
Optional
值而不是抛出错误,以避免频繁使用try?
。
使用方法
struct
进行键映射
1. 为需要使用
var
来声明属性并提供默认值。
struct TestStruct: Equatable {
private(set) var int: Int = 0
private(set) var string: String = ""
}
extension TestStruct: ExCodable {
static var keyMapping: [KeyMap<Self>] = [
KeyMap(\.int, to: "int"),
KeyMap(\.string, to: "string")
]
init(from decoder: Decoder) throws {
decode(with: Self.keyMapping, using: decoder)
}
func encode(to encoder: Encoder) throws {
encode(with: Self.keyMapping, using: encoder)
}
}
class
进行键映射
2. 为在扩展类中不能采用
ExCodable
。
class TestClass: ExCodable, Equatable {
var int: Int = 0
var string: String? = nil
init(int: Int, string: String?) {
self.int = int
self.string = string
}
static var keyMapping: [KeyMap<TestClass>] = [
KeyMap(ref: \.int, to: "int"),
KeyMap(ref: \.string, to: "string")
]
required init(from decoder: Decoder) throws {
decodeReference(with: Self.keyMapping, using: decoder)
}
func encode(to encoder: Encoder) throws {
encode(with: Self.keyMapping, using: encoder)
}
static func == (lhs: TestClass, rhs: TestClass) -> Bool {
return lhs.int == rhs.int && lhs.string == rhs.string
}
}
3. 子类的键映射
需要为子类声明另一个静态键映射。
class TestSubclass: TestClass {
var bool: Bool = false
required init(int: Int, string: String, bool: Bool) {
self.bool = bool
super.init(int: int, string: string)
}
static var keyMappingForTestSubclass: [KeyMap<TestSubclass>] = [
KeyMap(ref: \.bool, to: "bool")
]
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
decodeReference(with: Self.keyMappingForTestSubclass, using: decoder)
}
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
encode(with: Self.keyMappingForTestSubclass, using: encoder)
}
static func == (lhs: TestSubclass, rhs: TestSubclass) -> Bool {
return lhs.int == rhs.int
&& lhs.string == rhs.string
&& lhs.bool == rhs.bool
}
}
4. 附加键
static var keyMapping: [KeyMap<Self>] = [
KeyMap(\.int, to: "int", "i"),
KeyMap(\.string, to: "string", "str", "s")
]
5. 嵌套键
static var keyMapping: [KeyMap<Self>] = [
KeyMap(\.int, to: "nested.int"),
KeyMap(\.string, to: "nested.nested.string")
]
6. 自定义编码/解码处理器
static var keyMapping: [KeyMap<Self>] = [
KeyMap(\.int, to: "int"),
KeyMap(\.string, to: "string", encode: { (encoder, stringKeys, test, keyPath) in
encoder[stringKeys.first!] = "dddd"
}, decode: { (test, keyPath, decoder, stringKeys) in
switch test.int {
case 100: test.string = "Continue"
case 200: test.string = "OK"
case 304: test.string = "Not Modified"
case 403: test.string = "Forbidden"
case 404: test.string = "Not Found"
case 418: test.string = "I'm a teapot"
case 500: test.string = "Internal Server Error"
case 200..<400: test.string = "success"
default: test.string = "failure"
}
})
]
7. 使用下标编码解码常量属性
使用
let
声明没有默认值的属性。
struct TestSubscript: Equatable {
let int: Int
let string: String
}
extension TestSubscript: Codable {
enum Keys: CodingKey {
case int, string
}
init(from decoder: Decoder) throws {
int = decoder[Keys.int] ?? 0
string = decoder[Keys.string] ?? ""
}
func encode(to encoder: Encoder) throws {
encoder[Keys.int] = int
encoder[Keys.string] = string
}
}
8. 自定义类型转换
将
KeyedDecodingContainer
扩展到遵循KeyedDecodingContainerCustomTypeConversion
协议,并实现其方法,以解码替换类型并将它们转换为目标类型。
extension KeyedDecodingContainer: KeyedDecodingContainerCustomTypeConversion {
public func decodeForTypeConversion<T, K>(_ container: KeyedDecodingContainer<K>, codingKey: K, as type: T.Type) -> T? where T: Decodable, K: CodingKey {
if type is Double.Type || type is Double?.Type {
if let bool = try? decodeIfPresent(Bool.self, forKey: codingKey as! Self.Key) {
return (bool ? 1.0 : 0.0) as? T
}
}
else if type is Float.Type || type is Float?.Type {
if let bool = try? decodeIfPresent(Bool.self, forKey: codingKey as! Self.Key) {
return (bool ? 1.0 : 0.0) as? T
}
}
return nil
}
}
9. 使用类型推断进行编码解码
let test = TestStruct(int: 100, string: "Continue")
let data = test.encoded() as Data?
let copy1 = data?.decoded() as TestStruct?
let copy2 = TestStruct.decoded(from: data)
XCTAssertEqual(copy1, test)
XCTAssertEqual(copy2, test)
要求
- iOS 8.0+ | tvOS 9.0+ | macOS X 10.10+ | watchOS 2.0+
- Xcode 12.0+
- Swift 5.0+
安装
.package(url: "https://github.com/iwill/ExCodable", from: "0.2")
pod 'ExCodable', '~> 0.2'
- 代码片段
语言:Swift
平台:所有
完成:可序列化
可用性:顶级
<#extension/struct/class#> <#Type#>: ExCodable {
static var <#keyMapping#>: [KeyMap<<#Type#>>] = [
KeyMap(\.<#property#>, to: <#"key"#>),
<#...#>
]
init(from decoder: Decoder) throws {
decode(with: Self.<#keyMapping#>, using: decoder)
}
func encode(to encoder: Encoder) throws {
encode(with: Self.<#keyMapping#>, using: encoder)
}
}
致谢
- John Sundell (@JohnSundell) 以及他从他的 Codextended 中获得的想法
- ibireme (@ibireme) 以及从他他的 YYModel 中获得的功能
- 明先生 (@iwill)
许可协议
ExCodable 采用了 MIT 许可协议。详情请参阅 LICENSE。