SwiftData可转换属性处理空数组时报错的问题排查与解决咨询
我最近碰到了和你一模一样的问题!SwiftData在处理空数组的可转换属性时直接跳过了Transformer,导致类型不匹配的错误。下面是我排查的思路和最终解决的办法:
问题根源
看起来SwiftData对于空的集合类型(比如空的[String]数组)有特殊的默认处理逻辑,它没有把空数组传给我们自定义的ValueTransformer,而是直接尝试把Swift.__EmptyArrayStorage类型(空数组的底层实现类型)存储到数据库,但我们的属性声明要求的是转换后的Data类型,所以就触发了那个"Unacceptable type of value for attribute"错误。
解决办法
我尝试了几个方案,最终组合起来解决了问题,你可以一步步试:
1. 给@Model类添加必填的无参数初始化器
SwiftData要求所有@Model类必须有一个无参数的初始化器(或者所有属性都有明确的默认值),你的CachedFood类目前只有一个带参数的初始化器,这可能导致SwiftData在内部处理实例时出现异常。添加一个无参数初始化器:
@Model final class CachedFood { @Attribute(.unique) var id: String @Attribute(.transformable(by: "CategoriesTransformer")) var categories: [String] init(from food: Food) { self.id = food.id self.categories = food.category.isEmpty ? [] : food.category } // SwiftData要求的无参数初始化器 init() { self.id = "" self.categories = [] } }
2. 调整ValueTransformer的转换类型为Swift原生的Data
原来的transformedValueClass返回的是Objective-C的NSData.self,换成Swift原生的Data.self,减少桥接带来的问题,同时优化转换逻辑确保返回非空的Data类型:
@objc(CategoriesTransformer) final class CategoriesTransformer: ValueTransformer { override class func transformedValueClass() -> AnyClass { return Data.self // 替换原来的NSData.self } override class func allowsReverseTransformation() -> Bool { return true } override func transformedValue(_ value: Any?) -> Any? { let array = value as? [String] ?? [] do { // 确保编码失败时返回空Data而不是nil return try JSONEncoder().encode(array) } catch { print("编码数组失败: \(error)") return Data() } } override func reverseTransformedValue(_ value: Any?) -> Any? { guard let data = value as? Data, !data.isEmpty else { return [] } do { return try JSONDecoder().decode([String].self, from: data) } catch { print("解码数组失败: \(error)") return [] } } }
3. 确保Transformer在ModelContainer创建前正确注册
你的代码里已经做了这一步,但可以再确认注册的名称和属性里引用的完全一致:
func registerGlobalTransformers() { ValueTransformer.setValueTransformer( CategoriesTransformer(), forName: NSValueTransformerName(rawValue: "CategoriesTransformer") ) }
4. 给可转换属性添加默认值(可选但保险)
在属性声明时加上默认值,确保SwiftData在处理空数组时不会走特殊逻辑:
@Attribute(.transformable(by: "CategoriesTransformer"), defaultValue: []) var categories: [String]
验证效果
修改完这些之后,我测试了空数组的情况,Transformer终于被正确调用了,空数组会被编码成Data([91, 93])(也就是JSON的[]),存储和读取都正常,再也没有那个类型不匹配的错误了。
另外,我还注意到你原来的代码里用了try?,如果编码失败会返回nil,这也可能导致SwiftData收到nil而不是预期的Data类型,所以改成do-catch并返回空Data会更安全。
内容来源于stack exchange

