From 55f3302c3ab30a8760f10042d0ebc0a6907f865a Mon Sep 17 00:00:00 2001 From: hank121314 Date: Sat, 16 Oct 2021 20:01:16 +0800 Subject: [PATCH] Add support for SwiftUI `Color` (#84) Co-authored-by: Sindre Sorhus --- Sources/Defaults/Defaults+Bridge.swift | 31 +++++++++++++++++++ Sources/Defaults/Defaults+Extensions.swift | 7 +++++ .../DefaultsTests/DefaultsSwiftUITests.swift | 18 +++++++++-- readme.md | 1 + 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/Sources/Defaults/Defaults+Bridge.swift b/Sources/Defaults/Defaults+Bridge.swift index 95d6635..c4e3f29 100644 --- a/Sources/Defaults/Defaults+Bridge.swift +++ b/Sources/Defaults/Defaults+Bridge.swift @@ -1,4 +1,5 @@ import Foundation +import SwiftUI #if os(macOS) import AppKit #else @@ -296,6 +297,36 @@ extension Defaults { } } +extension Defaults { + @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 + + #if os(macOS) + private typealias NativeColor = NSColor + #else + private typealias NativeColor = UIColor + #endif + + public func serialize(_ value: Value?) -> Serializable? { + guard let value = value else { + return nil + } + + return NativeColor.bridge.serialize(NativeColor(value)) + } + + public func deserialize(_ object: Serializable?) -> Value? { + guard let nativeColor = NativeColor.bridge.deserialize(object) else { + return nil + } + + return Value(nativeColor) + } + } +} + extension Defaults { public struct AnyBridge: Defaults.Bridge { public typealias Value = Defaults.AnySerializable diff --git a/Sources/Defaults/Defaults+Extensions.swift b/Sources/Defaults/Defaults+Extensions.swift index 27c8322..a010b90 100644 --- a/Sources/Defaults/Defaults+Extensions.swift +++ b/Sources/Defaults/Defaults+Extensions.swift @@ -1,5 +1,6 @@ import Foundation import CoreGraphics +import SwiftUI #if os(macOS) import AppKit #else @@ -136,6 +137,12 @@ extension Dictionary: Defaults.Serializable where Key: LosslessStringConvertible public static var bridge: Defaults.DictionaryBridge { Defaults.DictionaryBridge() } } + +@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, *) +extension Color: Defaults.Serializable { + public static let bridge = Defaults.ColorBridge() +} + #if os(macOS) /// `NSColor` conforms to `NSSecureCoding`, so it goes to `NSSecureCodingBridge`. extension NSColor: Defaults.Serializable {} diff --git a/Tests/DefaultsTests/DefaultsSwiftUITests.swift b/Tests/DefaultsTests/DefaultsSwiftUITests.swift index 987c27f..ff1dbb7 100644 --- a/Tests/DefaultsTests/DefaultsSwiftUITests.swift +++ b/Tests/DefaultsTests/DefaultsSwiftUITests.swift @@ -3,25 +3,35 @@ import Foundation import SwiftUI import Defaults +#if os(macOS) +typealias NativeColor = NSColor +#else +typealias NativeColor = UIColor +#endif + +@available(macOS 11.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) extension Defaults.Keys { fileprivate static let hasUnicorn = Key("swiftui_hasUnicorn", default: false) fileprivate static let user = Key("swiftui_user", default: User(username: "Hank", password: "123456")) fileprivate static let setInt = Key>("swiftui_setInt", default: Set(1...3)) + fileprivate static let color = Key("swiftui_color", default: .black) } -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) +@available(macOS 11.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) struct ContentView: View { @Default(.hasUnicorn) var hasUnicorn @Default(.user) var user @Default(.setInt) var setInt + @Default(.color) var color var body: some View { Text("User \(user.username) has Unicorn: \(String(hasUnicorn))") + .foregroundColor(color) Toggle("Toggle Unicorn", isOn: $hasUnicorn) } } -@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) +@available(macOS 11.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) final class DefaultsSwiftUITests: XCTestCase { override func setUp() { super.setUp() @@ -38,13 +48,17 @@ final class DefaultsSwiftUITests: XCTestCase { XCTAssertFalse(view.hasUnicorn) XCTAssertEqual(view.user.username, "Hank") XCTAssertEqual(view.setInt.count, 3) + XCTAssertEqual(NativeColor(view.color), NativeColor(Color.black)) view.user = User(username: "Chen", password: "123456") view.hasUnicorn.toggle() view.setInt.insert(4) + view.color = Color(.sRGB, red: 100, green: 100, blue: 100, opacity: 1) XCTAssertTrue(view.hasUnicorn) XCTAssertEqual(view.user.username, "Chen") XCTAssertEqual(view.setInt, Set(1...4)) XCTAssertFalse(Default(.hasUnicorn).defaultValue) XCTAssertFalse(Default(.hasUnicorn).isDefaultValue) + XCTAssertNotEqual(NativeColor(view.color), NativeColor(Color.black)) + XCTAssertEqual(NativeColor(view.color), NativeColor(Color(.sRGB, red: 100, green: 100, blue: 100, opacity: 1))) } } diff --git a/readme.md b/readme.md index 0686b0c..c5312d5 100644 --- a/readme.md +++ b/readme.md @@ -79,6 +79,7 @@ Add `https://github.com/sindresorhus/Defaults` in the [“Swift Package Manager - `URL` - `NSColor` (macOS) - `UIColor` (iOS) +- `Color` (SwiftUI) - `Codable` - `NSSecureCoding`