iOS开发:关于Model

Swift宏让Codable的使用更加简单

我很早之前的文章就写了有关Swift中JSON与Model的转换,回顾了自己使用的一些工具与经验,有兴趣的请点击会看:

Swift:JSON解析(上)

Swift:JSON解析(下)

现在Swift已经支持宏了,Codable可以更简单的嵌入到代码中,同时有AI的加持,让CV工作越来越简单。

不过关于Swift宏需要注意的是,目前Swift宏原生支持只支持SPM。

但是据我了解,很多项目开发还是通过Cocoapods集成的,所以在使用Codable宏的时候,考虑这个宏框架是否支持Cocoapods,当然在一个有Cocopods的项目中使用SPM也是可行的。

我找到两个相关的库同时支持SPM与Cocoapods,大家可以参考一下:

MetaCodable

CodableWrapper

如果不想使用Codable的宏,自己稍微手动写一写也没有问题。

这里我有"被坑"经验分享给大家。这个坑也是我在之前文章中对JSON中的值转为Swift的枚举的后续。

转enum类型的注意事项

举个简单的例子,后端会传一个性别的JSON数据,数据如下:

json 复制代码
{
  "sexType": 0,// 0表示男性,1表示女性
}

在App端我写了这样一段代码

swift 复制代码
enum SexType: Int {
    case man
    case woman
}

extension SexType: Codable {}

struct SexModel: Codable {
    var sexType: SexType?
    var name: String?
}

我们转换逻辑大致如下:

swift 复制代码
let SexTypeJSONString = """
{
    "sexType": 0,
    "name": "season",
}
"""

let sexData = SexTypeJSONString.data(using: .utf8)!

let sexModel = try? JSONDecoder().decode(SexModel.self, from: sexData)

这样看,转换应该没啥问题。结果某一天后台传过来的数据出错了,数据以"sexType": 2,传过来了,会转换成功吗?

这里大家可以试试动手试试?

有的同学会想,SexModel中定义的字段都是可选类型,那么应该name转换成功,sexType转换为nil。

真的是这样吗?实际上整个sexModel为nil,并且会抛出error!

但是放在这里,可能看不出来什么问题。

我们把这个逻辑放在Moya的网络请求中:

swift 复制代码
provider.rx.request(Service.fake)
    .map(SexModel.self)
    .compactMap { $0 }
    .asObservable()
    .asSingle()
    .subscribe { event in
        switch event {
        case .success(let model):
            /// 模型转成功,将数据绑定到正常的页面
            /// 构建正常页面
            break
        case .failure:
            /// 模型转失败,显示异常的页面
            /// 构建异常页面
            break
        }
    }
    .disposed(by: disposeBag)

如果按照之前认为model转换成功,只是sexType为nil,那么此刻会走success的逻辑,可能只是页面上的性别相关的显示异常。

但是实际上,会走到failure逻辑中,页面中不会显示任何用户想获取的信息。

想想看,如果一个JSON很多个字段,而且字段类型传的也是对的,只是恰好转Swift时,enum类型的匹配不上,页面异常了,尬不尬?

而且这种情况,很有可能是Android端仅有性别的UI异常了,而iOS端直接展示Error页面了。于是一定会有人丢出这句话:

为什么安卓是好的?iOS却不行?

想想就鸭梨山大。

JSON一口气转为enum固然美好,因为通过0或者1这样的字段表示某个状态,理解上总要"翻译"一下,但是这里带来的风险也需要开发者承担,一旦JSON返回的字段无法与枚举值匹配上,就会导致整个Model转换失败!

对于这个问题有以下几个解决方式:

  • 模型转的时候,还是使用Int类型去接受,另起一个只读计算属性去手动转Int为SexType,这个方法可以规避Codable转化的异常,但是增加了手动写代码的逻辑,而且不同模型中的不同的枚举都需要自己写一份,维护成本高;
  • 通过PropertyWrapper进行一次保底,这种属性包装器会允许转失败的时候向nil转换,或者附上默认值;
  • 通过引入Swift宏,通过宏运算符进行异常守护;

下面是通过propertyWrapper改造的一个例子:

swift 复制代码
@propertyWrapper
struct CanNilEnumType<Enum>: Codable where Enum: Codable, Enum: RawRepresentable, Enum.RawValue: Codable {
    var wrappedValue: Enum?
    
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let value = try? container.decode(Enum.RawValue.self) {
            wrappedValue = Enum(rawValue: value) ?? nil
        } else {
            wrappedValue = nil
        }
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        if let wrappedValue {
            try? container.encode(wrappedValue.rawValue)
        } else {
            /// do Nothing
        }
    }
}

代码这样改造:

swift 复制代码
struct SexModel: Codable {
    /// 注意使用属性包装器修饰的属性只能用var修饰
    @CanNilEnumType
    var sexType: SexType?
}

有兴趣的朋友可以上网找更多的例子,比如兼容后台传Int类型或者String类的数字,Swift都可以通过String类型接住等等,这里只是一个抛砖引玉。

同时考虑到性能,Swift宏应该属性包装器更好。

同时有关Swift宏做异常处理的例子,上面分享的MetaCodable链接中有详细的文档说明:

swift 复制代码
@Codable
struct CodableData {
    @Default("some")
    let field: String
}

总结

Codable是Swift中转Model的利器,同时也需要时时刻刻记住,Swift是一门强语言,任何数据类型的错误,任何数据匹配不上,都可能导致转模型失败,在书写Swift代码的同时,加强容错是我们需要考虑的,同时后端正确、正常的传值往往也是必要的。

参考文档

Swift 5 属性包装器Property Wrappers完整指南

Property Wrappers

相关推荐
for_ever_love__13 小时前
UI学习:UISearchController基础了解和应用
学习·ui·ios·objective-c
_codemonster15 小时前
30分钟快速搭建 Spring Cloud Alibaba 微服务实战(一)
微服务·架构·毕业设计·课程设计
Cosolar15 小时前
从零写一个 Attention Is All You Need
人工智能·面试·架构
qcx2317 小时前
【系统学AI】09 Multi-Agent架构(2026版):从学术理论到工业级实践
java·人工智能·架构·multi-agent·claude agent
wb0430720117 小时前
厨房质检员——从阿明的“祖传配方“到标准化质检,看测试金字塔的落地
架构·log4j
Dongwoo Jeong17 小时前
微服务架构(MSA)是如何诞生的?
微服务·云原生·架构
代码的小搬运工18 小时前
ZARA仿写
ios
张忠琳19 小时前
【kubernetes v1.21】(kubelet 1)Kubelet 核心架构与启动流程
云原生·架构·kubernetes·kubelet
用户9874092388719 小时前
超算中心 高性能计算 htc命令module use的作用
架构
AI科技星19 小时前
基于**v=c(空间光速螺旋运动)唯一第一性原理**重新完整求导证明
人工智能·线性代数·算法·机器学习·架构·概率论·学习方法