cod

CodableWrapper

Codable + PropertyWrapper = ☕

Showing:

Popularity

Downloads/wk

0

GitHub Stars

94

Maintenance

Last Commit

5d ago

Contributors

2

Package

Dependencies

0

License

MIT

Categories

Readme

CodableWrapper

Codable + PropertyWrapper = @Codec("encoder", "decoder") var cool: Bool = true

  1. About
  2. Feature
  3. Installation
  4. Example
  5. How it works
  6. Advanced usage
  7. Transformer

About

  • This project is use PropertyWrapper to improve your Codable use experience.
  • Simply based on JSONEncoder JSONDecoder.
  • Powerful and simplifily API than BetterCodable or CodableWrappers.

Feature

  • Default value supported
  • Basic type convertible, between String Bool Number
  • Fix parsing failure due to missing fields from server
  • Fix parsing failure due to mismatch Enum raw value

Installation

Cocoapods

pod 'CodableWrapper'

Swift Package Manager

https://github.com/winddpan/CodableWrapper

Example

struct ExampleModel: Codable {
    @Codec("stringVal", "string_Val") 
    var stringVal: String = "scyano"
  
    @Codec("int_Val", "intVal") 
    var intVal: Int = 123456
  
    @Codec var array: [Double] = [1.998, 2.998, 3.998]
  
    @Codec var bool: Bool = false
  
    @Codec var unImpl: String?
}

let json = """
{"int_Val": "233", "string_Val": "pan", "bool": "1"}
"""

let model = try JSONDecoder().decode(ExampleModel.self, from: json.data(using: .utf8)!)
XCTAssertEqual(model.intVal, 233)
XCTAssertEqual(model.stringVal, "pan")
XCTAssertEqual(model.unImpl, "nil")
XCTAssertEqual(model.array, [1.998, 2.998, 3.998])
XCTAssertEqual(model.bool, true)

For more examples, please check the unit tests

How it works

struct DataModel: Codable {
    @Codec var stringVal: String = "OK"
}

/* pseudocode from Swift open source lib: Codable.Swift -> */
struct DataModel: Codable {
    private var _stringVal = Codec<String>(defaultValue: "OK")

    var stringVal: String {
        get {
            return _stringVal.wrappedValue
        }
        set {
            _stringVal.wrappedValue = newValue
        }
    }

    enum CodingKeys: CodingKey {
        case stringVal
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        /* decode `newStringVal` */
        /* remember `newStringVal`: Thread.current.lastCodableWrapper = wrapper */
        /*
         extension KeyedDecodingContainer {
            func decode<Value>(_ type: Codec<Value>.Type, forKey key: Key) throws -> Codec<Value> {
                ...
                let wrapper = Codec<Value>(unsafed: ())
                Thread.current.lastCodableWrapper = wrapper
                ...
            }
         }
         */
        let newStringVal = try container.decode(Codec<String>.self, forKey: CodingKeys.stringVal)

        /* old `_stringVal` deinit */
        /* old `_stringVal` invokeAfterInjection called: transform old `_stringVal` Configs to `newStringVal` */
        /* 
         deinit {
             if !unsafeCreated, let construct = construct, let lastWrapper = Thread.current.lastCodableWrapper as? Codec<Value> {
                 lastWrapper.invokeAfterInjection(with: construct)
                 Thread.current.lastCodableWrapper = nil
             }
         }
        */
        self._stringVal = newStringVal
    }
}

Advanced usage

DefaultValue

DefaultValue should implement Codable protocol

struct ExampleModel: Codable {
    @Codec var bool: Bool = false
}

let json = """ {"bool":"wrong value"} """

let model = try JSONDecoder().decode(ExampleModel.self, from: json.data(using: .utf8)!) XCTAssertEqual(model.bool, false)


#### CodingKeys 
> While Decoding: try each CodingKey until succeed; while Encoding: use first CodingKey as Dictionary key
```swift
struct ExampleModel: Codable {
    @Codec("int_Val", "intVal")
    var intVal: Int = 123456

    @Codec("intOptional", "int_optional")
    var intOptional: Int?
}

let json = """
{"int_Val": "233", "int_optional": 234}
"""

let model = try JSONDecoder().decode(ExampleModel.self, from: json.data(using: .utf8)!)
XCTAssertEqual(model.intVal, 233)
XCTAssertEqual(model.intOptional, 234)

let data = try JSONEncoder().encode(model)
let jsonObject = try JSONSerialization.jsonObject(with: data, options: []) as! [String: Any]
XCTAssertEqual(jsonObject["int_Val"] as? Int, 233)
XCTAssertEqual(jsonObject["intOptional"] as? Int, 234)

BasicTypeBridge

struct ExampleModel: Codable {
    @Codec var int: Int?
    
    @Codec var string: String?

    @Codec var bool: Bool?
}

let json = """
{"int": "1", "string": 2, "bool": "true"}
"""

let model = try JSONDecoder().decode(ExampleModel.self, from: json.data(using: .utf8)!)
XCTAssertEqual(model.int, 1)
XCTAssertEqual(model.string, "2")
XCTAssertEqual(model.bool, true)

Transformer

struct User: Codable {
    @Codec(transformer: SecondDateTransform())
    var registerDate: Date?
}       
let date = Date()
let json = #" { "sencondsDate": \(date.timeIntervalSince1970) } "#

let user = try JSONDecoder().decode(User.self, from: json.data(using: .utf8)!)
XCTAssertEqual(model.sencondsDate?.timeIntervalSince1970, date.timeIntervalSince1970)

It also support custom transformer, your CustomTransformer only need to comfirm to TransfromType

License

Distributed under the MIT License. See LICENSE for more information.

Rate & Review

Great Documentation0
Easy to Use0
Performant0
Highly Customizable0
Bleeding Edge0
Responsive Maintainers0
Poor Documentation0
Hard to Use0
Slow0
Buggy0
Abandoned0
Unwelcoming Community0
100