Skip to content

Commit d294cfb

Browse files
committed
Add variadic generics version of Defaults.updates()
1 parent d160249 commit d294cfb

File tree

3 files changed

+82
-2
lines changed

3 files changed

+82
-2
lines changed

Sources/Defaults/Defaults.swift

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,13 +292,64 @@ extension Defaults {
292292
}
293293
}
294294

295-
// We still keep this as it can be useful to pass a dynamic array of keys.
296295
/**
297296
Observe updates to multiple stored values.
298297

299298
- Parameter keys: The keys to observe updates from.
300299
- Parameter initial: Trigger an initial event on creation. This can be useful for setting default values on controls.
301300

301+
```swift
302+
Task {
303+
for await (foo, bar) in Defaults.updates([.foo, .bar]) {
304+
print("Values changed:", foo, bar)
305+
}
306+
}
307+
```
308+
*/
309+
public static func updates<each Value: Serializable>(
310+
_ keys: repeat Key<each Value>,
311+
initial: Bool = true
312+
) -> AsyncStream<(repeat each Value)> {
313+
.init { continuation in
314+
func getCurrentValues() -> (repeat each Value) {
315+
(repeat self[each keys])
316+
}
317+
318+
var observations = [DefaultsObservation]()
319+
320+
if initial {
321+
continuation.yield(getCurrentValues())
322+
}
323+
324+
for key in repeat (each keys) {
325+
let observation = DefaultsObservation(object: key.suite, key: key.name) { _, _ in
326+
continuation.yield(getCurrentValues())
327+
}
328+
329+
observation.start(options: [])
330+
observations.append(observation)
331+
}
332+
333+
let immutableObservations = observations
334+
335+
continuation.onTermination = { _ in
336+
// `invalidate()` should be thread-safe, but it is not in practice.
337+
DispatchQueue.main.async {
338+
for observation in immutableObservations {
339+
observation.invalidate()
340+
}
341+
}
342+
}
343+
}
344+
}
345+
346+
// We still keep this as it can be useful to pass a dynamic array of keys.
347+
/**
348+
Observe updates to multiple stored values without receiving the values.
349+
350+
- Parameter keys: The keys to observe updates from.
351+
- Parameter initial: Trigger an initial event on creation. This can be useful for setting default values on controls.
352+
302353
```swift
303354
Task {
304355
for await _ in Defaults.updates([.foo, .bar]) {
@@ -307,7 +358,7 @@ extension Defaults {
307358
}
308359
```
309360

310-
- 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.
361+
- Note: This does not include which of the values changed. Use ``Defaults/updates(_:initial:)-l03o`` if you need that.
311362
*/
312363
public static func updates(
313364
_ keys: [_AnyKey],

Sources/Defaults/Documentation.docc/Documentation.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ typealias Default = _Default
5252
### Methods
5353

5454
- ``Defaults/updates(_:initial:)-88orv``
55+
- ``Defaults/updates(_:initial:)-l03o``
5556
- ``Defaults/updates(_:initial:)-1mqkb``
5657
- ``Defaults/reset(_:)-7jv5v``
5758
- ``Defaults/reset(_:)-7es1e``

Tests/DefaultsTests/DefaultsTests.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,34 @@ final class DefaultsTests {
580580
let count = await counter.count
581581
#expect(count == 2)
582582
}
583+
584+
@available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, visionOS 1.0, *)
585+
@Test
586+
func testUpdatesMultipleKeysVariadic() async {
587+
let key1 = Defaults.Key<Bool>("updatesMultipleKeyVariadic1", default: false, suite: suite_)
588+
let key2 = Defaults.Key<Bool>("updatesMultipleKeyVariadic2", default: false, suite: suite_)
589+
let counter = Counter()
590+
591+
async let waiter: Void = {
592+
for await (_, _) in Defaults.updates(key1, key2, initial: false) {
593+
await counter.increment()
594+
595+
if await counter.count == 2 {
596+
break
597+
}
598+
}
599+
}()
600+
601+
try? await Task.sleep(for: .seconds(0.1))
602+
603+
Defaults[key1] = true
604+
Defaults[key2] = true
605+
606+
await waiter
607+
608+
let count = await counter.count
609+
#expect(count == 2)
610+
}
583611
}
584612

585613
actor Counter {

0 commit comments

Comments
 (0)