There are plenty of resources out there that will tell you how to make SwiftUI’s Color conform to the Codable protocol. They all boil down to creating a UIColor (when you’re running on iOS) or NSColor (when you’re running on macOS) and then grabbing the color components from there, because SwiftUI is helpful by not letting you access those things directly. The thing I learned yesterday is that if you construct your SwiftUI Color from a color asset catalog and then you construct an NSColor out of it, you need to set a color space before you can grab the components. If you don’t do that, then the thread that’s doing the work will crash. So, the code needs to look like this:
import SwiftUI
#if canImport(UIKit)
import UIKit
#elseif canImport(AppKit)
import AppKit
#endif
extension Color {
// swiftlint:disable:next large_tuple
var components: (red: Double, green: Double, blue: Double, opacity: Double) {
#if canImport(UIKit)
typealias NativeColor = UIColor
#elseif canImport(AppKit)
typealias NativeColor = NSColor
#endif
var red: CGFloat = 0
var green: CGFloat = 0
var blue: CGFloat = 0
var opacity: CGFloat = 0
var native = NativeColor(self)
#if canImport(AppKit)
native = native.usingColorSpace(NSColorSpace.genericRGB)!
#endif
native.getRed(&red, green: &green, blue: &blue, alpha: &opacity)
return (red, green, blue, opacity)
}
}
I have hated the way *Color works since .. well, the early 90’s at least. The reason for them being so complicated is that they are really complicated – if you give a shit. I have always wanted
[[CloseEnoughColor red: ###, green: ###, blue: ###] set]
to do what I obviously want without all the hassle of dealing with color spaces and all that mumbojumbo that I don’t give a crap about because I’m not publishing this and I just need it to show up as purple-ish.