HollowCodable
HollowCodable 是一个基于Swift使用属性包装器的对Codable协议进行扩展的框架。
- 使用属性包装器使复杂的可编码序列化变得轻而易举!
原生 JSON Codable 的问题
- 不支持自定义某个属性的 coding key, 一旦你有这种需求, 要么把所有的 coding key 手动实现一遍去修改想要的 coding key, 要么就得在 decode 的时候去设置 Decoder , 极其不方便
- 不支持忽略掉某些不能 Codable 的属性, 还是需要手动实现 coding key 才行
- decode 的时候不支持多个 coding key 映射同一个属性
- 不能使用模型的默认值, 当 decode 的数据缺失时无法使用定义里的默认值而是 throw 数据缺失错误, 这个设计导致例如版本更新后, 服务端删掉了模型的某个过期字段, 然后旧版本 app 都会陷入错误, 即使不用这个字段旧版本客户端依旧是能正常工作的(只是无效的数据显示缺失而已), 这很明显是不合理的.
- 不支持简单的类型转换, 比如转换 0/1 到 false/true, "123" 到 Int的123 或者反过来, 谁又能确保服务端的人员不会失手修改了字段类型导致 app 端故障呢?
ok, 以上问题在你使用HollowCodable之后统统可以得到解决,不用谢!
特性
-
基于Codable的扩展,采用属性包装器设计
-
支持属性键名称修改和多键映射到同一属性
-
JSON映射为Model,Model映射为JSON
-
支持嵌套对象,支持Any对象
-
支持nil对象
-
支持默认值
-
支持数组、字典默认值
-
支持String和整数、浮点数、Bool之间的转换
-
支持自定义解码器和编码器
-
Immutable: 通过属性包装使其变成只读属性,它可以与其他编码一起使用。
swift
struct YourModel: HollowCodable {
@Immutable
var id: Int
@Immutable @BoolCoding
var bar: Bool?
}
- IgnoredKey: 将此属性添加到可选属性中,以便在编码或解码时不包含它。
swift
struct YourModel: HollowCodable {
@IgnoredKey
var ignorKey: String? = "Condy" // 相当于使用 `lazy`.
lazy var ignorKey2: String? = "Coi"
}
- DefaultBacked: 为常见类型设置不同的默认值。
- 这个库内置了许多默认值,如Int, Bool, String, Array...
- 如果我们想为该字段设置不同的默认值。
swift
struct YourModel: HollowCodable {
@DefaultBacked<Int>
var val: Int // 如果该字段不是可选的,则默认为0。
@DefaultBacked<Bool>
var boo: Bool // 如果该字段不是可选的,则默认为false。
@DefaultBacked<String>
var string: String // 如果该字段不是可选的,则默认为""。
@DefaultBacked<AnyDictionary>
var list: [String: Any] // 如果该字段不是可选的,则默认为[:]。
}
UIColor/NSColor
- HexColorCoding: 将十六进制字符串颜色转化为UIColor/NSColor属性包装器。
- 支持十六进制字符串颜色格式有
#RGB
、#RGBA
、#RRGGBB
、#RRGGBBAA
.
- 支持十六进制字符串颜色格式有
- RGBColorCoding: 将Red、Green、Blue颜色转化为UIColor/NSColor属性包装器。
- RGBAColorCoding: 将Red、Green、Blue和Alpha颜色转化为UIColor/NSColor属性包装器。
swift
struct YourModel: HollowCodable {
@HexColorCoding
var color: HollowColor?
@RGBColorCoding
var background_color: HollowColor?
}
Bool
- BoolCoding: 支持使用
Bool
,Int
或String
来表示布尔值。- 大于0则转化为true,小于等于0则转化为false。
- 这些数据 "true"/"yes"/"y"/"t"/"1"/">0" 则转为true,
- 这些数据 "false"/"no"/"f"/"n"/"0" 则转为false.
- FalseBoolCoding: 如果字段不是可选类型,则默认为false。
- TrueBoolCoding: 如果字段不是可选类型,则默认为true。
swift
struct YourModel: HollowCodable {
@Immutable @BoolCoding
var bar: Bool?
@FalseBoolCoding
var hasD: Bool
@TrueBoolCoding
var hasT: Bool
}
Data
- Base64Coding: 对于应序列化为Base64编码字符串的Data属性。
swift
struct YourModel: HollowCodable {
@Base64Coding
var base64Data: Data?
}
您可以自定义您自己的数据(反)序列化类型DataValue.
Like:
swift
public enum YourData: DataConverter {
public typealias Value = String
public typealias FromValue = Data
public typealias ToValue = String
public static let hasValue: Value = ""
public static func transformToValue(with value: FromValue) -> ToValue? {
// data to string..
}
public static func transformFromValue(with value: ToValue) -> FromValue? {
// string to data..
}
}
Used:
struct YourModel: HollowCodable {
AnyBacked<DataValue<YourData>>
var data: Data?
}
Date
- ISO8601DateCoding: 将
String
或TimeInterval
值解码为ISO8601日期。 - RFC2822DateCoding: 将
String
或TimeInterval
值解码为RFC2822日期。 - RFC3339DateCoding: 将
String
或TimeInterval
值解码为RFC3339日期。
swift
struct YourModel: HollowCodable {
@ISO8601DateCoding
var iso8601: Date? // 现在解码到ISO8601日期。
@RFC2822DateCoding
var data1: Date? // 现在解码到RFC2822日期。
@RFC3339DateCoding
var data2: Date? // 现在解码到RFC3339日期。
}
- SecondsSince1970DateCoding: 对于应该序列化为SecondsSince1970的Date时间戳属性。
- MillisecondsSince1970DateCoding: 对于应该被序列化为msecondssince1970的Date时间戳属性。
swift
struct YourModel: HollowCodable {
@SecondsSince1970DateCoding
var timestamp: Date // 现在编码为SecondsSince1970
@MillisecondsSince1970DateCoding
var timestamp2: Date // 现在编码为MillisecondsSince1970
}
支持使用不同格式的(反)序列化,例如:
swift
struct YourModel: HollowCodable {
@DateCoding<Hollow.DateFormat.yyyy_mm_dd, Hollow.Timestamp.secondsSince1970>
var time: Date? // 解码使用`yyyy-MM-dd`格式和编码使用`seconds`时间戳。
}
Enum
- EnumCoding: 要实现可转换,enum必须符合rawrepresable协议。现在没有什么特别需要做的。
swift
struct YourModel: HollowCodable {
@EnumCoding<AnimalType>
var animal: AnimalType? //
}
enum AnimalType: String {
case Cat = "cat"
case Dog = "dog"
case Bird = "bird"
}
NSDecimalNumber
- NSDecimalNumberCoding: 可以将
String
、Double
、Float
、CGFloat
、Int
orInt64
解码成NSDecimalNumber属性.
swift
struct YourModel: HollowCodable {
@DecimalNumberCoding
var amount: NSDecimalNumber?
}
AnyDictionary/AnyDictionaryArray
- DictionaryCoding: 支持任何
[String:Any]
带有字典的值属性包装器。 - ArrayCoding: 支持任何值
[String:Any]
字典属性包装与数组。
swift
"mixDict": {
"sub": {
"amount": "52.9",
},
"array": [{
"val": 718,
}, {
"val": 911,
}],
"opt": null
}
struct YourModel: HollowCodable {
@AnyBacked<AnyDictionary>
var mixDict: [String: Any]? // 嵌套支持。
@AnyBacked<AnyDictionaryArray>
var mixLiat: [[String: Any]]?
}
AnyX
- AnyXCoding: 支持设置任意类型
Any
。
swift
struct YourModel: HollowCodable {
@AnyBacked<AnyX>
var x: Any? // Also nested support.
}
JSON
支持直接解码网络请求响应数据。
objc
{
"code": 200,
"message": "test case.",
"data": [{
"id": 2,
"title": "Harbeth Framework",
"github": "https://github.com/yangKJ/Harbeth",
"amount": "23.6",
"hex_color": "#FA6D5B",
"type": "text1",
"timestamp" : 590277534,
"bar": 1,
"hasDefBool": 2,
"time": "2024-05-29 23:49:55",
"iso8601": null,
"anyString": 5,
"background_color": {
"red": 255,
"green": 128,
"blue": 128
},
"dict": {
"amount": "326.0"
},
"mixDict": {
"sub": {
"amount": "52.9",
},
"array": [{
"val": 718,
}, {
"val": 911,
}]
},
"list": [{
"fruit": "Apple",
"dream": null
}, {
"fruit": "Banana",
"dream": "Night"
}]
}, {
"id": 7,
"title": "Network Framework",
"github": "https://github.com/yangKJ/RxNetworks",
"amount": 120.3,
"hex_color2": "#1AC756",
"type": null,
"timestamp" : 590288534,
"bar": null,
"hasDefBool": null,
"time": "2024-05-29 20:23:46",
"iso8601": "2023-05-23T09:43:38Z",
"anyString": null,
"background_color": null,
"dict": null,
"mixDict": null,
"list": null
}]
}
- 使用如下:
swift
let datas = ApiResponse<[YourModel]>.deserialize(from: json)?.data
Model和JSON互相转换。
objc
json = """
{
"uid":888888,
"name": "Condy",
"age": 18
}
"""
struct YourModel: HollowCodable {
@Immutable
var uid: Int?
@DefaultBacked<Int>
var age: Int // 如果非可选字段,则为0。
@AnyBacked<String>
var named: String? // 支持多键。
static var codingKeys: [ReplaceKeys] {
return [
ReplaceKeys(location: CodingKeys.named, keys: "name", "named"),
]
}
}
// JSON to Model.
let model = YourModel.deserialize(from: json)
// Model to JSON
let json = model.toJSONString(prettyPrint: true)
属性包装器
- @Immutable: 将属性变成只读。
- @IgnoredKey: 序列化/反序列化时将忽略的字段。
- @AnyBacked: 支持多种类型的属性包装器。
- @DefaultBacked: 解码失败时,将设置默认值。
- @BoolCoding: Bool类型的属性包装器。
- @Base64DataCoding: Base64与Data属性包装器。
- @DateFormatterCoding: 支持自定义日期格式的属性包装器。
- @ISO8601DateCoding: ISO8601日期属性包装器。
- @TimestampDateCoding: 时间戳属性包装器。
- @DecimalNumberCoding: NSDecimalNumber属性包装器。
- @EnumCoding: 枚举属性包装器。
- @HexColorCoding: Hex与Color属性包装器。
- @RGBAColorCoding: RGBA与Color属性包装器。
- @PointCoding: CGPoint属性包装器。
- @RectCoding: CGRect属性包装器。
这边也支持用户自定义,你只需要实现Transformer协议即可。
它还支持可以设置默认值的属性包装器,您需要实现HasDefaultValuable协议即可。
Booming
Booming 是Swift的基础网络库。它是为Swift 5开发的,旨在利用最新的语言特性。该框架的最终目标是实现简单的网络连接,从而使编写易于维护的代码变得容易。
这个模块是序列化和反序列化数据,取代HandyJSON。
🎷 结合网络接口使用如下:
scss
func request(_ count: Int) -> Observable<[CodableModel]> {
CodableAPI.cache(count)
.request(callbackQueue: DispatchQueue(label: "request.codable"))
.deserialized(ApiResponse<[CodableModel]>.self)
.compactMap({ $0.data })
.observe(on: MainScheduler.instance)
.catchAndReturn([])
}
答疑
-
你为什么会写这个库, 我为什么不用 HandyJSON
我之前项目是用 HandyJSON 的, 但由于 HandyJSON 是基于操作 Swift 底层数据结构实现的, 已经好几次 Swift 版本迭代后, 由于数据结构的改变 HandyJSON 都会出问题, 由于我不想手动解析模型, 促使了我写这个库, 配上足够多的测试用例总比手动安全一些
可能有人会说更新 HandyJSON 不就好了, 但是你既不能确保以后 Swift 不会更新底层数据结构后, 直接导致HandyJSON 死亡, 也不能确保你所开发的 APP 突然被迫停止开发后, 你的用户更新系统就不能用了对吧,目前为止最新版5.9HandyJSON已停止维护更新。so 不得不更换掉。
为了迁移到 HappyCodable, HappyCodable 的 API 很大程度参考了 HandyJSON
最后
- 关于Codable框架介绍与设计到此为止吧。
- 慢慢再补充其他相关属性包装器,喜欢就给我点个星🌟吧。
- Demo地址,目前包含
20+
种属性包装器。 - 再附上一个图像滤镜库HabethDemo地址 🎷喜欢的老板们可以点个星🌟
✌️.