CodableWrappers
使用属性包装器简化序列化
通过声明式注解轻松实现复杂的Codable序列化!
@CustomCodable @SnakeCase
struct User: Codable {
let firstName: String
let lastName: String
@SecondsSince1970DateCoding
var joinDate: Date
@CustomCodingKey("data")
var imageData: Data
}
3.0版本已发布!发布说明
文档
感谢Swift Package Index提供的完整DocC文档!
安装
Swift Package Manager 推荐
URL:
https://github.com/GottaGetSwifty/CodableWrappers.git
清单:
dependencies: [
.package(url: "https://github.com/GottaGetSwifty/CodableWrappers.git", .upToNextMajor(from: "3.0.0" )),
]
CocoaPods
pod 'CodableWrappers', '~> 3.0.0'
可用的CodingKey宏
- @CustomCodable 3.0新增!
- @CustomCodingKey(String) 3.0新增!
- @CodingKeyPrefix(String) 3.0新增!
- @CodingKeySuffix(String) 3.0新增!
- @CamelCase 3.0新增!
- @FlatCase 3.0新增!
- @PascalCase 3.0新增!
- @UpperCase 3.0新增!
- @SnakeCase 3.0新增!
- @CamelSnakeCase 3.0新增!
- @PascalSnakeCase 3.0新增!
- @ScreamingSnakeCase 3.0新增!
- @KebabCase 3.0新增!
- @CamelKebabCase 3.0新增!
- @PascalKebabCase 3.0新增!
- @ScreamingKebabCase 3.0新增!
可用的属性包装器
- @EncodeNulls
- 有损集合
- 空默认值
- 其他回退值
- @OmitCoding
- @Base64Coding
- @SecondsSince1970DateCoding
- @MillisecondsSince1970DateCoding
- @DateFormatterCoding<DateFormatterStaticCoder>
- @ISO8601DateCoding
- @ISO8601DateFormatterCoding<ISO8601DateFormatterStaticCoder>
- @NonConformingFloatCoding<ValueProvider>
- @NonConformingDoubleCoding<ValueProvider>
- 布尔值编码
- 额外自定义
- 属性可变性
- 仅编码或解码
其他自定义
其他链接
@CustomCodable
自定义CodingKeys的前提条件
@Codable
struct MyType: Codable {
}
@CustomCodingKey(String)
为属性的CodingKey使用自定义字符串值
@CustomCodable
struct YourType: Codable {
@CodingKey("your-Custom_naming")
let firstProperty: String // 编码键将为 "your-Custom_naming"
}
CodingKeyPrefix(String)
使用自定义值设置属性的CodingKey前缀
@CustomCodable
struct YourType: Codable {
@CodingKeyPrefix("beta-")
let firstProperty: String // CodingKey将为 "beta-firstProperty"
}
@CustomCodable @CodingKeyPrefix("beta-")
struct YourType: Codable {
let firstProperty: String // CodingKey将为 "beta-firstProperty"
}
CodingKeySuffix(String)
为属性的CodingKey或类型的CodingKeys添加自定义后缀
@CustomCodable
struct YourType: Codable {
@CodingKeySuffix("-beta")
let firstProperty: String // CodingKey将为 "firstProperty-beta"
}
@CustomCodable @CodingKeySuffix("-beta")
struct YourType: Codable {
let firstProperty: String // CodingKey将为 "firstProperty-beta"
}
CamelCase
将属性或类型的CodingKey设为camelCase
@CustomCodable
struct YourType: Codable {
@CamelCase
let first-property: String // CodingKey将为 "firstProperty"
}
@CustomCodable @CamelCase
struct YourType: Codable {
let first-property: String // CodingKey将为 "firstProperty"
}
@FlatCase
将属性或类型的CodingKey设为flatcase
@CustomCodable
struct YourType: Codable {
@FlatCase
let firstProperty: String // CodingKey将为 "firstproperty"
}
@CustomCodable @FlatCase
struct YourType: Codable {
let firstProperty: String // CodingKey将为 "firstproperty"
}
@PascalCase
将属性或类型的CodingKey设为PascalCase
@CustomCodable
struct YourType: Codable {
@PascalCase
let firstProperty: String // CodingKey将为 "FirstProperty"
}
@CustomCodable @PascalCase
struct YourType: Codable {
let firstProperty: String // CodingKey将为 "FirstProperty"
}
@UpperCase
将属性或类型的CodingKey设为UPPERCASE
@CustomCodable
struct YourType: Codable {
@UpperCase
let firstProperty: String // CodingKey将为 "FIRSTPROPERTY"
}
@CustomCodable @UpperCase
struct YourType: Codable {
let firstProperty: String // CodingKey将为 "FIRSTPROPERTY"
}
@SnakeCase
将属性或类型的CodingKey设为snake_case
@CustomCodable
struct YourType: Codable {
@SnakeCase
let firstProperty: String // CodingKey将为 "first_property"
}
@CustomCodable @SnakeCase
struct YourType: Codable {
let firstProperty: String // CodingKey将为 "first_property"
}
@CamelSnakeCase
将属性或类型的CodingKey设为camel_Snake_Case
@CustomCodable
struct YourType: Codable {
@CamelSnakeCase
let firstProperty: String // CodingKey 将会是 "first_Property"
}
@CustomCodable @CamelSnakeCase
struct YourType: Codable {
let firstProperty: String // CodingKey 将会是 "first_Property"
}
@PascalSnakeCase
将属性或类型的 CodingKey 设为 Pascal_Snake_Case
@CustomCodable
struct YourType: Codable {
@PascalSnakeCase
let firstProperty: String // CodingKey 将会是 "First_Property"
}
@CustomCodable @PascalSnakeCase
struct YourType: Codable {
let firstProperty: String // CodingKey 将会是 "First_Property"
}
@ScreamingSnakeCase
将属性或类型的 CodingKey 设为 SCREAMING_SNAKE_CASE
@CustomCodable
struct YourType: Codable {
@ScreamingSnakeCase
let firstProperty: String // CodingKey 将会是 "FIRST_PROPERTY"
}
@CustomCodable @ScreamingSnakeCase
struct YourType: Codable {
let firstProperty: String // CodingKey 将会是 "FIRST_PROPERTY"
}
@KebabCase
将属性或类型的 CodingKey 设为 kebab-case
@CustomCodable
struct YourType: Codable {
@KebabCase
let firstProperty: String // CodingKey 将会是 "first-property"
}
@CustomCodable @KebabCase
struct YourType: Codable {
let firstProperty: String // CodingKey 将会是 "first-property"
}
@CamelKebabCase
将属性或类型的 CodingKey 设为 camel-Kebab-Case
@CustomCodable
struct YourType: Codable {
@CamelKebabCase
let firstProperty: String // CodingKey 将会是 "first-Property"
}
@CustomCodable @CamelKebabCase
struct YourType: Codable {
let firstProperty: String // CodingKey 将会是 "first-Property"
}
@PascalKebabCase
将属性或类型的 CodingKey 设为 Pascal-Kebab-Case
@CustomCodable
struct YourType: Codable {
@PascalKebabCase
let firstProperty: String // CodingKey 将会是 "First-Property"
}
@CustomCodable @PascalKebabCase
struct YourType: Codable {
let firstProperty: String // CodingKey 将会是 "First-Property"
}
@ScreamingKebabCase
将属性或类型的 CodingKey 设为 SCREAMING-KEBAB-CASE
@CustomCodable
struct YourType: Codable {
@ScreamingKebabCase
let firstProperty: String // CodingKey 将会是 "FIRST-PROPERTY"
}
@CustomCodable @ScreamingKebabCase
struct YourType: Codable {
let firstProperty: String // CodingKey 将会是 "FIRST-PROPERTY"
}
@EncodeNulls
用于应该为 nil
值编码 null
的属性
struct MyType: Codable {
@EncodeNulls
var myText: String? // 当为nil时不会被省略,例如在JSON中会编码为`null`,在PLIST中会编码为`$null`
}
有损集合
@LossyArrayDecoding
@LossyDictionaryDecoding
@LossySetDecoding
在解码过程中过滤掉空值而不抛出错误
private struct LossyCollectionModel: Codable, Equatable {
@LossyArrayDecoding
var array: [String] // 忽略空值而不抛出错误
@LossyDictionaryDecoding
var dictionary: [String:String] // 忽略空值而不抛出错误
@LossySetDecoding
var set: Set<String> // 忽略空值而不抛出错误
}
空值默认
当你想要编码/解码一个空值而不是解码为nil或省略编码时
struct MyType: Codable {
@FallbackEncoding<EmptyInt>
var int: Int? // 当为nil时会编码为`0`
@FallbackDecoding<EmptyString>
var string: String // 当值缺失/为nil时会解码为""
@FallbackCoding<EmptyArray>
var array: [Int]? // 当缺失/为nil时会编码/解码为[]
}
所有空值
BoolTrue
BoolFalse
EmptyBool
EmptyString
EmptyInt
EmptyInt16
EmptyInt32
EmptyInt64
EmptyInt8
EmptyUInt
EmptyUInt16
EmptyUInt32
EmptyUInt64
EmptyUInt8
EmptyCGFloat
EmptyDouble
EmptyFloat
EmptyFloat16
EmptyArray
EmptyDictionary
EmptySet
Empty
默认值可用于大多数典型的Foundation类型
其他回退值
任何其他类型的默认值可以通过自定义 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? // 永远不会编码,并且在解码数据中存在值时会忽略它
}
@Base64Coding
用于应该序列化为Base64编码字符串的Data属性
struct MyType: Codable {
@Base64Coding
var myData: Data // 现在会编码为Base64字符串
}
@SecondsSince1970DateCoding
用于应该序列化为1970年以来秒数的Date属性
struct MyType: Codable {
@SecondsSince1970DateCoding
var myDate: Date // 现在会编码为1970年以来的秒数
}
@MillisecondsSince1970DateCoding
用于应该序列化为1970年以来毫秒数的Date属性
struct MyType: Codable {
@MillisecondsSince1970DateCoding
var myDate: Date // 现在会编码为1970年以来的毫秒数
}
@DateFormatterCoding<DateFormatterStaticCoder>
对于其他日期格式,创建一个遵循 DateFormatterStaticCoder
协议的类型,并使用便利的 @DateFormatterCoding
类型别名或 @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 // 现在会编码为格式:"MM:dd:yy H:mm:ss"
}
@ISO8601DateCoding
用于应该使用ISO8601DateFormatter序列化的Date属性
struct MyType: Codable {
@ISO8601DateCoding
var myDate: Date // 现在会编码为ISO8601格式
}
@ISO8601DateFormatterCoding<ISO8601DateFormatterStaticCoder>
对于其他日期格式,创建一个遵循 ISO8601DateFormatterStaticCoder
协议的类型,并使用便利的 @ISO8601DateFormatterCoding
类型别名或 @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 // 现在会使用MyCustomISO8601DateFormatter的格式化器进行编码
}
@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 // 现在会使用MyNonConformingValueProvider的值来编码无穷大/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 // 现在使用MyNonConformingValueProvider的值来编码无穷大/NaN
}
布尔值编码
有时API会使用Int
或String
来表示布尔值。
@BoolAsStringCoding
struct MyType: Codable {
@BoolAsStringCoding
var myBool: Bool // 现在以字符串形式编码/解码。`true`编码为`"true"`,`false`编码为`"false"`。(解码前值会转为小写)
}
@BoolAsIntCoding
struct MyType: Codable {
@BoolAsIntCoding
var myBool: Bool // 现在以整数形式编码/解码。`true`编码为`1`,`false`编码为`0`。
}
额外自定义
该架构在设计时考虑了可扩展性,实现自定义编码只需遵循StaticCoder
协议即可。然后只需在属性上添加@CodingUses<YourCustomCoder>
,或创建一个类型别名使其更简洁: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)
}
}
// 方法1:CustomCoding
struct MyType: Codable {
@CodingUses<NanosecondsSince9170Coder>
var myData: Date // 现在使用NanosecondsSince9170Coder进行序列化
}
// 方法2:CustomEncoding属性包装器类型别名
typealias NanosecondsSince9170Coding = CodingUses<NanosecondsSince9170Coder>
struct MyType: Codable {
@NanosecondsSince9170Coding
var myData: Date // 现在使用NanosecondsSince9170Coder进行序列化
}
查看这些其他示例以了解更多可能性。
属性可变性
在2.0版本中,所有包装器默认都是可变的,可以通过属性包装器组合使其不可变
struct MyType: Codable {
@Immutable @SecondsSince1970DateCoding
var createdAt: Date
@SecondsSince1970DateCoding
var updatedAt: Date
mutating func update() {
createdAt = Date() // 错误 - 无法赋值给属性:'createdAt'是只读属性
updatedAt = Date() // 正常工作!
}
}
可选类型
2.0版本引入了@OptionalCoding<StaticCodingWrapper>
来支持属性的可选类型。
struct MyType: Codable {
@SecondsSince1970DateCoding
var createdAt: Date
@OptionalCoding<SecondsSince1970DateCoding>
var updatedAt: Date?
}
仅编码或解码
有时你可能只想或只能实现编码或解码。
为了支持这一点,(在实际可行的情况下)所有包含的包装器都有编码和解码变体
将Coder改为Encoder/Decoder或将Coding改为Encoding/Decoding以仅实现其中一个
例如:@Base64Encoding
、@SecondsSince1970DateDecoding
、@EncodingUses<ACustomEncoder>
等。
struct MyType: Encodable {
@SecondsSince1970DateEncoding
var myDate: Date
}
struct MyType: Decodable {
@SecondsSince1970DateDecoding
var myDate: Date
}
兼容性
- 3.x支持Swift 5.9
- 2.x支持Swift 5.2
- 1.x支持Swift 5.1
贡献
如果有标准的序列化或编码键策略可以添加,欢迎开启一个问题请求或提交包含新选项的拉取请求。