ExCodable 1.0.0

ExCodable 1.0.0

MingLQ 维护。



ExCodable 1.0.0

ExCodable

Swift 5.0 Swift Package Manager Platforms Build and Test GitHub Releases (latest SemVer) Deploy to CocoaPods Cocoapods LICENSE @minglq

En | 中文

ExCodable - Swift Codable 的键映射扩展。

内容

特性

  • 扩展 Swift Codable - 支持编码和解码功能;
  • 支持通过 KeyPath 和编码键进行键映射;
    • ExCodable 不会通过不安全的指针读写内存;
    • 无需逐个编码/解码属性;
    • 只需要使用 var 声明属性并提供默认值;
    • 在大多数情况下,不再需要 CodingKey 类型,因为它只使用一次,因此字符串字面量可能更好。
  • 支持对不同数据源进行多个键映射;
  • 支持通过 Array 进行解码,支持多个替代键;
  • 支持使用点语法通过 String 进行嵌套键映射;
  • 支持使用下标进行自定义编码/解码;
  • 支持内置和自定义类型转换;
  • 支持 structclass 和子类;
  • 默认使用 JSON 编码器/解码器,并支持 PList;
  • 使用类型推断,支持 JSON DataStringObject
  • 返回 Optional 值而不是抛出错误,以避免频繁使用 try?

使用方法

1. 为struct进行键映射

需要使用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)
    }
    
}

2. 为class进行键映射

在扩展类中不能采用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)
    }
    
}

致谢

许可协议

ExCodable 采用了 MIT 许可协议。详情请参阅 LICENSE