【Swift】同时使用RawRepresentable和Codable的注意点

AppStorage相信很多人用过,相当于UserDefaults的属性包装器,但比UserDefaults更好用的是能在SwiftUI中作为一个响应式变量同步刷新视图。

起因

想使用AppStorage来存储一些比较简单的自定义模型,例如:

swift 复制代码
struct Cat {
    var name: String
    var age: Int
}

首先,想存储自定义模型到AppStorage中,就得遵守RawRepresentable,并且RawValue要为StringInt类型。模型转String一般就是转成JSON 字符串,因此很自然会想到Codable,使用JSONEncoder编码和JSONDecoder解码:

swift 复制代码
extension Cat: Codable, RawRepresentable {
    var rawValue: String {
        let jsonData = try! JSONEncoder().encode(self)
        return String(data: jsonData, encoding: .utf8)!
    }
    
    init?(rawValue: String) {
        let data = rawValue.data(using: .utf8)!
        self = try! JSONDecoder().decode(Self.self, from: data)
    }
}

补充说明,遵守Codable的模型,如果所有属性都是已经遵守了Codable,那么就不用自己去实现endodedecode

  • PS: Swift提供的基本类型都是Codable的,如StringInt,还有ArrayDictionary(只要存储的元素也是Codable的即可)。

写完,编译没问题,运行,卡死!!!

What the hell?

直接揭晓答案,这是因为死循环 了,看看swift的源码:

可以看出,只要遵守了CodableRawRepresentable,其默认实现的endode里面会调用rawValue,而rawValue是刚刚自己重写 的计算属性,内部则调用了JSONEncoder().encode(self),因此造成死循环了。

解决方法

自己去实现endodedecode

swift 复制代码
extension Cat: Codable {
    enum CodingKeys: String, CodingKey {
        case name, age
    }
    
    init(from decoder: Decoder) throws {
        let c = try decoder.container(keyedBy: CodingKeys.self)
        name = try c.decode(String.self, forKey: .name)
        age = try c.decode(Int.self, forKey: .age)
    }
    
    func encode(to encoder: Encoder) throws {
        var c = encoder.container(keyedBy: CodingKeys.self)
        try c.encode(name, forKey: .name)
        try c.encode(age, forKey: .age)
    }
}

打破循环!

最后

这个问题最主要的原因是过于依赖Swift的便捷性(默认实现Codable),真没想到CodableRawRepresentable一同使用时,会被这些默认实现导致死循环,还好这种问题开发时就能发现,以后使用新东西还是要多做测试,防止这种问题上线后才发现。

总结一下:使用AppStorage存储自定义模型,模型需要遵守RawRepresentable并且RawValue要为String,如果使用Codable进行模型转换,要自己去实现endodedecode防止死循环。

相关推荐
sakiko_1 天前
UIKit学习笔记3-布局、滚动视图、隐藏或显示视图
前端·笔记·学习·objective-c·swift·uikit
sakiko_2 天前
UIKit学习笔记5-使用UITableView制作聊天页面
笔记·学习·swift·uikit
朗清风2 天前
“\“在字符串表示正则语义中的作用
swift
四眼蒙面侠3 天前
深入 SwiftWork(第 0 篇):用 SwiftUI 构建一个 Agent 可视化工作台
swift·openagentsdk
sakiko_4 天前
UIKit学习笔记4-使用UITableView制作滚动视图
笔记·学习·ios·swift·uikit
四眼蒙面侠5 天前
深入 Open Agent SDK(番外篇):实战验证——把 SDK 塞进一个 macOS 原生 Agent 应用
swift·claudecode·bmad·agentsdk·openagentsdk
2501_915106326 天前
在Mac上搭建iOS开发环境的详细步骤与注意事项
ide·vscode·macos·ios·个人开发·swift·敏捷流程
harder3216 天前
RMP模式的创新突破
开发语言·学习·ios·swift·策略模式
sakiko_6 天前
UIKit学习笔记2-组件嵌套、滚动视图等
笔记·学习·objective-c·swift·uikit
四眼蒙面侠7 天前
深入 Open Agent SDK(五):会话持久化与安全防线
swift·claudecode·bmad·openagentsdk