用来快速替代项目中HandyJSON的Codable库

HollowCodable

HollowCodable 是一个基于Swift使用属性包装器的对Codable协议进行扩展的框架。

  • 使用属性包装器使复杂的可编码序列化变得轻而易举!

原生 JSON Codable 的问题

  1. 不支持自定义某个属性的 coding key, 一旦你有这种需求, 要么把所有的 coding key 手动实现一遍去修改想要的 coding key, 要么就得在 decode 的时候去设置 Decoder , 极其不方便
  2. 不支持忽略掉某些不能 Codable 的属性, 还是需要手动实现 coding key 才行
  3. decode 的时候不支持多个 coding key 映射同一个属性
  4. 不能使用模型的默认值, 当 decode 的数据缺失时无法使用定义里的默认值而是 throw 数据缺失错误, 这个设计导致例如版本更新后, 服务端删掉了模型的某个过期字段, 然后旧版本 app 都会陷入错误, 即使不用这个字段旧版本客户端依旧是能正常工作的(只是无效的数据显示缺失而已), 这很明显是不合理的.
  5. 不支持简单的类型转换, 比如转换 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: 支持使用BoolIntString来表示布尔值。
    • 大于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: 将StringTimeInterval值解码为ISO8601日期。
  • RFC2822DateCoding: 将StringTimeInterval值解码为RFC2822日期。
  • RFC3339DateCoding: 将StringTimeInterval值解码为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: 可以将StringDoubleFloatCGFloatInt or Int64解码成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)

属性包装器

这边也支持用户自定义,你只需要实现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([])
}

答疑

  1. 你为什么会写这个库, 我为什么不用 HandyJSON

    我之前项目是用 HandyJSON 的, 但由于 HandyJSON 是基于操作 Swift 底层数据结构实现的, 已经好几次 Swift 版本迭代后, 由于数据结构的改变 HandyJSON 都会出问题, 由于我不想手动解析模型, 促使了我写这个库, 配上足够多的测试用例总比手动安全一些

    可能有人会说更新 HandyJSON 不就好了, 但是你既不能确保以后 Swift 不会更新底层数据结构后, 直接导致HandyJSON 死亡, 也不能确保你所开发的 APP 突然被迫停止开发后, 你的用户更新系统就不能用了对吧,目前为止最新版5.9HandyJSON已停止维护更新。so 不得不更换掉。

    为了迁移到 HappyCodable, HappyCodable 的 API 很大程度参考了 HandyJSON

最后

  • 关于Codable框架介绍与设计到此为止吧。
  • 慢慢再补充其他相关属性包装器,喜欢就给我点个星🌟吧。
  • Demo地址,目前包含20+种属性包装器。
  • 再附上一个图像滤镜库HabethDemo地址 🎷喜欢的老板们可以点个星🌟

✌️.

相关推荐
gqkmiss19 分钟前
Chrome 浏览器 131 版本开发者工具(DevTools)更新内容
前端·chrome·浏览器·chrome devtools
Summer不秃25 分钟前
Flutter之使用mqtt进行连接和信息传输的使用案例
前端·flutter
旭日猎鹰29 分钟前
Flutter踩坑记录(二)-- GestureDetector+Expanded点击无效果
前端·javascript·flutter
Viktor_Ye35 分钟前
高效集成易快报与金蝶应付单的方案
java·前端·数据库
hummhumm38 分钟前
第 25 章 - Golang 项目结构
java·开发语言·前端·后端·python·elasticsearch·golang
乐闻x1 小时前
Vue.js 性能优化指南:掌握 keep-alive 的使用技巧
前端·vue.js·性能优化
一条晒干的咸魚1 小时前
【Web前端】创建我的第一个 Web 表单
服务器·前端·javascript·json·对象·表单
Amd7941 小时前
Nuxt.js 应用中的 webpack:compiled 事件钩子
前端·webpack·开发·编译·nuxt.js·事件·钩子
生椰拿铁You1 小时前
09 —— Webpack搭建开发环境
前端·webpack·node.js
狸克先生2 小时前
如何用AI写小说(二):Gradio 超简单的网页前端交互
前端·人工智能·chatgpt·交互