SwiftData 与 ValueTransformer 类似于核心数据

问题描述 投票:0回答:1

在核心数据中,您可以使用

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
    }
}
ios swift core-data swift-data
1个回答
0
投票

@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
© www.soinside.com 2019 - 2024. All rights reserved.