CodableWrapper
Codable + PropertyWrapper = @Codec("encoder", "decoder") var cool: Bool = true
中文说明
About
- This project is use
PropertyWrapper
to improve yourCodable
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
- Custom key support
- Fix parsing failure due to missing fields from server
- Fix parsing failure due to mismatch Enum raw value
- Custom transform
Installation
Cocoapods
pod 'CodableWrapper'
Swift Package Manager
https://github.com/winddpan/CodableWrapper
Example
enum Animal: String, Codable {
case dog
case cat
case fish
}
struct ExampleModel: Codable {
@Codec("aString")
var stringVal: String = "scyano"
@Codec("aInt")
var intVal: Int = 123456
@Codec var defaultArray: [Double] = [1.998, 2.998, 3.998]
@Codec var bool: Bool = false
@Codec var unImpl: String?
@Codec var animal: Animal = .dog
}
let json = #"{"aString": "pan", "aInt": "233", "bool": "1", "animal": "cat"}"#
let model = try JSONDecoder().decode(ExampleModel.self, from: json.data(using: .utf8)!)
XCTAssertEqual(model.stringVal, "pan")
XCTAssertEqual(model.intVal, 233)
XCTAssertEqual(model.defaultArray, [1.998, 2.998, 3.998])
XCTAssertEqual(model.bool, true)
XCTAssertEqual(model.unImpl, nil)
XCTAssertEqual(model.animal, .cat)
For more examples, please check the unit tests or Playground
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
}
}
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)
Auto snake camel convert
struct ExampleModel: Codable {
@Codec var snake_string: String = ""
@Codec var camelString: String = ""
}
let json = #"{"snakeString":"snake", "camel_string": "camel"}"#
let model = try JSONDecoder().decode(ExampleModel.self, from: json.data(using: .utf8)!)
XCTAssertEqual(model.snake_string, "snake")
XCTAssertEqual(model.camelString, "camel")
CodingKeys
Decoding: try each CodingKey until succeed Encoding: use first CodingKey as Dictionary key
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)
Basic type bridging
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 toTransfromType
License
Distributed under the MIT License. See LICENSE
for more information.