@@ -33,7 +33,7 @@ PersistentKeyValueKit is backed by a robust test suite.
3333- [x] Persistence for any type that conforms to ` KeyValuePersistible ` .
3434- [x] Universal interface for ` UserDefaults ` and ` NSUbiquitousKeyValueStore ` .
3535- [x] Type-safe property wrapper and view modifier for SwiftUI.
36- - [x] AsyncSequence for observing key changes in any context.
36+ - [x] AsyncSequence and Combine publishers for observing key changes in any context.
3737- [x] Built-in support for all primitive (i.e. property list) types.
3838- [x] Built-in representations for all common ways to persist values.
3939- [x] Keys that are only mutable in Debug builds.
@@ -126,6 +126,15 @@ for await runtimeColorScheme in userDefaults.values(for: .runtimeColorScheme) {
126126}
127127```
128128
129+ Observe it from Combine.
130+
131+ ``` swift
132+ let cancellable = userDefaults.publisher (for : .runtimeColorScheme )
133+ .sink { runtimeColorScheme in
134+ apply (runtimeColorScheme)
135+ }
136+ ```
137+
129138## Usage
130139
131140### Keys
@@ -384,9 +393,12 @@ extension PersistentKeyProtocol where Self == PersistentKey<Date?> {
384393}
385394```
386395
387- ### SwiftUI
396+ ### Observation
397+
398+ PersistentKeyValueKit exposes store observation in three forms: a SwiftUI property wrapper, an `AsyncSequence`, and a
399+ Combine publisher. Pick the one that matches the code that owns cancellation.
388400
389- #### Property Wrapper
401+ #### SwiftUI State
390402
391403`PersistentValue` is a property wrapper that provides a type- safe way to access and modify values from `UserDefaults` or
392404`NSUbiquitousKeyValueStore` in SwiftUI views. It supports automatic observation and updates whenever the value changes
@@ -407,10 +419,10 @@ var isAppStoreRatingEnabled: Bool
407419var isAppStoreRatingEnabled: Bool
408420```
409421
410- ##### View Modifier
422+ ##### Default Store
411423
412- A view modifier is provided to set the default store used by any `@PersistentValue ` property wrapper in the view (or
413- its descendants). The default store can be overridden by supplying one directly in the `@PersistentValue ` declaration .
424+ Use `. defaultPersistentKeyValueStore (_:)` to set the default store for any `@PersistentValue ` property wrapper in the
425+ view or its descendants. Passing a store directly to `@PersistentValue ` still takes precedence .
414426
415427e.g.
416428
@@ -423,10 +435,9 @@ extension App: SwiftUI.App {
423435}
424436```
425437
426- ### `AsyncSequence`
438+ #### Async Tasks
427439
428- `PersistentKeyValues` observes a persistent key as an `AsyncSequence`. Use it outside SwiftUI when you want key changes
429- without writing KVO or `NotificationCenter` code.
440+ `PersistentKeyValues` is an `AsyncSequence` for one persistent key. Use it when an async task owns cancellation.
430441
431442The same sequence is available from the store.
432443
@@ -468,6 +479,48 @@ for await username in UserDefaults.standard.changes(for: .username, bufferingPol
468479Each iterator registers with the store. It deregisters when iteration ends, the iterator is released, or the task is
469480cancelled. Iterate from a cancellable task and cancel it when the owner no longer needs updates.
470481
482+ #### Combine Pipelines
483+
484+ `PersistentKeyValuePublisher` is a Combine `Publisher` for one persistent key. Use it when a Combine subscription owns
485+ cancellation.
486+
487+ The same publisher is available from the store.
488+
489+ ```swift
490+ let cancellable = UserDefaults.standard.publisher (for : .username )
491+ .sink { username in
492+ usernameLabel.text = username
493+ }
494+ ```
495+
496+ It is also available from the key.
497+
498+ ```swift
499+ let usernameKey: PersistentKey<String > = .username
500+
501+ let cancellable = usernameKey.publisher (in : UserDefaults.standard )
502+ .sink { username in
503+ usernameLabel.text = username
504+ }
505+ ```
506+
507+ By default , `publisher (for: )` and `publisher (in: )` emit the current value when demand is requested, then later changes.
508+
509+ Use `changesPublisher (for: )` or `changesPublisher (in: )` to skip the current value and observe only later changes.
510+
511+ ```swift
512+ let cancellable = UserDefaults.standard .changesPublisher (for : .username )
513+ .sink { username in
514+ handleChange (username : username)
515+ }
516+ ```
517+
518+ When downstream demand is exhausted, later changes are coalesced. The next demand receives the latest current value
519+ instead of an unbounded backlog of intermediate values.
520+
521+ Each subscription registers with the store. It deregisters when the subscription is cancelled or released. Use Combine
522+ operators such as `receive (on: )` when a subscriber needs delivery on a specific scheduler.
523+
471524### `UserDefaults` Registration
472525
473526PersistentKeyValueKit supports traditional `UserDefaults` registration. The default value of the key will be registered
@@ -563,12 +616,11 @@ affordances for property list safety or proxy representations—but it is availa
563616
564617### Limited `NSUbiquitousKeyValueStore` Observability
565618
566- There is no platform support for observing changes to keys in `NSUbiquitousKeyValueStore`. The only affordance is
567- listening for external changes from other devices. PersistentKeyValueKit implements observability for all mutations
568- made through the framework: any ` @PersistentValue ` or `AsyncSequence` using `NSUbiquitousKeyValueStore` will
569- automatically update with any changes made by PersistentKeyValueKit anywhere, on any device. However, any changes to
570- `NSUbiquitousKeyValueStore` made outside of the framework will not be automatically reflected in `@PersistentValue`
571- properties or `AsyncSequence` iterations.
619+ There is no platform support for observing individual keys in `NSUbiquitousKeyValueStore`. The only affordance is
620+ listening for external changes from other devices. PersistentKeyValueKit observes mutations made through the package:
621+ ` @PersistentValue ` , `PersistentKeyValues` , and `PersistentKeyValuePublisher` receive changes made by
622+ PersistentKeyValueKit on any device. They do not receive mutations written directly to `NSUbiquitousKeyValueStore`
623+ outside this package.
572624
573625## Contributions
574626
0 commit comments