CodableWrappers
(属性包装器)
使用《属性包装器》简化序列化将您的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'
信息
可用的属性包装器
- @EncodeNulls *2.0版本新功能*
- LossyCollections *2.0版本新功能*
- EmptyDefaults *2.0版本新功能*
- OtherFallbacks *2.0版本新功能*
- @OmitCoding
- @Base64Coding
- @SecondsSince1970DateCoding
- @MillisecondsSince1970DateCoding
- @DateFormatterCoding
- @ISO8601DateCoding
- @ISO8601DateFormatterCoding
- @NonConformingFloatCoding< ValueProvider >
- @NonConformingDoubleCoding< ValueProvider >
- 布尔编码
- 额外自定义
- 属性可变性
- 仅编码或解码
其他自定义
- PropertyMutability *2.0版本新功能*
- Optionals *2.0版本新功能*
- 仅编码或解码
额外链接
优点
- 声明式
- 可扩展性
- 为所有编码器和解码器声明一次。(例如 JSONEncoder 和 PropertyListEncoder)
- 允许自定义(编/解码),而无需在您的整个类型中重写
encode(to: Encoder)
或init(with decoder)
- 允许多样的(编/解码)策略
- 跨平台
兼容性
2.x 的最低要求是 Swift 5.2,而 5.1 在 1.x 版本上可用
@EncodeNulls
对于应该编码 null
的 nil
值的属性
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。