SwiftData如何用ValueTransformer存储自定义类型的集合?
SwiftData中复用NSMeasurement转换器处理集合类型
核心结论
不需要为数组、字典等集合类型编写全新的ValueTransformer,可以基于已有的单对象转换器复用逻辑;针对Swift集合是struct的问题,通过桥接Foundation集合类型(NSArray/NSDictionary)即可避开NSCoding兼容性限制。
复用单对象转换器的实现方案
假设你已经实现并注册了NSMeasurementValueTransformer单对象转换器,以下是针对数组和字典的复用实现:
1. 先确认单对象转换器的基础实现(示例)
@objc(NSMeasurementValueTransformer) class NSMeasurementValueTransformer: ValueTransformer { override class func transformedValueClass() -> AnyClass { return NSData.self } override class func allowsReverseTransformation() -> Bool { return true } override func transformedValue(_ value: Any?) -> Any? { guard let measurement = value as? NSMeasurement else { return nil } return try? NSKeyedArchiver.archivedData(withRootObject: measurement, requiringSecureCoding: false) } override func reverseTransformedValue(_ value: Any?) -> Any? { guard let data = value as? Data else { return nil } return try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? NSMeasurement } } // 注册单对象转换器 ValueTransformer.setValueTransformer(NSMeasurementValueTransformer(), forName: NSValueTransformerName(rawValue: "NSMeasurementValueTransformer"))
2. 数组类型转换器(复用单对象逻辑)
利用NSArray的归档能力,自动调用元素的转换器:
@objc(NSMeasurementArrayValueTransformer) class NSMeasurementArrayValueTransformer: ValueTransformer { override class func transformedValueClass() -> AnyClass { return NSData.self } override class func allowsReverseTransformation() -> Bool { return true } override func transformedValue(_ value: Any?) -> Any? { guard let measurements = value as? [NSMeasurement] else { return nil } // 桥接为NSArray,归档时自动使用元素的转换器 let nsArray = measurements as NSArray return try? NSKeyedArchiver.archivedData(withRootObject: nsArray, requiringSecureCoding: false) } override func reverseTransformedValue(_ value: Any?) -> Any? { guard let data = value as? Data, let nsArray = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? NSArray else { return nil } return nsArray.compactMap { $0 as? NSMeasurement } } } // 注册数组转换器 ValueTransformer.setValueTransformer(NSMeasurementArrayValueTransformer(), forName: NSValueTransformerName(rawValue: "NSMeasurementArrayValueTransformer"))
3. 字典类型转换器(复用单对象逻辑)
同样桥接为NSDictionary,自动处理值类型的转换:
@objc(NSMeasurementDictionaryValueTransformer) class NSMeasurementDictionaryValueTransformer: ValueTransformer { override class func transformedValueClass() -> AnyClass { return NSData.self } override class func allowsReverseTransformation() -> Bool { return true } override func transformedValue(_ value: Any?) -> Any? { guard let measurementDict = value as? [String: NSMeasurement] else { return nil } // 桥接为NSDictionary,归档时自动使用值元素的转换器 let nsDict = measurementDict as NSDictionary return try? NSKeyedArchiver.archivedData(withRootObject: nsDict, requiringSecureCoding: false) } override func reverseTransformedValue(_ value: Any?) -> Any? { guard let data = value as? Data, let nsDict = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? NSDictionary else { return nil } var measurementDict = [String: NSMeasurement]() for (key, value) in nsDict { if let stringKey = key as? String, let measurement = value as? NSMeasurement { measurementDict[stringKey] = measurement } } return measurementDict } } // 注册字典转换器 ValueTransformer.setValueTransformer(NSMeasurementDictionaryValueTransformer(), forName: NSValueTransformerName(rawValue: "NSMeasurementDictionaryValueTransformer"))
SwiftData模型中的使用方式
在模型属性上指定对应的转换器即可:
@Model class MeasurementRecord { @Attribute(.transformable(by: "NSMeasurementValueTransformer")) var singleMeasurement: NSMeasurement? @Attribute(.transformable(by: "NSMeasurementArrayValueTransformer")) var measurementList: [NSMeasurement] = [] @Attribute(.transformable(by: "NSMeasurementDictionaryValueTransformer")) var measurementMap: [String: NSMeasurement] = [:] init() {} }
关键原理说明
- 复用逻辑:通过桥接Swift集合到Foundation的NSArray/NSDictionary(类类型,支持NSCoding),归档时会自动调用元素已注册的ValueTransformer,完全复用单对象的编码解码逻辑,避免重复代码。
- struct兼容:不需要直接处理Swift集合的struct特性,而是利用Foundation集合的归档能力间接完成转换,绕开NSCoding对class类型的限制。
内容的提问来源于stack exchange,提问作者pkamb
相关产品推荐
相关产品推荐

