设置SwiftData对象的Codable可选String属性时崩溃求助
问题场景
当SwiftData的@Model类中包含带有可选String属性的Codable结构体时,使用仅包含部分非String属性的JSON初始化该结构体后,应用会崩溃。相关代码与错误信息如下:
Model类与结构体定义
@Model final class Item { var timestamp: Date var codableProp: CodedValue? init(timestamp: Date, cProp: CodedValue?) { self.timestamp = timestamp self.codableProp = cProp } } struct CodedValue: Codable{ var string0: String? = nil var bool0: Bool? = nil var bool1: Bool? = nil }
触发崩溃的JSON
{ "bool0": true }
错误信息
"Could not cast value of type 'Swift.Optional<Swift.String>' (0x1c1e3f000) to 'Swift.String' (0x1c1e37e30)"
崩溃发生在@Model宏自动生成的代码内部,原因是SwiftData解析Codable结构体内部的可选String属性时,错误地将Optional<String>尝试转换为非可选的String类型。
解决方案
方案1:使用@Attribute(.externalStorage)修饰Codable属性
给codableProp添加@Attribute(.externalStorage)属性,让SwiftData将整个Codable结构体序列化为Data存储,而非拆解结构体的每个属性到数据库字段。这种方式能彻底避免内部属性的类型映射错误。
修改后的Model类代码:
@Model final class Item { var timestamp: Date @Attribute(.externalStorage) var codableProp: CodedValue? init(timestamp: Date, cProp: CodedValue?) { self.timestamp = timestamp self.codableProp = cProp } }
方案2:手动实现Codable协议
手动实现Codable逻辑,明确控制可选属性的解析流程,确保nil值被正确处理,避免SwiftData宏的自动映射出错。
修改后的结构体代码:
struct CodedValue: Codable{ var string0: String? = nil var bool0: Bool? = nil var bool1: Bool? = nil enum CodingKeys: String, CodingKey { case string0, bool0, bool1 } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) string0 = try container.decodeIfPresent(String.self, forKey: .string0) bool0 = try container.decodeIfPresent(Bool.self, forKey: .bool0) bool1 = try container.decodeIfPresent(Bool.self, forKey: .bool1) } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encodeIfPresent(string0, forKey: .string0) try container.encodeIfPresent(bool0, forKey: .bool0) try container.encodeIfPresent(bool1, forKey: .bool1) } }
方案3:用空字符串替代nil(按需选择)
如果业务场景允许,可以把可选String属性改为非可选类型,用空字符串作为默认值,避免nil带来的类型转换问题:
struct CodedValue: Codable{ var string0: String = "" var bool0: Bool? = nil var bool1: Bool? = nil }
总结
最推荐方案1,通过@Attribute(.externalStorage)让SwiftData以Data形式存储整个Codable结构体,既简单又能彻底解决类型映射问题。如果需要保留结构体内部属性的可查询性,可以选择方案2手动实现Codable逻辑。
内容的提问来源于stack exchange,提问作者Michael Berk

