Target macOS 11, iOS 13, tvOS 14, watchOS 7, visionOS 1 and later
This commit is contained in:
parent
d8a9f51056
commit
fcdf1967b9
|
@ -6,11 +6,11 @@ jobs:
|
||||||
test:
|
test:
|
||||||
runs-on: macos-13
|
runs-on: macos-13
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- run: sudo xcode-select -switch /Applications/Xcode_15.0.app
|
- run: sudo xcode-select -switch /Applications/Xcode_15.2.app
|
||||||
- run: swift test
|
- run: swift test
|
||||||
lint:
|
lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: norio-nomura/action-swiftlint@3.2.1
|
- uses: norio-nomura/action-swiftlint@3.2.1
|
||||||
|
|
|
@ -4,10 +4,11 @@ import PackageDescription
|
||||||
let package = Package(
|
let package = Package(
|
||||||
name: "Defaults",
|
name: "Defaults",
|
||||||
platforms: [
|
platforms: [
|
||||||
.macOS(.v10_15),
|
.macOS(.v11),
|
||||||
.iOS(.v13),
|
.iOS(.v14),
|
||||||
.tvOS(.v13),
|
.tvOS(.v14),
|
||||||
.watchOS(.v6)
|
.watchOS(.v7),
|
||||||
|
.visionOS(.v1)
|
||||||
],
|
],
|
||||||
products: [
|
products: [
|
||||||
.library(
|
.library(
|
||||||
|
|
|
@ -27,7 +27,7 @@ extension Defaults.CodableBridge {
|
||||||
return nil
|
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.
|
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 struct ColorBridge: Bridge {
|
||||||
public typealias Value = Color
|
public typealias Value = Color
|
||||||
public typealias Serializable = Any
|
public typealias Serializable = Any
|
||||||
|
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
private typealias NativeColor = NSColor
|
private typealias XColor = NSColor
|
||||||
#else
|
#else
|
||||||
private typealias NativeColor = UIColor
|
private typealias XColor = UIColor
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public func serialize(_ value: Value?) -> Serializable? {
|
public func serialize(_ value: Value?) -> Serializable? {
|
||||||
|
@ -394,15 +393,15 @@ extension Defaults {
|
||||||
let colorSpace = cgColor.colorSpace?.name as? String,
|
let colorSpace = cgColor.colorSpace?.name as? String,
|
||||||
let components = cgColor.components
|
let components = cgColor.components
|
||||||
else {
|
else {
|
||||||
return NativeColor.bridge.serialize(NativeColor(value))
|
return XColor.bridge.serialize(XColor(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
return [colorSpace, components] as [Any]
|
return [colorSpace, components] as [Any]
|
||||||
}
|
}
|
||||||
|
|
||||||
public func deserialize(_ object: Serializable?) -> Value? {
|
public func deserialize(_ object: Serializable?) -> Value? {
|
||||||
if let object = object as? NativeColor.Serializable {
|
if let object = object as? XColor.Serializable {
|
||||||
guard let nativeColor = NativeColor.bridge.deserialize(object) else {
|
guard let nativeColor = XColor.bridge.deserialize(object) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -419,7 +418,7 @@ extension Defaults {
|
||||||
return nil
|
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)
|
return Value(cgColor: cgColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -140,7 +140,6 @@ extension UUID: Defaults.Serializable {
|
||||||
public static let bridge = Defaults.UUIDBridge()
|
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 {
|
extension Color: Defaults.Serializable {
|
||||||
public static let bridge = Defaults.ColorBridge()
|
public static let bridge = Defaults.ColorBridge()
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,7 +156,6 @@ extension Default where Value: Equatable {
|
||||||
public var isDefaultValue: Bool { wrappedValue == defaultValue }
|
public var isDefaultValue: Bool { wrappedValue == defaultValue }
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(macOS 11, iOS 14, tvOS 14, watchOS 7, *)
|
|
||||||
extension Defaults {
|
extension Defaults {
|
||||||
/**
|
/**
|
||||||
A SwiftUI `Toggle` view that is connected to a ``Defaults/Key`` with a `Bool` value.
|
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<Text> {
|
extension Defaults.Toggle<Text> {
|
||||||
public init(_ title: some StringProtocol, key: Defaults.Key<Bool>) {
|
public init(_ title: some StringProtocol, key: Defaults.Key<Bool>) {
|
||||||
self.label = { Text(title) }
|
self.label = { Text(title) }
|
||||||
|
@ -219,7 +217,6 @@ extension Defaults.Toggle<Text> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(macOS 11, iOS 14, tvOS 14, watchOS 7, *)
|
|
||||||
extension Defaults.Toggle {
|
extension Defaults.Toggle {
|
||||||
/**
|
/**
|
||||||
Do something when the value changes to a different value.
|
Do something when the value changes to a different value.
|
||||||
|
|
|
@ -5,21 +5,22 @@ import OSLog
|
||||||
#endif
|
#endif
|
||||||
#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) }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extension Decodable {
|
||||||
|
init(jsonData: Data) throws {
|
||||||
|
self = try JSONDecoder().decode(Self.self, from: jsonData)
|
||||||
}
|
}
|
||||||
|
|
||||||
init?(jsonString: String) {
|
init(jsonString: String) throws {
|
||||||
guard let data = jsonString.data(using: .utf8) else {
|
try self.init(jsonData: jsonString.toData)
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
self.init(jsonData: data)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#if canImport(AppKit)
|
#if os(macOS)
|
||||||
import Foundation
|
import Foundation
|
||||||
import Defaults
|
import Defaults
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
|
@ -4,12 +4,11 @@ import SwiftUI
|
||||||
import Defaults
|
import Defaults
|
||||||
|
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
typealias NativeColor = NSColor
|
typealias XColor = NSColor
|
||||||
#else
|
#else
|
||||||
typealias NativeColor = UIColor
|
typealias XColor = UIColor
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@available(macOS 11.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)
|
|
||||||
extension Defaults.Keys {
|
extension Defaults.Keys {
|
||||||
fileprivate static let hasUnicorn = Key<Bool>("swiftui_hasUnicorn", default: false)
|
fileprivate static let hasUnicorn = Key<Bool>("swiftui_hasUnicorn", default: false)
|
||||||
fileprivate static let user = Key<User>("swiftui_user", default: User(username: "Hank", password: "123456"))
|
fileprivate static let user = Key<User>("swiftui_user", default: User(username: "Hank", password: "123456"))
|
||||||
|
@ -17,7 +16,6 @@ extension Defaults.Keys {
|
||||||
fileprivate static let color = Key<Color>("swiftui_color", default: .black)
|
fileprivate static let color = Key<Color>("swiftui_color", default: .black)
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(macOS 11.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)
|
|
||||||
struct ContentView: View {
|
struct ContentView: View {
|
||||||
@Default(.hasUnicorn) var hasUnicorn
|
@Default(.hasUnicorn) var hasUnicorn
|
||||||
@Default(.user) var user
|
@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 {
|
final class DefaultsSwiftUITests: XCTestCase {
|
||||||
override func setUp() {
|
override func setUp() {
|
||||||
super.setUp()
|
super.setUp()
|
||||||
|
@ -48,7 +45,7 @@ final class DefaultsSwiftUITests: XCTestCase {
|
||||||
XCTAssertFalse(view.hasUnicorn)
|
XCTAssertFalse(view.hasUnicorn)
|
||||||
XCTAssertEqual(view.user.username, "Hank")
|
XCTAssertEqual(view.user.username, "Hank")
|
||||||
XCTAssertEqual(view.setInt.count, 3)
|
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.user = User(username: "Chen", password: "123456")
|
||||||
view.hasUnicorn.toggle()
|
view.hasUnicorn.toggle()
|
||||||
view.setInt.insert(4)
|
view.setInt.insert(4)
|
||||||
|
@ -58,7 +55,7 @@ final class DefaultsSwiftUITests: XCTestCase {
|
||||||
XCTAssertEqual(view.setInt, Set(1...4))
|
XCTAssertEqual(view.setInt, Set(1...4))
|
||||||
XCTAssertFalse(Default(.hasUnicorn).defaultValue)
|
XCTAssertFalse(Default(.hasUnicorn).defaultValue)
|
||||||
XCTAssertFalse(Default(.hasUnicorn).isDefaultValue)
|
XCTAssertFalse(Default(.hasUnicorn).isDefaultValue)
|
||||||
XCTAssertNotEqual(NativeColor(view.color), NativeColor(Color.black))
|
XCTAssertNotEqual(XColor(view.color), XColor(Color.black))
|
||||||
XCTAssertEqual(NativeColor(view.color), NativeColor(Color(.sRGB, red: 100, green: 100, blue: 100, opacity: 1)))
|
XCTAssertEqual(XColor(view.color), XColor(Color(.sRGB, red: 100, green: 100, blue: 100, opacity: 1)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
2
license
2
license
|
@ -1,6 +1,6 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
|
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (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:
|
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:
|
||||||
|
|
||||||
|
|
13
readme.md
13
readme.md
|
@ -30,17 +30,16 @@ It's used in production by [all my apps](https://sindresorhus.com/apps) (1 milli
|
||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
|
|
||||||
- macOS 10.15+
|
- macOS 11+
|
||||||
- iOS 13+
|
- iOS 14+
|
||||||
- tvOS 13+
|
- tvOS 14+
|
||||||
- watchOS 6+
|
- watchOS 7+
|
||||||
|
- visionOS 1+
|
||||||
|
|
||||||
## Install
|
## 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).
|
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
|
## Support types
|
||||||
|
|
||||||
- `Int(8/16/32/64)`
|
- `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
|
### Observe changes to a key
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
|
|
Loading…
Reference in New Issue