From 9e65eac602fe7d786275a7b3f0714474273e1781 Mon Sep 17 00:00:00 2001 From: hank121314 Date: Fri, 10 Jun 2022 15:52:03 +0800 Subject: [PATCH] Fix preserving color space for the `Color` type (#105) Co-authored-by: Sindre Sorhus --- Sources/Defaults/Defaults+Bridge.swift | 39 +++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/Sources/Defaults/Defaults+Bridge.swift b/Sources/Defaults/Defaults+Bridge.swift index 3485f45..6dddbac 100644 --- a/Sources/Defaults/Defaults+Bridge.swift +++ b/Sources/Defaults/Defaults+Bridge.swift @@ -391,10 +391,15 @@ extension Defaults { } extension Defaults { + /** + The bridge which is responsible for `SwiftUI.Color` serialization and deserialization. + + It is unsafe to convert `SwiftUI.Color` to `UIColor` and use `UIColor.bridge` to serialize it, because `UIColor` does not hold a color space, but `Swift.Color` does (which means color space might get lost in the conversion). The bridge will always try to preserve the color space whenever `Color#cgColor` exists. Only when `Color#cgColor` is `nil`, will it use `UIColor.bridge` to do the serialization and deserialization. + */ @available(iOS 15.0, macOS 11.0, tvOS 15.0, watchOS 8.0, iOSApplicationExtension 15.0, macOSApplicationExtension 11.0, tvOSApplicationExtension 15.0, watchOSApplicationExtension 8.0, *) public struct ColorBridge: Bridge { public typealias Value = Color - public typealias Serializable = Data + public typealias Serializable = Any #if os(macOS) private typealias NativeColor = NSColor @@ -407,15 +412,41 @@ extension Defaults { return nil } - return NativeColor.bridge.serialize(NativeColor(value)) + guard + let cgColor = value.cgColor, + let colorSpace = cgColor.colorSpace?.name as? String, + let components = cgColor.components + else { + return NativeColor.bridge.serialize(NativeColor(value)) + } + + return [colorSpace, components] } public func deserialize(_ object: Serializable?) -> Value? { - guard let nativeColor = NativeColor.bridge.deserialize(object) else { + if let object = object as? NativeColor.Serializable { + guard let nativeColor = NativeColor.bridge.deserialize(object) else { + return nil + } + + return Value(nativeColor) + } + + guard + let object = object as? [Any], + let rawColorspace = object[0] as? String, + let colorspace = CGColorSpace(name: rawColorspace as CFString), + let components = object[1] as? [CGFloat], + let cgColor = CGColor(colorSpace: colorspace, components: components) + else { return nil } - return Value(nativeColor) + if #available(macOS 12.0, macOSApplicationExtension 12.0, *) { + return Value(cgColor: cgColor) + } else { + return Value(cgColor) + } } } }