Skip to content

仓库 files navigation

Requirements

Xcode Minimun 部署 Version
Xcode15+ iOS13+ / macOS11+ 1.0+
Xcode15- iOS13- / macOS11- 0.3.3

关于

The project objective is to enhance the usage experience of the Codable protocol using the macro provided by Swift 5.9 and to address the shortcomings of various official versions.

Feature

  • Default value
  • Basic type automatic convertible, between String Bool Number etc.
  • Custom multiple CodingKey
  • Nested Dictionary CodingKey
  • Automatic compatibility between camel case and snake case
  • Convenience Codable subclass
  • Transformer
  • Decode and encode completion hooks

Installation

CocoaPods

pod 'CodableWrapper', :git => 'https://github.com/winddpan/CodableWrapper.git'

Swift Package Manager

https://github.com/winddpan/CodableWrapper

Example

@Codable
struct BasicModel {
    var defaultVal: String = "hello world"
    var defaultVal2: String = Bool.random() ? "hello world" : ""
    let strict: String
    let noStrict: String?
    let autoConvert: Int?

    @CodingKey("hello")
    var hi: String = "there"

    @CodingNestedKey("nested.hi")
    @CodingTransformer(StringPrefixTransform("HELLO -> "))
    var codingKeySupport: String

    @CodingNestedKey("nested.b")
    var nestedB: String

    var testGetter: String {
        nestedB
    }
}

final class CodableWrapperTests: XCTestCase {
    func testBasicUsage() throws {
        let jsonStr = """
        {
            "strict": "value of strict",
            "autoConvert": "998",
            "nested": {
                "hi": "nested there",
                "b": "b value"
            }
        }
        """

        let model = try JSONDecoder().decode(BasicModel.self, from: jsonStr.data(using: .utf8)!)
        XCTAssertEqual(model.defaultVal, "hello world")
        XCTAssertEqual(model.strict, "value of strict")
        XCTAssertEqual(model.noStrict, nil)
        XCTAssertEqual(model.autoConvert, 998)
        XCTAssertEqual(model.hi, "there")
        XCTAssertEqual(model.codingKeySupport, "HELLO -> nested there")
        XCTAssertEqual(model.nestedB, "b value")

        let encoded = try JSONEncoder().encode(model)
        let dict = try JSONSerialization.jsonObject(with: encoded) as! [String: Any]
        XCTAssertEqual(model.defaultVal, dict["defaultVal"] as! String)
        XCTAssertEqual(model.strict, dict["strict"] as! String)
        XCTAssertNil(dict["noStrict"])
        XCTAssertEqual(model.autoConvert, dict["autoConvert"] as? Int)
        XCTAssertEqual(model.hi, dict["hello"] as! String)
        XCTAssertEqual("nested there", (dict["nested"] as! [String: Any])["hi"] as! String)
        XCTAssertEqual(model.nestedB, (dict["nested"] as! [String: Any])["b"] as! String)
    }
}

Macro usage

@Codable

  • Auto conformance Codable protocol if not explicitly declared

    // both below works well
    
    @Codable
    struct BasicModel {}
    
    @Codable
    struct BasicModel: Codable {}
  • Default value

    @Codable
    struct TestModel {
        let name: String
        var balance: Double = 0
    }
    
    // { "name": "jhon" }
  • Basic type automatic convertible, between String Bool Number etc.

    @Codable
    struct TestModel {
        let autoConvert: Int?
    }
    
    // { "autoConvert": "998" }
  • Automatic compatibility between camel case and snake case

    @Codable
    struct TestModel {
        var userName: String = ""
    }
    
    // { "user_name": "jhon" }
  • Member Wise Init

    @Codable
    public struct TestModel {
        public var userName: String = ""
    
        // Automatic generated
        public init(userName: String = "") {
            self.userName = userName
        }
    }
    @Codable(wiseInit: false)
    public struct TestModel {
        public var userName: String = ""
    
        // Disable WiseInit Automatic generated
    }

@CodingKey

  • Custom CodingKeys

    @Codable
    struct TestModel {
        @CodingKey("u1", "u2", "u9")
        var userName: String = ""
    }
    
    // { "u9": "jhon" }

@CodingKeyIgnored

  • Ignored propery on encode and decode

    struct NonCodable {}
    
    @Codable
    struct TestModel {
        @CodingKeyIgnored
        var nonCodable: NonCodable?
    }

@CodingNestedKey

  • Custom CodingKeys in nested dictionary

    @Codable
    struct TestModel {
        @CodingNestedKey("data.u1", "data.u2", "data.u9")
        var userName: String = ""
    }
    
    // { "data": {"u9": "jhon"} }

@CodableSubclass

  • Automatic generate Codable class's subclass init(from:) and encode(to:) super calls

    @Codable
    class BaseModel {
        let userName: String
    }
    
    @CodableSubclass
    class SubModel: BaseModel {
        let age: Int
    }
    
    // {"user_name": "jhon", "age": 22}

@DecodeEnd / @EncodeEnd

  • Run parameterless instance methods after all properties have been decoded or encoded. Multiple methods run in declaration order. Throwing methods are supported.

    @Codable
    struct TestModel {
        var userName: String = ""
    
        @DecodeEnd
        func finishFirstDecodeStep() {}
    
        @DecodeEnd
        func finishSecondDecodeStep() throws {}
    
        @EncodeEnd
        func finishEncoding() {}
    }

@CodingTransformer

  • Transformer between in Codable / NonCodable model

    struct DateWrapper {
        let timestamp: TimeInterval
    
        var date: Date {
            Date(timeIntervalSince1970: timestamp)
        }
    
        init(timestamp: TimeInterval) {
            self.timestamp = timestamp
        }
    
        static var transformer = TransformOf<DateWrapper, TimeInterval>(fromJSON: { DateWrapper(timestamp: $0 ?? 0) }, toJSON: { $0.timestamp })
    }
    
    @Codable
    struct DateModel {
        @CodingTransformer(DateWrapper.transformer)
        var time: DateWrapper? = DateWrapper(timestamp: 0)
        
        @CodingTransformer(DateWrapper.transformer)
        var time1: DateWrapper = DateWrapper(timestamp: 0)
        
        @CodingTransformer(DateWrapper.transformer)
        var time2: DateWrapper?
    }
    
    class TransformTest: XCTestCase {
        func testDateModel() throws {
            let json = """
            {"time": 12345}
            """
    
            let model = try JSONDecoder().decode(DateModel.self, from: json.data(using: .utf8)!)
            XCTAssertEqual(model.time?.timestamp, 12345)
            XCTAssertEqual(model.time?.date.description, "1970-01-01 03:25:45 +0000")
    
            let encode = try JSONEncoder().encode(model)
            let jsonObject = try JSONSerialization.jsonObject(with: encode, options: []) as! [String: Any]
            XCTAssertEqual(jsonObject["time"] as! TimeInterval, 12345)
        }
    }

Star History

Star History Chart

关于

@codingkey("encoder", "decoder") var cool: Bool = true

Topics

Resources

Stars

388 stars

关注者

6 watching

复刻s

发布

贡献者

Languages