Skip to content

Commit

Permalink
Add variadic generics version of Defaults.updates()
Browse files Browse the repository at this point in the history
  • Loading branch information
sindresorhus committed Sep 30, 2024
1 parent 32b0c67 commit 1637cf4
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 2 deletions.
55 changes: 53 additions & 2 deletions Sources/Defaults/Defaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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]) {
Expand All @@ -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],
Expand Down
1 change: 1 addition & 0 deletions Sources/Defaults/Documentation.docc/Documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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``
Expand Down
28 changes: 28 additions & 0 deletions Tests/DefaultsTests/DefaultsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 1637cf4

Please sign in to comment.