How to use transformable in SwiftData?

Did anyone successfully used transformable in SwiftData to store UIColor or SwiftUI Color type?

 @Attribute(.transformable) var color: UIColor

Post not yet marked as solved Up vote post of azamsharp Down vote post of azamsharp
2.7k views
  • It is probably a bad idea to try to store a UI type in the model. It would be better to store a model type in the model and then transform it into a UI type using the SwiftUI View hierarchy, i.e. using a computed property called from body that returns a Color struct created from the model type.

Add a Comment

Replies

I made .transformable work this way, but there is - probably/hopefully - better solution as this looks ugly.

import SwiftUI
import SwiftData

@Model
final class MyModel {
    // ...
    @Attribute(.transformable) var color: Color {
        get {
            _$observationRegistrar.access(self, keyPath: \.hexColor)
            return Color(hex: self.getValue(for: \.hexColor))
        }

        set {
            _$observationRegistrar.withMutation(of: self, keyPath: \.hexColor) {
                self.setValue(for: \.hexColor, to: newValue.hex)
            }
        }
    }
    // ...
}

And here is the extension.

import SwiftData
import SwiftUI
import UIKit

extension Color {
    var hex: UInt {
        let components = self.cgColor?.components
        let r = components?[0] ?? 0
        let g = components?[1] ?? 0
        let b = components?[2] ?? 0

        let red = UInt(r * 255) << 16
        let green = UInt(g * 255) << 08
        let blue = UInt(b * 255)

        return red | green | blue
    }

    init(hex: UInt, alpha: Double = 1) {
        self.init(
            .sRGB,
            red: Double((hex >> 16) & 0xff) / 255,
            green: Double((hex >> 08) & 0xff) / 255,
            blue: Double((hex >> 00) & 0xff) / 255,
            opacity: alpha
        )
    }
}

I missed required property.

    var hexColor: UInt

Thanks!

I agree! There has to be a better way. Since if I am using hexColor then I can simply save hexColor in the database or hex code and construct Color from that. I think we have to somehow make Color or UIColor conform to codable.

It's now possible to use transformable(by:).

@Model
final class Profile {
    @Attribute(.unique) let id: UUID
    
    var name: String
    @Attribute(.transformable(by: UIColorValueTransformer.self)) var uiColor: UIColor
    
    var color: Color {
        get {
            .init(uiColor: uiColor)
        }
        set {
            uiColor = .init(newValue)
        }
    }
    
    init(id: UUID = UUID(), name: String, color: Color) {
        self.id = id
        self.name = name
        self.uiColor = .init(color)
    }
}


@main
struct MyApp: App {
    init() {
        UIColorValueTransformer.register()
    }
}

The transformer code can be found here: https://www.avanderlee.com/swift/valuetransformer-core-data/

  • Is this working for your on iOS 17.1.0 and above? Since transformable properties just crash for me

Add a Comment