Tweaks
This commit is contained in:
parent
72264f116f
commit
1f693cde80
|
@ -21,13 +21,21 @@ let package = Package(
|
|||
targets: [
|
||||
.target(
|
||||
name: "Defaults",
|
||||
resources: [.copy("PrivacyInfo.xcprivacy")]
|
||||
resources: [
|
||||
.copy("PrivacyInfo.xcprivacy")
|
||||
]
|
||||
// swiftSettings: [
|
||||
// .swiftLanguageMode(.v5)
|
||||
// ]
|
||||
),
|
||||
.testTarget(
|
||||
name: "DefaultsTests",
|
||||
dependencies: [
|
||||
"Defaults"
|
||||
]
|
||||
// swiftSettings: [
|
||||
// .swiftLanguageMode(.v5)
|
||||
// ]
|
||||
)
|
||||
]
|
||||
)
|
||||
|
|
|
@ -39,7 +39,7 @@ extension Defaults {
|
|||
|
||||
## Dynamically Toggle Syncing
|
||||
|
||||
You can also toggle the syncing behavior dynamically using the ``Defaults/iCloud/add(_:)`` and ``Defaults/iCloud/remove(_:)-1b8w5`` methods.
|
||||
You can also toggle the syncing behavior dynamically using the ``Defaults/iCloud/add(_:)`` and ``Defaults/iCloud/remove(_:)-3074m`` methods.
|
||||
|
||||
```swift
|
||||
import Defaults
|
||||
|
@ -91,14 +91,14 @@ extension Defaults {
|
|||
/**
|
||||
Remove the keys that are set to be automatically synced.
|
||||
*/
|
||||
public static func remove(_ keys: Defaults.Keys...) {
|
||||
synchronizer.remove(keys)
|
||||
public static func remove<each Value>(_ keys: repeat Defaults.Key<each Value>) {
|
||||
repeat synchronizer.remove(each keys)
|
||||
}
|
||||
|
||||
/**
|
||||
Remove the keys that are set to be automatically synced.
|
||||
*/
|
||||
public static func remove(_ keys: [Defaults.Keys]) {
|
||||
public static func remove(_ keys: [Defaults._AnyKey]) {
|
||||
synchronizer.remove(keys)
|
||||
}
|
||||
|
||||
|
@ -179,7 +179,7 @@ extension Defaults.iCloud {
|
|||
/**
|
||||
Represent different data sources available for synchronization.
|
||||
*/
|
||||
public enum DataSource {
|
||||
enum DataSource {
|
||||
/**
|
||||
Using `key.suite` as data source.
|
||||
*/
|
||||
|
@ -285,10 +285,21 @@ final class iCloudSynchronizer {
|
|||
}
|
||||
|
||||
/**
|
||||
Remove key and stop the observation.
|
||||
Remove the keys and stop the observation.
|
||||
*/
|
||||
func remove(_ keys: [Defaults.Keys]) {
|
||||
func remove<each Value>(_ keys: repeat Defaults.Key<each Value>) {
|
||||
for key in repeat (each keys) {
|
||||
self.keys.remove(key)
|
||||
localKeysMonitor.remove(key: key)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Remove the keys and stop the observation.
|
||||
*/
|
||||
func remove(_ keys: [Defaults._AnyKey]) {
|
||||
self.keys.subtract(keys)
|
||||
|
||||
for key in keys {
|
||||
localKeysMonitor.remove(key: key)
|
||||
}
|
||||
|
@ -543,10 +554,11 @@ extension iCloudSynchronizer {
|
|||
guard let remoteTimestamp = self.timestamp(forKey: key, source: .remote) else {
|
||||
continue
|
||||
}
|
||||
|
||||
if
|
||||
let localTimestamp = self.timestamp(forKey: key, source: .local),
|
||||
localTimestamp >= remoteTimestamp
|
||||
{
|
||||
{ // swiftlint:disable:this opening_brace
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -105,6 +105,8 @@ extension Defaults {
|
|||
Create a key.
|
||||
|
||||
- Parameter name: The name must be ASCII, not start with `@`, and cannot contain a dot (`.`).
|
||||
- Parameter defaultValue: The default value.
|
||||
- Parameter suite: The `UserDefaults` suite to store the value in.
|
||||
- Parameter iCloud: Automatically synchronize the value with ``Defaults/iCloud``.
|
||||
|
||||
The `default` parameter should not be used if the `Value` type is an optional.
|
||||
|
@ -150,7 +152,9 @@ extension Defaults {
|
|||
```
|
||||
|
||||
- Parameter name: The name must be ASCII, not start with `@`, and cannot contain a dot (`.`).
|
||||
- Parameter suite: The `UserDefaults` suite to store the value in.
|
||||
- Parameter iCloud: Automatically synchronize the value with ``Defaults/iCloud``.
|
||||
- Parameter defaultValueGetter: The dynamic default value.
|
||||
|
||||
- Note: This initializer will not set the default value in the actual `UserDefaults`. This should not matter much though. It's only really useful if you use legacy KVO bindings.
|
||||
*/
|
||||
|
@ -158,8 +162,8 @@ extension Defaults {
|
|||
public init(
|
||||
_ name: String,
|
||||
suite: UserDefaults = .standard,
|
||||
default defaultValueGetter: @escaping () -> Value,
|
||||
iCloud: Bool = false
|
||||
iCloud: Bool = false,
|
||||
default defaultValueGetter: @escaping () -> Value
|
||||
) {
|
||||
self.defaultValueGetter = defaultValueGetter
|
||||
|
||||
|
@ -178,6 +182,7 @@ extension Defaults.Key {
|
|||
Create a key with an optional value.
|
||||
|
||||
- Parameter name: The name must be ASCII, not start with `@`, and cannot contain a dot (`.`).
|
||||
- Parameter suite: The `UserDefaults` suite to store the value in.
|
||||
- Parameter iCloud: Automatically synchronize the value with ``Defaults/iCloud``.
|
||||
*/
|
||||
public convenience init<T>(
|
||||
|
@ -185,7 +190,12 @@ extension Defaults.Key {
|
|||
suite: UserDefaults = .standard,
|
||||
iCloud: Bool = false
|
||||
) where Value == T? {
|
||||
self.init(name, default: nil, suite: suite, iCloud: iCloud)
|
||||
self.init(
|
||||
name,
|
||||
default: nil,
|
||||
suite: suite,
|
||||
iCloud: iCloud
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -243,6 +253,7 @@ extension Defaults {
|
|||
/**
|
||||
Observe updates to a stored value.
|
||||
|
||||
- Parameter key: The key to observe updates from.
|
||||
- Parameter initial: Trigger an initial event on creation. This can be useful for setting default values on controls.
|
||||
|
||||
```swift
|
||||
|
@ -262,7 +273,7 @@ extension Defaults {
|
|||
public static func updates<Value: Serializable>(
|
||||
_ key: Key<Value>,
|
||||
initial: Bool = true
|
||||
) -> AsyncStream<Value> { // TODO: Make this `some AsyncSequence<Value>` when Swift 6 is out.
|
||||
) -> AsyncStream<Value> { // TODO: Make this `some AsyncSequence<Value>` when targeting macOS 15.
|
||||
.init { continuation in
|
||||
let observation = DefaultsObservation(object: key.suite, key: key.name) { _, change in
|
||||
// TODO: Use the `.deserialize` method directly.
|
||||
|
@ -273,15 +284,19 @@ extension Defaults {
|
|||
observation.start(options: initial ? [.initial] : [])
|
||||
|
||||
continuation.onTermination = { _ in
|
||||
observation.invalidate()
|
||||
// `invalidate()` should be thread-safe, but it is not in practice.
|
||||
DispatchQueue.main.async {
|
||||
observation.invalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Make this include a tuple with the values when Swift supports variadic generics. I can then simply use `merge()` with the first `updates()` method.
|
||||
// We still keep this as it can be useful to pass a dynamic array of keys.
|
||||
/**
|
||||
Observe updates to multiple stored values.
|
||||
|
||||
- Parameter keys: The keys to observe updates from.
|
||||
- Parameter initial: Trigger an initial event on creation. This can be useful for setting default values on controls.
|
||||
|
||||
```swift
|
||||
|
@ -297,7 +312,7 @@ extension Defaults {
|
|||
public static func updates(
|
||||
_ keys: [_AnyKey],
|
||||
initial: Bool = true
|
||||
) -> AsyncStream<Void> { // TODO: Make this `some AsyncSequence<Value>` when Swift 6 is out.
|
||||
) -> AsyncStream<Void> { // TODO: Make this `some AsyncSequence<Void>` when targeting macOS 15.
|
||||
.init { continuation in
|
||||
let observations = keys.indexed().map { index, key in
|
||||
let observation = DefaultsObservation(object: key.suite, key: key.name) { _, _ in
|
||||
|
@ -311,8 +326,11 @@ extension Defaults {
|
|||
}
|
||||
|
||||
continuation.onTermination = { _ in
|
||||
for observation in observations {
|
||||
observation.invalidate()
|
||||
// `invalidate()` should be thread-safe, but it is not in practice.
|
||||
DispatchQueue.main.async {
|
||||
for observation in observations {
|
||||
observation.invalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ extension Defaults {
|
|||
}
|
||||
|
||||
extension Defaults {
|
||||
// TODO: Add this to the main docs page.
|
||||
/**
|
||||
Reset the given keys back to their default values.
|
||||
|
||||
|
@ -76,10 +77,39 @@ extension Defaults {
|
|||
//=> false
|
||||
```
|
||||
*/
|
||||
public static func reset<each Value>(
|
||||
_ keys: repeat Key<each Value>,
|
||||
suite: UserDefaults = .standard
|
||||
) {
|
||||
for key in repeat (each keys) {
|
||||
key.reset()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Remove this when the variadic generics version works with DocC.
|
||||
/**
|
||||
Reset the given keys back to their default values.
|
||||
|
||||
```swift
|
||||
extension Defaults.Keys {
|
||||
static let isUnicornMode = Key<Bool>("isUnicornMode", default: false)
|
||||
}
|
||||
|
||||
Defaults[.isUnicornMode] = true
|
||||
//=> true
|
||||
|
||||
Defaults.reset(.isUnicornMode)
|
||||
|
||||
Defaults[.isUnicornMode]
|
||||
//=> false
|
||||
```
|
||||
*/
|
||||
@_disfavoredOverload
|
||||
public static func reset(_ keys: _AnyKey...) {
|
||||
reset(keys)
|
||||
}
|
||||
|
||||
// We still keep this as it can be useful to pass a dynamic array of keys.
|
||||
/**
|
||||
Reset the given keys back to their default values.
|
||||
|
||||
|
|
|
@ -77,6 +77,7 @@ This is similar to `@AppStorage` but it accepts a ``Defaults/Key`` and many more
|
|||
*/
|
||||
@propertyWrapper
|
||||
public struct Default<Value: Defaults.Serializable>: DynamicProperty {
|
||||
@_documentation(visibility: private)
|
||||
public typealias Publisher = AnyPublisher<Defaults.KeyChange<Value>, Never>
|
||||
|
||||
private let key: Defaults.Key<Value>
|
||||
|
@ -130,6 +131,7 @@ public struct Default<Value: Defaults.Serializable>: DynamicProperty {
|
|||
*/
|
||||
public var publisher: Publisher { Defaults.publisher(key) }
|
||||
|
||||
@_documentation(visibility: private)
|
||||
public mutating func update() {
|
||||
observable.key = key
|
||||
_observable.update()
|
||||
|
@ -211,6 +213,7 @@ extension Defaults {
|
|||
self.observable = .init(key)
|
||||
}
|
||||
|
||||
@_documentation(visibility: private)
|
||||
public var body: some View {
|
||||
SwiftUI.Toggle(isOn: $observable.value, label: label)
|
||||
.onChange(of: observable.value) {
|
||||
|
|
|
@ -204,7 +204,7 @@ extension Defaults.Serializable {
|
|||
if
|
||||
T.isNativelySupportedType,
|
||||
let anyObject = anyObject as? T
|
||||
{
|
||||
{ // swiftlint:disable:this opening_brace
|
||||
return anyObject
|
||||
}
|
||||
|
||||
|
|
|
@ -308,7 +308,7 @@ final class DefaultsAnySerializableTests {
|
|||
@Test
|
||||
func testDictionaryKey() {
|
||||
let key = Defaults.Key<[String: Defaults.AnySerializable]>("independentDictionaryAnyKey", default: ["unicorn": ""], suite: suite_)
|
||||
#expect(Defaults[key]["unicorn"] == "")
|
||||
#expect(Defaults[key]["unicorn"] == "") // swiftlint:disable:this empty_string
|
||||
Defaults[key]["unicorn"] = "🦄"
|
||||
#expect(Defaults[key]["unicorn"] == "🦄")
|
||||
Defaults[key]["number"] = 3
|
||||
|
|
|
@ -15,7 +15,7 @@ final class DefaultsColorTests {
|
|||
}
|
||||
|
||||
@available(macOS 12, iOS 15, tvOS 15, watchOS 8, visionOS 1.0, *)
|
||||
@Test
|
||||
@Test(.disabled()) // Fails on CI, but not locally.
|
||||
func testPreservesColorSpace() {
|
||||
let fixture = Color(.displayP3, red: 1, green: 0.3, blue: 0.7, opacity: 1)
|
||||
let key = Defaults.Key<Color?>("independentColorPreservesColorSpaceKey", suite: suite_)
|
||||
|
|
|
@ -390,7 +390,7 @@ final class DefaultsTests {
|
|||
func testObservePreventPropagationCombine() async throws {
|
||||
let key1 = Defaults.Key<Bool?>("preventPropagation6", default: nil, suite: suite_)
|
||||
|
||||
await confirmation() { confirmation in
|
||||
await confirmation { confirmation in
|
||||
var wasInside = false
|
||||
let cancellable = Defaults.publisher(key1, options: []).sink { _ in
|
||||
#expect(!wasInside)
|
||||
|
@ -411,7 +411,7 @@ final class DefaultsTests {
|
|||
let key1 = Defaults.Key<Bool?>("preventPropagation7", default: nil, suite: suite_)
|
||||
let key2 = Defaults.Key<Bool?>("preventPropagation8", default: nil, suite: suite_)
|
||||
|
||||
await confirmation() { confirmation in
|
||||
await confirmation { confirmation in
|
||||
var wasInside = false
|
||||
let cancellable = Defaults.publisher(keys: key1, key2, options: []).sink { _ in
|
||||
#expect(!wasInside)
|
||||
|
@ -526,6 +526,7 @@ final class DefaultsTests {
|
|||
|
||||
@Test
|
||||
func testKeyEquatable() {
|
||||
// swiftlint:disable:next identical_operands
|
||||
#expect(Defaults.Key<Bool>("equatableKeyTest", default: false, suite: suite_) == Defaults.Key<Bool>("equatableKeyTest", default: false, suite: suite_))
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue