Add variadic generics version of `Defaults.updates()` (#191)

This commit is contained in:
Sindre Sorhus 2024-10-04 17:27:57 +07:00 committed by GitHub
parent 1f693cde80
commit a89f799930
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 82 additions and 2 deletions

View File

@ -292,13 +292,64 @@ extension Defaults {
}
}
// 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
Task {
for await (foo, bar) in Defaults.updates([.foo, .bar]) {
print("Values changed:", foo, bar)
}
}
```
*/
public static func updates<each Value: Serializable>(
_ keys: repeat Key<each Value>,
initial: Bool = true
) -> AsyncStream<(repeat each Value)> {
.init { continuation in
func getCurrentValues() -> (repeat each Value) {
(repeat self[each keys])
}
var observations = [DefaultsObservation]()
if initial {
continuation.yield(getCurrentValues())
}
for key in repeat (each keys) {
let observation = DefaultsObservation(object: key.suite, key: key.name) { _, _ in
continuation.yield(getCurrentValues())
}
observation.start(options: [])
observations.append(observation)
}
let immutableObservations = observations
continuation.onTermination = { _ in
// `invalidate()` should be thread-safe, but it is not in practice.
DispatchQueue.main.async {
for observation in immutableObservations {
observation.invalidate()
}
}
}
}
}
// We still keep this as it can be useful to pass a dynamic array of keys.
/**
Observe updates to multiple stored values without receiving the 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
Task {
for await _ in Defaults.updates([.foo, .bar]) {
@ -307,7 +358,7 @@ extension Defaults {
}
```
- Note: This does not include which of the values changed. Use ``Defaults/updates(_:initial:)-88orv`` if you need that. You could use [`merge`](https://github.com/apple/swift-async-algorithms/blob/main/Sources/AsyncAlgorithms/AsyncAlgorithms.docc/Guides/Merge.md) to merge them into a single sequence.
- Note: This does not include which of the values changed. Use ``Defaults/updates(_:initial:)-l03o`` if you need that.
*/
public static func updates(
_ keys: [_AnyKey],

View File

@ -52,6 +52,7 @@ typealias Default = _Default
### Methods
- ``Defaults/updates(_:initial:)-88orv``
- ``Defaults/updates(_:initial:)-l03o``
- ``Defaults/updates(_:initial:)-1mqkb``
- ``Defaults/reset(_:)-7jv5v``
- ``Defaults/reset(_:)-7es1e``

View File

@ -581,6 +581,34 @@ final class DefaultsTests {
let count = await counter.count
#expect(count == 2)
}
@available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, visionOS 1.0, *)
@Test
func testUpdatesMultipleKeysVariadic() async {
let key1 = Defaults.Key<Bool>("updatesMultipleKeyVariadic1", default: false, suite: suite_)
let key2 = Defaults.Key<Bool>("updatesMultipleKeyVariadic2", default: false, suite: suite_)
let counter = Counter()
async let waiter: Void = {
for await (_, _) in Defaults.updates(key1, key2, initial: false) {
await counter.increment()
if await counter.count == 2 {
break
}
}
}()
try? await Task.sleep(for: .seconds(0.1))
Defaults[key1] = true
Defaults[key2] = true
await waiter
let count = await counter.count
#expect(count == 2)
}
}
actor Counter {