Store SwiftUI Color in SwiftData

I have an app that needs to store a SwiftUI Color within SwiftData and I was wondering if anyone had found a way to do so easily and accurately.

I'd prefer not to have to store the Color components (e.g. RGB values) and would ideally like to have a single variable in the @Model that stores the Color.

I had considered using an extension to the Color type to create a HEX encoded String of the Color and an initializer that creates a Color from the HEX encoded String. Unfortunately, doing so proved not to be accurate due data loss when converting component values to integers. When testing this in Photoshop, the original color #FBAA1D became #FFAB00.

Is there a way to accurately store the Color in SwiftData, possibly using a binary conversion to Data or somehow storing the Color.Resolved, which itself does not appear to be compatible with SwiftData.

Any thoughts on how to best store the Color accurately within SwiftData would be greatly appreciated.

Answered by intrepidreality in 814510022

Recommend the approach described in this blog post which also works for SwiftData. https://www.avanderlee.com/swift/valuetransformer-core-data/

I store Strings, and have no issue. Try this function and extension, and confirm that they encode and decode your colours correctly:

func colorToHex(_ colour: Color) -> String {
	guard let components: [CGFloat] = colour.cgColor?.components else { return "#FFFFFFFF" }

	if(components.count == 2) {
		return String(format: "#%02lX%02lX%02lX", lroundf(Float(components[0]) * 255.0), lroundf(Float(components[0]) * 255.0), lroundf(Float(components[0]) * 255.0))
	}
	return String(format: "#%02lX%02lX%02lX%02lX", lroundf(Float(components[0]) * 255.0), lroundf(Float(components[1]) * 255.0), lroundf(Float(components[2]) * 255.0), lroundf(Float(components[3]) * 255.0))
}

extension UIColor {
	public convenience init?(fromHex: String) {
		if(!fromHex.hasPrefix("#")) {
			return nil;
		}

		let r, g, b, a: CGFloat
		let start = fromHex.index(fromHex.startIndex, offsetBy: 1)
		let hexColor = String(fromHex[start...])

		if(hexColor.count == 8) {
			let scanner = Scanner(string: hexColor)
			var hexNumber: UInt64 = 0

			if(scanner.scanHexInt64(&hexNumber)) {
				r = CGFloat((hexNumber & 0xff000000) >> 24) / 255
				g = CGFloat((hexNumber & 0x00ff0000) >> 16) / 255
				b = CGFloat((hexNumber & 0x0000ff00) >> 8) / 255
				a = CGFloat(hexNumber & 0x000000ff) / 255

				self.init(red: r, green: g, blue: b, alpha: a)
				return
			}
		}
		return nil;
	}
}

Note, the extension is on UIColor, but it's trivial to convert to Color.

Accepted Answer

Recommend the approach described in this blog post which also works for SwiftData. https://www.avanderlee.com/swift/valuetransformer-core-data/

Store SwiftUI Color in SwiftData
 
 
Q