CodableKit

一个为 Codable 提供额外功能的 Swift 库。 当您需要从服务器解码数据,但不确定数据是否正确时,CodableKit 非常有用。

LossyValue

允许您解码格式部分错误或值不正确的字段。 如果字段不正确,该值将为 nil。

enum Payment: String, Codable, Equatable {
    case newCard = "NewCard"
    case applePay = "ApplePay"
}

private struct WrappedUser: Codable, Equatable {
    let name: String

    @LossyValue
    var payment: Payment? <<-------------- LossyValue behavior
}

private struct SDKUser: Codable, Equatable {
    let name: String
    let payment: Payment? <<-------------- SDK behavior
}

func test_when_decoding_data_is_valid() {
    let json = [
        "name": "bob",
        "payment": "NewCard"
    ]
    let wrappedUser: WrappedUser? = subjectAction(json)
    let expectedUser = WrappedUser(name: "bob", payment: .newCard)
    XCTAssertEqual(wrappedUser, expectedUser)

    let sdkUser: SDKUser? = subjectAction(json)
    let expectedSdkUser = SDKUser(name: "bob", payment: .newCard)
    XCTAssertEqual(sdkUser, expectedSdkUser)
}

func test_when_decoding_data_is_invalid() {
    let json = [
        "name": "bob",
        "payment": "GooglePay"
    ]
    let wrappedUser: WrappedUser? = subjectAction(json)
    let expectedUser = WrappedUser(name: "bob", payment: nil) <<-------------- LossyValue behavior
    XCTAssertEqual(wrappedUser, expectedUser)

    let sdkUser: SDKUser? = subjectAction(json) <<-------------- SDK behavior
    XCTAssertNil(sdkUser)
}

func test_when_decoding_data_is_lack() {
    let json = [
        "name": "bob",
        // payment field is required
        "other name of field": "NewCard"
    ]
    let wrappedUser: WrappedUser? = subjectAction(json) <<-------------- LossyValue behavior
    XCTAssertNil(wrappedUser)

    let sdkUser: SDKUser? = subjectAction(json)
    let expectedSdkUser = SDKUser(name: "bob", payment: nil) <<-------------- SDK behavior
    XCTAssertEqual(sdkUser, expectedSdkUser)
}

LossyArray/LossyDictionary/LossySet

允许您解码包含部分不正确数据的数组或字典。 如果数据不正确,该值将从结果中省略。

private struct User: Decodable, Equatable {
    enum Payment: String, Decodable, Equatable {
        case newCard = "NewCard"
        case applePay = "ApplePay"
    }

    let name: String

    @LossyArray
    var payments: [Payment]
}

let subject: User? = subjectAction([
    "name": "bob",
    "payments": [
        "NewCard", "GooglePay"
    ]
])
let expectedUser = User(name: "bob", payments: [.newCard]) <<-------------- unknown "GooglePay" is omitted
XCTAssertEqual(subject, expectedUser)

Nullable

允许您在服务器需要时,将 'nil' 字段编码为 JSON 中的 'null'。

private struct User: Encodable, Equatable {
    enum Payment: String, Encodable, Equatable {
        case newCard = "NewCard"
        case applePay = "ApplePay"
    }

    let name: String

    @Nullable
    var payments: Payment?

    /// absent in JSON when value is `nil`
    var payments2: Payment?
}

func test_common() throws {
    let subject: User = .init(name: "bob",
                              payments: .applePay,
                              payments2: nil)
    let data = try Data.make(from: subject)
    let str = String(data: data, encoding: .utf8)
    let expected =
        """
        {
          "name" : "bob",
          "payments" : "ApplePay"
        }
        """
    XCTAssertEqual(str, expected, str ?? "")
}

func test_null() throws {
    let subject: User = .init(name: "bob",
                              payments: nil,
                              payments2: nil)
    let data = try Data.make(from: subject)
    let str = String(data: data, encoding: .utf8)
    let expected =
        """
        {
          "name" : "bob",
          "payments" : null  <<-------------- `null` instead of absent
        }
        """
    XCTAssertEqual(str, expected, str ?? "")
}