diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 604c650..7a5a98c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,11 +6,11 @@ jobs: test: runs-on: macos-13 steps: - - uses: actions/checkout@v3 - - run: sudo xcode-select -switch /Applications/Xcode_15.0.app + - uses: actions/checkout@v4 + - run: sudo xcode-select -switch /Applications/Xcode_15.2.app - run: swift test lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: norio-nomura/action-swiftlint@3.2.1 diff --git a/Package.swift b/Package.swift index 8dff91b..58c0fb1 100644 --- a/Package.swift +++ b/Package.swift @@ -4,10 +4,11 @@ import PackageDescription let package = Package( name: "Defaults", platforms: [ - .macOS(.v10_15), - .iOS(.v13), - .tvOS(.v13), - .watchOS(.v6) + .macOS(.v11), + .iOS(.v14), + .tvOS(.v14), + .watchOS(.v7), + .visionOS(.v1) ], products: [ .library( diff --git a/Sources/Defaults/Defaults+Bridge.swift b/Sources/Defaults/Defaults+Bridge.swift index 137020e..12eeeb5 100644 --- a/Sources/Defaults/Defaults+Bridge.swift +++ b/Sources/Defaults/Defaults+Bridge.swift @@ -27,7 +27,7 @@ extension Defaults.CodableBridge { return nil } - return Value(jsonString: object) + return try? Value(jsonString: object) } } @@ -373,15 +373,14 @@ extension Defaults { 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 = Any #if os(macOS) - private typealias NativeColor = NSColor + private typealias XColor = NSColor #else - private typealias NativeColor = UIColor + private typealias XColor = UIColor #endif public func serialize(_ value: Value?) -> Serializable? { @@ -394,15 +393,15 @@ extension Defaults { let colorSpace = cgColor.colorSpace?.name as? String, let components = cgColor.components else { - return NativeColor.bridge.serialize(NativeColor(value)) + return XColor.bridge.serialize(XColor(value)) } return [colorSpace, components] as [Any] } public func deserialize(_ object: Serializable?) -> Value? { - if let object = object as? NativeColor.Serializable { - guard let nativeColor = NativeColor.bridge.deserialize(object) else { + if let object = object as? XColor.Serializable { + guard let nativeColor = XColor.bridge.deserialize(object) else { return nil } @@ -419,7 +418,7 @@ extension Defaults { return nil } - if #available(macOS 12.0, macOSApplicationExtension 12.0, *) { + if #available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, iOSApplicationExtension 15.0, macOSApplicationExtension 12.0, tvOSApplicationExtension 15.0, watchOSApplicationExtension 8.0, *) { return Value(cgColor: cgColor) } diff --git a/Sources/Defaults/Defaults+Extensions.swift b/Sources/Defaults/Defaults+Extensions.swift index ce2162e..22efa7e 100644 --- a/Sources/Defaults/Defaults+Extensions.swift +++ b/Sources/Defaults/Defaults+Extensions.swift @@ -140,7 +140,6 @@ extension UUID: Defaults.Serializable { public static let bridge = Defaults.UUIDBridge() } -@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() } diff --git a/Sources/Defaults/SwiftUI.swift b/Sources/Defaults/SwiftUI.swift index c6c8fb1..4e58ce5 100644 --- a/Sources/Defaults/SwiftUI.swift +++ b/Sources/Defaults/SwiftUI.swift @@ -156,7 +156,6 @@ extension Default where Value: Equatable { public var isDefaultValue: Bool { wrappedValue == defaultValue } } -@available(macOS 11, iOS 14, tvOS 14, watchOS 7, *) extension Defaults { /** A SwiftUI `Toggle` view that is connected to a ``Defaults/Key`` with a `Bool` value. @@ -211,7 +210,6 @@ extension Defaults { } } -@available(macOS 11, iOS 14, tvOS 14, watchOS 7, *) extension Defaults.Toggle { public init(_ title: some StringProtocol, key: Defaults.Key) { self.label = { Text(title) } @@ -219,7 +217,6 @@ extension Defaults.Toggle { } } -@available(macOS 11, iOS 14, tvOS 14, watchOS 7, *) extension Defaults.Toggle { /** Do something when the value changes to a different value. diff --git a/Sources/Defaults/Utilities.swift b/Sources/Defaults/Utilities.swift index cb37ffe..3b66b06 100644 --- a/Sources/Defaults/Utilities.swift +++ b/Sources/Defaults/Utilities.swift @@ -5,21 +5,22 @@ import OSLog #endif #endif -extension Decodable { - init?(jsonData: Data) { - guard let value = try? JSONDecoder().decode(Self.self, from: jsonData) else { - return nil - } - self = value - } +extension String { + /** + Get the string as UTF-8 data. + */ + var toData: Data { Data(utf8) } +} - init?(jsonString: String) { - guard let data = jsonString.data(using: .utf8) else { - return nil - } - self.init(jsonData: data) +extension Decodable { + init(jsonData: Data) throws { + self = try JSONDecoder().decode(Self.self, from: jsonData) + } + + init(jsonString: String) throws { + try self.init(jsonData: jsonString.toData) } } diff --git a/Tests/DefaultsTests/DefaultsNSColorTests.swift b/Tests/DefaultsTests/DefaultsNSColorTests.swift index f39d3e5..068f98a 100644 --- a/Tests/DefaultsTests/DefaultsNSColorTests.swift +++ b/Tests/DefaultsTests/DefaultsNSColorTests.swift @@ -1,4 +1,4 @@ -#if canImport(AppKit) +#if os(macOS) import Foundation import Defaults import XCTest diff --git a/Tests/DefaultsTests/DefaultsSwiftUITests.swift b/Tests/DefaultsTests/DefaultsSwiftUITests.swift index ff1dbb7..cff0ebd 100644 --- a/Tests/DefaultsTests/DefaultsSwiftUITests.swift +++ b/Tests/DefaultsTests/DefaultsSwiftUITests.swift @@ -4,12 +4,11 @@ import SwiftUI import Defaults #if os(macOS) -typealias NativeColor = NSColor +typealias XColor = NSColor #else -typealias NativeColor = UIColor +typealias XColor = 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")) @@ -17,7 +16,6 @@ extension Defaults.Keys { fileprivate static let color = Key("swiftui_color", default: .black) } -@available(macOS 11.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) struct ContentView: View { @Default(.hasUnicorn) var hasUnicorn @Default(.user) var user @@ -31,7 +29,6 @@ struct ContentView: View { } } -@available(macOS 11.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) final class DefaultsSwiftUITests: XCTestCase { override func setUp() { super.setUp() @@ -48,7 +45,7 @@ final class DefaultsSwiftUITests: XCTestCase { XCTAssertFalse(view.hasUnicorn) XCTAssertEqual(view.user.username, "Hank") XCTAssertEqual(view.setInt.count, 3) - XCTAssertEqual(NativeColor(view.color), NativeColor(Color.black)) + XCTAssertEqual(XColor(view.color), XColor(Color.black)) view.user = User(username: "Chen", password: "123456") view.hasUnicorn.toggle() view.setInt.insert(4) @@ -58,7 +55,7 @@ final class DefaultsSwiftUITests: XCTestCase { 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))) + XCTAssertNotEqual(XColor(view.color), XColor(Color.black)) + XCTAssertEqual(XColor(view.color), XColor(Color(.sRGB, red: 100, green: 100, blue: 100, opacity: 1))) } } diff --git a/license b/license index e7af2f7..fa7ceba 100644 --- a/license +++ b/license @@ -1,6 +1,6 @@ MIT License -Copyright (c) Sindre Sorhus (sindresorhus.com) +Copyright (c) Sindre Sorhus (https://sindresorhus.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/readme.md b/readme.md index 0a8584c..a936902 100644 --- a/readme.md +++ b/readme.md @@ -30,17 +30,16 @@ It's used in production by [all my apps](https://sindresorhus.com/apps) (1 milli ## Compatibility -- macOS 10.15+ -- iOS 13+ -- tvOS 13+ -- watchOS 6+ +- macOS 11+ +- iOS 14+ +- tvOS 14+ +- watchOS 7+ +- visionOS 1+ ## Install Add `https://github.com/sindresorhus/Defaults` in the [“Swift Package Manager” tab in Xcode](https://developer.apple.com/documentation/xcode/adding_package_dependencies_to_your_app). -**Requires Xcode 14.1 or later** - ## Support types - `Int(8/16/32/64)` @@ -233,8 +232,6 @@ struct ShowAllDayEventsSetting: View { } ``` -*Requires at least macOS 11, iOS 14, tvOS 14, watchOS 7.* - ### Observe changes to a key ```swift