CodableWrappers 2.0.7

CodableWrappers 2.0.7

PJ Fechner维护。



  • GottaGetSwifty

CodableWrappers

Swift Package Manager Platforms Build Status

使用《属性包装器》简化序列化(属性包装器)

将您的Codable和(编码/解码)定制移动到注释中!

struct YourType: Codable {
    @MillisecondsSince1970DateCoding
    var millisecondsDate: Date
    @Base64Coding
    var someData: Data
    @OmitCoding
    var temporaryProperty
}

2.0版本已发布! 发行说明


安装

Swift 包管理器 *首选*

URL

https://github.com/GottaGetSwifty/CodableWrappers.git

清单

dependencies: [
    .package(url: "https://github.com/GottaGetSwifty/CodableWrappers.git", .upToNextMajor(from: "2.0.0" )),
]

CocoaPods

pod 'CodableWrappers', '~> 2.0.0'

信息

可用的属性包装器

其他自定义

额外链接


优点

  • 声明式
  • 可扩展性
  • 为所有编码器和解码器声明一次。(例如 JSONEncoder 和 PropertyListEncoder)
  • 允许自定义(编/解码),而无需在您的整个类型中重写 encode(to: Encoder)init(with decoder)
  • 允许多样的(编/解码)策略
  • 跨平台

兼容性

2.x 的最低要求是 Swift 5.2,而 5.1 在 1.x 版本上可用


@EncodeNulls

对于应该编码 nullnil 值的属性

struct MyType: Codable {
    @EncodeNulls
    var myText: String? // Will not be omitted when nil, e.g. will encode to `null` in JSON and `$null` in PLISTs
}

损耗集合

@LossyArrayDecoding
@LossyDictionaryDecoding
@LossySetDecoding

在解码时过滤空值而不抛出错误

private struct LossyCollectionModel: Codable, Equatable {
    @LossyArrayDecoding
    var array: [String] // Ignores null values without throwing an Error
    @LossyDictionaryDecoding
    var dictionary: [String:String] // Ignores null values without throwing an Error
    @LossySetDecoding
    var set: Set<String> // Ignores null values without throwing an Error
}

空默认值

当您想要编码/解码一个空值而不是解码 nil 或者省略编码时

struct MyType: Codable {
    @FallbackEncoding<EmptyInt>
    var int: Int? // will encode `0` when nil
    @FallbackDecoding<EmptyString>
    var string: String // will decode to "" when value was missing/nil
    @FallbackCoding<EmptyArray>
    var array: [Int]? // will encode/decode to [] when missing/nil
}
所有空值
EmptyBool
EmptyString
EmptyInt
EmptyInt16
EmptyInt32
EmptyInt64
EmptyInt8
EmptyUInt
EmptyUInt16
EmptyUInt32
EmptyUInt64
EmptyUInt8
EmptyCGFloat
EmptyDouble
EmptyFloat
EmptyFloat16
EmptyArray
EmptyDictionary
EmptySet

大多数典型 Foundation 类型都提供了 Empty 默认值

其他后备

任何其他类型的默认值都可以通过自定义的 FallbackValueProvider 提供

public struct DistantFutureDateProvider: FallbackValueProvider {
    public static var defaultValue: Date { Date.distantFuture }
}

struct MyType: Codable {
    @FallbackEncoding<DistantFutureDateProvider>
    var updatedDate: Date?
}

@OmitCoding

对于您想要在编码/解码时忽略的属性

struct MyType: Codable {
    @OmitCoding
    var myText: String? // Never encodes and ignores a value if one is in decoded data.
}

@Base64Coding

对于应序列化为Base64编码字符串的数据属性

struct MyType: Codable {
    @Base64Coding
    var myData: Data // Now encodes to a Base64 String
}

@SecondsSince1970DateCoding

对于应序列化为SecondsSince1970的日期属性

struct MyType: Codable {
    @SecondsSince1970DateCoding
    var myDate: Date // Now encodes to SecondsSince1970
}

@MillisecondsSince1970DateCoding

对于应序列化为MillisecondsSince1970的日期属性

struct MyType: Codable {
    @MillisecondsSince1970DateCoding
    var myDate: Date // Now encodes to MillisecondsSince1970
}

@DateFormatterCoding<DateFormatterStaticCoder>

对于其他日期格式,创建一个遵守DateFormatterStaticCoder协议的Type,并使用便利的@DateFormatterCoding typealias@CodingUses<StaticCoder>

struct MyCustomDateCoder: DateFormatterStaticCoder {
    static let dateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "MM:dd:yy H:mm:ss"
        return formatter
    }()
}

struct MyType: Codable {
    @DateFormatterCoding<MyCustomDateCoder>
    var myDate: Date // Now encodes to the format: "MM:dd:yy H:mm:ss"
}

@ISO8601DateCoding

对于应使用ISO8601DateFormatter序列化的日期属性

struct MyType: Codable {
    @ISO8601DateCoding
    var myDate: Date // Now encodes to ISO8601
}

@ISO8601DateFormatterCoding<ISO8601DateFormatterStaticCoder>

对于其他日期格式,创建一个符合 ISO8601DateFormatterStaticCoder 协议的类型,并使用便利的 @ISO8601DateFormatterCoding typealias@CodingUses<StaticCoder>

@available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *)
struct MyCustomISO8601DateFormatter: ISO8601DateFormatterStaticCoder {
    static let iso8601DateFormatter: ISO8601DateFormatter = {
        let formatter = ISO8601DateFormatter()
        formatter.formatOptions = [.withInternetDateTime, .withDashSeparatorInDate]
        return formatter
    }()
}


struct MyType: Codable {
    @ISO8601DateFormatterCoding<MyCustomISO8601DateFormatter>
    var myDate: Date // Now encodes with MyCustomISO8601DateFormatter's formatter
}

@NonConformingFloatCoding<ValueProvider>

当使用不一致的Float时,创建一个符合 NonConformingDecimalValueProvider 的类型,并使用 @NonConformingFloatCoding<NonConformingDecimalValueProvider>

struct MyNonConformingValueProvider: NonConformingDecimalValueProvider {
    static var positiveInfinity: String = "100"
    static var negativeInfinity: String = "-100"
    static var nan: String = "-1"
}

struct MyType: Codable {
    @NonConformingFloatCoding<MyNonConformingValueProvider>
    var myFloat: Float // Now encodes with the MyNonConformingValueProvider values for infinity/NaN
}

@NonConformingDoubleCoding<ValueProvider>

当使用不一致的Double时,创建一个符合 NonConformingDecimalValueProvider 的类型,并使用 @NonConformingDoubleCoding<NonConformingDecimalValueProvider>

struct MyNonConformingValueProvider: NonConformingDecimalValueProvider {
    static var positiveInfinity: String = "100"
    static var negativeInfinity: String = "-100"
    static var nan: String = "-1"
}

struct MyType: Codable {
    @NonConformingDoubleCoding<MyNonConformingValueProvider>
    var myFloat: Float // Now encodes with the MyNonConformingValueProvider values for infinity/NaN
}

Bool编码

有时API会使用整数(Int)或字符串(String)来表示布尔值。

@BoolAsStringCoding

struct MyType: Codable {
    @BoolAsStringCoding
    var myBool: Bool // Now encodes/decodes as a String. `"true"` for `true` and `"false"` for `false`. (Values are lower-cased before decoding)
}

@BoolAsIntCoding

struct MyType: Codable {
    @BoolAsIntCoding
    var myBool: Bool // Now encodes/decodes as an Int. `1` for `true` and `0` for `false`.
}

其他自定义

该架构考虑到扩展性,实施自定义编码就像遵守 StaticCoder 协议 一样简单。然后,只需在属性中添加 @CodingUses<YourCustomCoder>,或者创建一个 typealias 使其更简洁:typealias YourCustomCoding = CodingUses<YourCustomCoder>

实际上,所有包含的包装器都是这样构建的!

完整示例

struct NanosecondsSince9170Coder: StaticCoder {

    static func decode(from decoder: Decoder) throws -> Date {
        let nanoSeconds = try Double(from: decoder)
        let seconds = nanoSeconds * 0.000000001
        return Date(secondsSince1970: seconds)
    }

    static func encode(value: Date, to encoder: Encoder) throws {
        let nanoSeconds = value.secondsSince1970 / 0.000000001
        return try nanoSeconds.encode(to: encoder)
    }
}

// Approach 1: CustomCoding
struct MyType: Codable {
    @CodingUses<NanosecondsSince9170Coder>
    var myData: Date // Now uses the NanosecondsSince9170Coder for serialization
}

// Approach 2: CustomEncoding Property Wrapper typealias

typealias NanosecondsSince9170Coding = CodingUses<NanosecondsSince9170Coder>

struct MyType: Codable {
    @NanosecondsSince9170Coding
    var myData: Date // Now uses the NanosecondsSince9170Coder for serialization
}

请参阅 这些其他示例,以了解其他可能性。


属性可变性

在 2.0 版本中,所有包装器默认都是可变的,可以通过属性包装器组合使其不可变。

struct MyType: Codable {
    @Immutable @SecondsSince1970DateCoding
    var createdAt: Date

    @SecondsSince1970DateCoding
    var updatedAt: Date

    mutating func update() {
        createdAt = Date() // ERROR - Cannot assign to property: 'createdAt' is a get-only property
        updatedAt = Date() // Works!
    }
}

可选

2.0 版本引入了 @OptionalCoding<StaticCodingWrapper> 以使属性启用可选。

struct MyType: Codable {
    @SecondsSince1970DateCoding
    var createdAt: Date

    @OptionalCoding<SecondsSince1970DateCoding>
    var updatedAt: Date?
}

仅编码或解码

有时你可能只能或只想实现编码或解码。

为了启用这一点,(在可能的情况下),所有的包装器都有编码和解码变体。

将编码器改为编码器/解码器或编码改为编码/解码以实现仅一种 例如,@Base64Encoding@SecondsSince1970DateDecoding@EncodingUses<ACustomEncoder> 等。

struct MyType: Encodable {
    @SecondsSince1970DateEncoding
    var myDate: Date
}

struct MyType: Decodable {
    @SecondsSince1970DateDecoding
    var myDate: Date
}

贡献

如果你可以添加一个标准的序列化策略,请随意打开一个请求此功能的 issue 并/或提交一个包含新选项的 pull request。