iOS Codable 模型与严格解析

举报
yd_290101916 发表于 2026/09/23 09:19:29 2026/09/23
【摘要】 iOS Codable 模型与严格解析Codable 能减少序列化样板代码,但模型是否可靠仍取决于接口契约。把所有字段声明为可选可以让解析“更容易成功”,却会把协议错误推迟到业务页面。更稳妥的做法是在网络边界严格解析,区分协议模型与领域模型,并只为接口确实允许的缺失情况设计可选值。 一、协议模型忠实表达响应struct OrderDTO: Decodable { let identi...

iOS Codable 模型与严格解析

Codable 能减少序列化样板代码,但模型是否可靠仍取决于接口契约。把所有字段声明为可选可以让解析“更容易成功”,却会把协议错误推迟到业务页面。更稳妥的做法是在网络边界严格解析,区分协议模型与领域模型,并只为接口确实允许的缺失情况设计可选值。

一、协议模型忠实表达响应

struct OrderDTO: Decodable {
    let identifier: String
    let amountInCent: Int64
    let status: String
    let createdAt: Date

    enum CodingKeys: String, CodingKey {
        case identifier = "orderId"
        case amountInCent = "amount"
        case status
        case createdAt
    }
}

字段被协议定义为必填时使用非可选类型,类型或字段缺失会直接导致解析失败。这样问题停留在网络边界,而不是进入页面后以空白内容表现。

二、DTO 与领域模型分离

接口状态字符串不应直接扩散到业务层:

enum OrderStatus: Equatable {
    case pending
    case completed
    case cancelled
}

extension OrderDTO {
    func toDomain() throws -> Order {
        let domainStatus: OrderStatus
        switch status {
        case "pending":
            domainStatus = .pending
        case "completed":
            domainStatus = .completed
        case "cancelled":
            domainStatus = .cancelled
        default:
            throw OrderMappingError.unsupportedStatus(status)
        }

        return Order(
            id: identifier,
            amountInCent: amountInCent,
            status: domainStatus,
            createdAt: createdAt
        )
    }
}

未知状态是否允许继续展示,由业务协议决定。关键流程不应擅自把未知值映射为某个正常状态。

三、统一日期解析策略

日期格式应在统一解码器中配置,不要让每个模型自行创建格式化器。日期格式化器创建和线程使用也应由网络基础层管理。

func makeDecoder(dateStrategy: JSONDecoder.DateDecodingStrategy) -> JSONDecoder {
    let decoder = JSONDecoder()
    decoder.dateDecodingStrategy = dateStrategy
    return decoder
}

如果同一响应中存在多种日期格式,优先推动协议统一;确实无法统一时,再对特定字段实现明确解析。

四、区分 null 与字段缺失

默认可选属性通常会把空值和缺失都解码为 nil。若两者业务含义不同,需要自定义解码并检查键是否存在。

struct ProfilePatchDTO: Decodable {
    enum IntroductionValue {
        case unchanged
        case cleared
        case value(String)
    }

    let introduction: IntroductionValue

    enum CodingKeys: String, CodingKey {
        case introduction
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        guard container.contains(.introduction) else {
            introduction = .unchanged
            return
        }
        if try container.decodeNil(forKey: .introduction) {
            introduction = .cleared
        } else {
            introduction = .value(
                try container.decode(String.self, forKey: .introduction)
            )
        }
    }
}

只有协议明确区分这些含义时才需要增加复杂度。

五、处理多态对象

消息列表可能包含文本、图片和系统事件。可以先读取类型字段,再选择具体模型:

enum MessageDTO: Decodable {
    case text(TextMessageDTO)
    case system(SystemMessageDTO)

    private enum CodingKeys: String, CodingKey {
        case type
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let type = try container.decode(String.self, forKey: .type)

        switch type {
        case "text":
            self = .text(try TextMessageDTO(from: decoder))
        case "system":
            self = .system(try SystemMessageDTO(from: decoder))
        default:
            throw DecodingError.dataCorruptedError(
                forKey: .type,
                in: container,
                debugDescription: "不支持的消息类型"
            )
        }
    }
}

若产品允许忽略未知消息,应在列表解析层明确记录并过滤,不能让每个页面随意决定。

六、不要默认做宽松数组解析

某个数组元素解析失败时跳过它,可以让页面继续展示,但也可能隐藏关键数据缺失。资讯推荐可能允许忽略单项,交易记录则通常要求整体失败并报警。

宽松解析必须是业务策略,并记录足够的非敏感诊断信息,不应成为通用解码器默认行为。

七、数值类型要符合范围

金额优先使用最小单位整数或明确十进制类型,不用二进制浮点直接承载精确金额。标识即使全是数字,也常应使用字符串,避免前导零和范围问题。

接口可能返回超出 Int 范围的值时,应选择明确位宽并测试边界。

八、编码请求也要有专用模型

更新命令和响应实体字段通常不同。请求模型只包含允许提交的字段,避免把只读标识或界面临时值一起编码。

struct UpdateProfileCommand: Encodable {
    let nickname: String
    let introduction: String?
}

是否编码空值、缺失字段的含义必须遵循协议,不要在全局编码器中随意删除所有空值。

九、记录错误而不泄露数据

解析失败日志可以包含模型类型、字段路径和请求标识,但不应完整输出用户响应。错误上下文足以定位协议变化时,无需记录敏感内容。

十、建立契约样本测试

为成功、空值、缺失字段、未知枚举、数值边界和多态类型准备固定样本。测试 DTO 解码和领域映射分别失败在预期位置。

协议升级时更新样本,能够提前发现字段类型和必填性变化。

总结

可靠的 Codable 设计强调边界严格。DTO 忠实表达协议,领域映射收敛字符串和状态,日期与数值使用统一策略,并只在业务明确允许时宽松处理。这样解析错误能尽早暴露,也不会让大量可选值污染整个应用。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。