Swift中为类成员添加@Published属性包装器及Codable报错解决
报错原因
给属性添加@Published后,属性的底层存储类型变为Published<Value>,而非声明时写的原始类型。Swift编译器自动合成Codable协议实现时,无法自动识别@Published包装属性和编解码键的映射关系,因此会抛出类型不符合Encodable/Decodable协议的编译错误。
可行方案
以下两种方案都可以在保留@Published值变更发布能力的前提下,让类正常遵循Codable协议:
方案1:手动实现Codable编解码逻辑
不需要修改属性声明,手动编写Group类的编解码方法,明确指定属性的映射关系即可,代码如下:
class Item: Identifiable, Codable { var id = UUID() var text = "Hello World!" } class Group: Identifiable, Codable { var id = UUID() @Published var items: [Item] = [] enum CodingKeys: String, CodingKey { case id, items } required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decode(UUID.self, forKey: .id) items = try container.decode([Item].self, forKey: .items) } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: .id) try container.encode(items, forKey: .items) } } var group = Group()
- 编解码时直接访问
items属性本身即可,不需要使用带$前缀的投影值,Swift会自动处理和@Published包装器底层存储的赋值/读取逻辑。 - 这个方案通用性强,不会影响其他文件的代码逻辑,适合仅个别类需要处理
@Published编解码的场景。
方案2:扩展Published使其支持Codable
如果项目中大量存在@Published标记的Codable属性,可以直接给Published类型添加扩展,当它包装的值遵循Codable时,自动实现Codable协议:
extension Published: Codable where Value: Codable { public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() let initialValue = try container.decode(Value.self) self.init(initialValue: initialValue) } public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() try container.encode(wrappedValue) } }
添加这个扩展后,你最初写的原始代码不需要任何修改就可以直接编译通过,所有@Published标记的、值类型本身遵循Codable的属性,都会自动获得Codable能力,不需要每个类单独写编解码逻辑。
注意事项
- 不要对带
$前缀的投影值(比如$items)做编解码,这个值是Publisher类型,仅用于订阅值变更事件,不支持序列化。 - 如果你的类继承自其他
Codable的父类,手动实现编解码时记得要调用父类的init(from:)和encode(to:)方法,避免父类属性丢失。
内容的提问来源于stack exchange,提问作者Berry Blue
相关产品推荐
相关产品推荐

