Skip to content

Commit 39c3496

Browse files
committed
Add variadic generics version of Defaults.updates()
1 parent 1f693cd commit 39c3496

File tree

3 files changed

+82
-2
lines changed

3 files changed

+82
-2
lines changed

Sources/Defaults/Defaults.swift

+53-2
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

+1
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

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

586614
actor Counter {

0 commit comments

Comments
 (0)