UIColor 可以持久化在 SwiftData 中吗

Can UIColor be persisted in SwiftData

提问人:Michael May 提问时间:11/16/2023 更新时间:11/17/2023 访问量:80

问:

我正在尝试通过 SwiftData 持久化一个包含该类型属性的数据模型。我按照 [教程][1] 使用声明并实现了 .完整的代码如下。UIColor@Attribute(.transformable(by: UIColorValueTransformer.self)) var tileColor: UIColor?UIColorValueTransformer' which subclasses 'ValueTransformer

但是,Xcode 会引发运行时异常,并显示错误消息“SwiftData/SchemaCoreData.swift:244:致命错误:无法初始化 UIColorValueTransformer”。我不知道复杂的模型如何通过 SwiftData 持久化或持久化。ValueTransformerUIColorColor

// Tile.swift
import SwiftData
import UIKit

@Model
final class Tile {
    @Attribute (.unique) var name: String
    var type: String
    var @Attribute(.transformable(by: UIColorValueTransformer.self)) var tileColor: UIColor?

    init(name: String, type: String, tileColor: UIColor) {
        self.name = name
        self.type = type
        self.tileColor = tileColor
    }
}

final class UIColorValueTransformer: ValueTransformer {

    override class func transformedValueClass() -> AnyClass {
        return UIColor.self
    }

    // return data
    override 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 {
            return nil
        }
    }

    // return UIColor
    override func reverseTransformedValue(_ value: Any?) -> Any? {
        guard let data = value as? Data else { return nil }
    
        do {
            let color = try NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: data)
            return color
        } catch {
            return nil
        }
    }
}

// TileApp.swift
import SwiftUI
import SwiftData

@main
struct TileApp: App {

    init() {
        ValueTransformer.setValueTransformer(UIColorValueTransformer(), forName: NSValueTransformerName("UIColorValueTransformer"))
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
                .modelContainer(for: [Tile.self])
        }
    }
}

非常感谢您的帮助。 [1]:https://azamsharp.com/2023/07/04/the-ultimate-swift-data-guide.html

iOS SwiftUI swift-data

评论

0赞 matt 11/16/2023
danielbernal.co/......
0赞 matt 11/16/2023
可能是 stackoverflow.com/questions/65858279/ 的副本......UIColor 是安全的可存档的,所以应该没有问题。
0赞 Michael May 11/16/2023
@matt,感谢您的及时回复和建议。恐怕您提供的重复帖子中的错误与我帖子中的错误不同。很抱歉,我正在尝试使用 SwiftData 而不是 CoreData 来持久化数据模型。我想就如何通过 SwiftData 持久化 UIColor 甚至 Color 而不导致运行时错误提供进一步的建议,如我的帖子中所示。
0赞 matt 11/16/2023
SwiftData Core Data。
1赞 Joakim Danielson 11/17/2023
@MichaelMay,如果您有一个有效的解决方案,尽管有警告,您应该将其作为答案发布。

答:

0赞 Michael May 11/17/2023 #1

如上@Bram所示,解决方案如下:

// Tile.swift
import SwiftData
import UIKit

@Model
final class Tile {
    @Attribute (.unique) var name: String
    var type: String
    var @Attribute(.transformable(by: UIColorValueTransformer.self)) var tileColor: UIColor?

    init(name: String, type: String, tileColor: UIColor) {
        self.name = name
        self.type = type
        self.tileColor = tileColor
    }
}

@objc(UIColorValueTransformer) // The solution is adding this line
final class UIColorValueTransformer: ValueTransformer {

    override class func transformedValueClass() -> AnyClass {
        return UIColor.self
    }

    // return data
    override 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 {
            return nil
        }
    }

    // return UIColor
    override func reverseTransformedValue(_ value: Any?) -> Any? {
        guard let data = value as? Data else { return nil }
    
        do {
            let color = try NSKeyedUnarchiver.unarchivedObject(ofClass: UIColor.self, from: data)
            return color
        } catch {
            return nil
        }
    }
}

// TileApp.swift
import SwiftUI
import SwiftData

@main
struct TileApp: App {

    init() {
        ValueTransformer.setValueTransformer(UIColorValueTransformer(), forName: NSValueTransformerName("UIColorValueTransformer"))
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
                .modelContainer(for: [Tile.self])
        }
    }
}