You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Core Data中存储自定义Struct?ValueTransformer归档报错

问题场景

需要将大型自定义Struct存储到Core Data中,使用ValueTransformer实现数据归档时抛出如下错误:

Error Domain=NSCocoaErrorDomain Code=4866 "The data couldn’t be written because it isn’t in the correct format." UserInfo={NSUnderlyingError=0x600002d70ba0 {Error Domain=NSCocoaErrorDomain Code=4864 "This decoder will only decode classes that adopt NSSecureCoding. Class '__SwiftValue' does not adopt it." UserInfo={NSDebugDescription=This decoder will only decode classes that adopt NSSecureCoding. Class '__SwiftValue' does not adopt it.}}}

当前编写的ValueTransformer代码如下:

class MyValueTransformer: ValueTransformer {

override func transformedValue(_ value: Any?) -> Any? {
    
    guard let model = value as? CoreDataModel else { print("HUI"); return nil }
    
    do {
        let data = try NSKeyedArchiver.archivedData(withRootObject: model, requiringSecureCoding: true)
        print("value transformer do")
        return data
    } catch {
        print(error) // 捕获上述抛出的错误
        print("value transformer catch")
        return nil
    }
}

相关配置参考:

  • 模型结构:模型结构截图
  • 模型属性配置:模型属性截图
报错原因

这个错本质是NSKeyedArchiver开了requiringSecureCoding: true的安全校验之后,只认遵守NSSecureCoding协议的Objective-C类。你存的是Swift原生Struct值类型,直接塞给归档器的时候,系统会自动把它包装成内部私有的__SwiftValue类,这个类根本没实现NSSecureCoding,校验直接就失败了。

正确存储方案

不需要把Struct改成OC类,也不用关闭安全编码凑活,用Swift原生的Codable做序列化适配值类型,比NSCoding简单得多,按下面步骤改就行:

  • 给CoreDataModel以及它内部嵌套的所有自定义类型都加上Codable协议遵守,普通的String、Int、Bool、数组、字典这类基础类型默认就支持Codable,不用额外编写编码解码逻辑。
  • 重写ValueTransformer逻辑,替换掉原来直接用NSKeyedArchiver归档Struct的实现,改用JSONEncoder/JSONDecoder做正反转换:
// 先定义Transformer的名称,后面Core Data配置里要填完全一致的内容
extension NSValueTransformerName {
    static let myModelTransformer = NSValueTransformerName("MyValueTransformer")
}

class MyValueTransformer: ValueTransformer {
    // 声明正向转换(写入数据)的输出类型为Data
    override class func transformedValueClass() -> AnyClass {
        NSData.self
    }

    // 声明支持反向转换(读取数据),可以将存储的Data转回模型对象
    override class func allowsReverseTransformation() -> Bool {
        true
    }

    // 写入Core Data时调用:模型转Data
    override func transformedValue(_ value: Any?) -> Any? {
        guard let model = value as? CoreDataModel else { return nil }
        do {
            return try JSONEncoder().encode(model)
        } catch {
            print("模型序列化失败:\(error)")
            return nil
        }
    }

    // 从Core Data读取时调用:Data转模型
    override func reverseTransformedValue(_ value: Any?) -> Any? {
        guard let data = value as? Data else { return nil }
        do {
            return try JSONDecoder().decode(CoreDataModel.self, from: data)
        } catch {
            print("模型反序列化失败:\(error)")
            return nil
        }
    }
}
  • 必须在Core Data加载持久化存储之前,提前注册自定义Transformer,否则Core Data找不到对应转换器会直接报错:
ValueTransformer.setValueTransformer(MyValueTransformer(), forName: .myModelTransformer)
  • 打开Core Data模型编辑器,找到存储该模型的属性做如下配置:
    • 属性类型选择Transformable
    • Transformer输入框填写MyValueTransformer
    • Custom Class输入框填写Struct名称CoreDataModel
    • 如果模型体积较大,建议勾选Allows External Storage,可降低数据库运行时的内存占用

内容的提问来源于stack exchange,提问作者Павел Кулицкий

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 11:42:18