在核心数据中,您可以使用
ValueTransformer
将各个属性转换为核心数据或从核心数据转换。这在从托管上下文存储/获取时加密/解密某些属性等情况下非常有用。
SwiftData 有类似的方法吗?我想加密保存数据,但在我的
@Query
变量中解密。
我需要这个用于 iOS。
这是通过核心数据使用
ValueTransformer
的示例(来源:https://www.avanderlee.com/swift/valuetransformer-core-data/):
override public func transformedValue(_ value: Any?) -> Any? {
guard let color = value as? UIColor else { return nil }
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: color, requiringSecureCoding: true)
return data
} catch {
assertionFailure("Failed to transform `UIColor` to `Data`")
return nil
}
}
override public func reverseTransformedValue(_ value: Any?) -> Any? {
guard let data = value as? NSData else { return nil }
do {
let color = try NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: data as Data)
return color
} catch {
assertionFailure("Failed to transform `Data` to `UIColor`")
return nil
}
}
@joakim-danielson 评论中的建议效果很好:)
本质上它是与 SwiftData 挂钩的 CoreData ValueTransformer。下面的示例在
Date
和 String
之间进行转换。
这是 SwiftData 模型:
import Foundation
import SwiftData
@Model
final class Item {
@Attribute(.transformable(by: DateValueTransformer.name.rawValue))
var timestamp: Date
init(timestamp: Date) {
self.timestamp = timestamp
}
}
这是 ValueTransformer,它将日期保存为编码字符串:
import Foundation
@objc(DateValueTransformer)
class DateValueTransformer: ValueTransformer {
var randomKey = "nothing"
private var dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "YYYY-MM-dd HH:mm:ss"
return formatter
}()
override class func transformedValueClass() -> AnyClass {
return NSDate.self
}
override class func allowsReverseTransformation() -> Bool {
return true
}
override func transformedValue(_ value: Any?) -> Any? {
guard let date = value as? Date else { return nil }
let dateString = dateFormatter.string(from: date) + randomKey
print("transform: \(dateString)")
do {
return try NSKeyedArchiver.archivedData(withRootObject: dateString, requiringSecureCoding: true)
} catch {
assertionFailure("Failed to transform `Date` to `Data(String)`")
return nil
}
}
override func reverseTransformedValue(_ value: Any?) -> Any? {
guard let dateData = value as? Data else { return nil }
do {
guard let dateString = try NSKeyedUnarchiver.unarchivedObject(ofClass: NSString.self, from: dateData as Data) as? String
else { return nil }
print("reverse: \(dateString)")
return dateFormatter.date(from: String(dateString.dropLast(randomKey.count)))
} catch {
assertionFailure("Failed to transform `Data(String)` to `Date`")
return nil
}
}
}
extension DateValueTransformer {
static let name = NSValueTransformerName(rawValue: String(describing: DateValueTransformer.self))
public static func register(randomKey: String) {
let transformer = DateValueTransformer()
transformer.randomKey = randomKey
ValueTransformer.setValueTransformer(transformer, forName: name)
}
}
为了演示如何将动态值传递给 ValueTransformer,此代码片段使用自定义
DateValueTransformer
注册了 randomKey
的实例:
import SwiftUI
import SwiftData
@main
struct SwiftDataExperimentApp: App {
var sharedModelContainer: ModelContainer = {
DateValueTransformer.register(randomKey: "something")
let schema = Schema([
Item.self,
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
do {
return try ModelContainer(for: schema, configurations: [modelConfiguration])
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(sharedModelContainer)
}
}
通过 SwiftData 存储
Item
的实例时,您将获得以下控制台输出。这表明我们的自定义randomKey
也正在工作:
transform: 2023-10-23 09:56:53something
reverse: 2023-10-23 09:56:53something