Skip to content

Commit 10f4e42

Browse files
authored
Scoped Observe (#109)
1 parent 6047664 commit 10f4e42

File tree

10 files changed

+36
-23
lines changed

10 files changed

+36
-23
lines changed

Examples/Packages/iOS/Sources/ExampleTimeTravel/ExampleTimeTravel.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ struct TimeTravelDebug<Content: View>: View {
8989
}
9090
.padding()
9191
}
92-
.observe { snapshot in
92+
.scopedObserve { snapshot in
9393
Task {
9494
snapshots = Array(snapshots.prefix(position + 1))
9595
snapshots.append(snapshot)

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,7 +1214,7 @@ See the [Override Atoms](#override-atoms) and [Debugging](#debugging) sections f
12141214
AtomScope {
12151215
CounterView()
12161216
}
1217-
.observe { snapshot in
1217+
.scopedObserve { snapshot in
12181218
if let count = snapshot.lookup(CounterAtom()) {
12191219
print(count)
12201220
}
@@ -1443,8 +1443,8 @@ var debugButton: some View {
14431443
}
14441444
```
14451445

1446-
Or, you can observe all updates of atoms and always continue to receive `Snapshots` at that point in time through `observe(_:)` modifier of [AtomRoot](#atomroot) or [AtomScope](#atomscope).
1447-
Note that observing in `AtomRoot` will receive all atom updates that happened in the whole app, but observing in `AtomScope` will only receive atoms used in the descendant views.
1446+
Or, you can observe all state changes and always continue to receive `Snapshots` at that point in time with `observe(_:)` modifier of [AtomRoot](#atomroot) or with `scopedObserve(_:)` modifier of [AtomScope](#atomscope).
1447+
Note that observing in `AtomRoot` will receive every state changes that happened in the whole app, but observing in `AtomScope` will observe changes of atoms used in the scope.
14481448

14491449
```swift
14501450
AtomRoot {

Sources/Atoms/AtomRoot.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,8 @@ public struct AtomRoot<Content: View>: View {
108108
}
109109
}
110110

111-
/// For debugging purposes, each time there is a change in the internal state,
112-
/// a snapshot is taken that captures the state of the atoms and their dependency graph
113-
/// at that point in time.
111+
/// Observes the state changes with a snapshot that captures the whole atom states and
112+
/// their dependency graph at the point in time for debugging purposes.
114113
///
115114
/// - Parameter onUpdate: A closure to handle a snapshot of recent updates.
116115
///
@@ -178,6 +177,7 @@ private extension AtomRoot {
178177
scopeKey: ScopeKey(token: state.token),
179178
inheritedScopeKeys: [:],
180179
observers: observers,
180+
scopedObservers: [],
181181
overrides: overrides
182182
)
183183
)
@@ -206,6 +206,7 @@ private extension AtomRoot {
206206
scopeKey: ScopeKey(token: state.token),
207207
inheritedScopeKeys: [:],
208208
observers: observers,
209+
scopedObservers: [],
209210
overrides: overrides
210211
)
211212
)

Sources/Atoms/AtomScope.swift

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ import SwiftUI
22

33
/// A view to override or monitor atoms in scope.
44
///
5-
/// This view allows you to monitor changes of atoms used in descendant views by``AtomScope/observe(_:)``.
5+
/// This view allows you to monitor changes of atoms used in descendant views by``AtomScope/scopedObserve(_:)``.
66
///
77
/// ```swift
88
/// AtomScope {
99
/// CounterView()
1010
/// }
11-
/// .observe { snapshot in
11+
/// .scopedObserve { snapshot in
1212
/// if let count = snapshot.lookup(CounterAtom()) {
1313
/// print(count)
1414
/// }
@@ -87,17 +87,16 @@ public struct AtomScope<Content: View>: View {
8787
}
8888
}
8989

90-
/// For debugging purposes, each time there is a change in the internal state,
91-
/// a snapshot is taken that captures the state of the atoms and their dependency graph
92-
/// at that point in time.
90+
/// Observes the state changes with a snapshot that captures the whole atom states and
91+
/// their dependency graph at the point in time for debugging purposes.
9392
///
94-
/// Note that unlike observed by ``AtomRoot``, this is triggered only by internal state changes
95-
/// caused by atoms use in this scope.
93+
/// Note that unlike ``AtomRoot/observe(_:)``, this observes only the state changes caused by atoms
94+
/// used in this scope.
9695
///
9796
/// - Parameter onUpdate: A closure to handle a snapshot of recent updates.
9897
///
9998
/// - Returns: The self instance.
100-
public func observe(_ onUpdate: @escaping @MainActor (Snapshot) -> Void) -> Self {
99+
public func scopedObserve(_ onUpdate: @escaping @MainActor (Snapshot) -> Void) -> Self {
101100
mutating(self) { $0.observers.append(Observer(onUpdate: onUpdate)) }
102101
}
103102

@@ -181,7 +180,7 @@ private extension AtomScope {
181180
content.environment(
182181
\.store,
183182
context._store.inherited(
184-
observers: observers,
183+
scopedObservers: observers,
185184
overrides: overrides
186185
)
187186
)

Sources/Atoms/Context/AtomTestContext.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ import Foundation
55
///
66
/// This context has a store that manages the state of atoms, so it can be used to test individual
77
/// atoms or their interactions with other atoms without depending on the SwiftUI view tree.
8-
/// Furthermore, unlike other contexts, it is possible to override or observe changes in atoms
9-
/// through this context.
8+
/// Furthermore, unlike other contexts, it is possible to override atoms through this context.
109
@MainActor
1110
public struct AtomTestContext: AtomWatchableContext {
1211
private let location: SourceLocation
@@ -444,6 +443,7 @@ internal extension AtomTestContext {
444443
scopeKey: ScopeKey(token: _state.token),
445444
inheritedScopeKeys: [:],
446445
observers: [],
446+
scopedObservers: [],
447447
overrides: _state.overrides
448448
)
449449
}

Sources/Atoms/Core/Environment.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ private struct StoreEnvironmentKey: EnvironmentKey {
1414
scopeKey: ScopeKey(token: ScopeKey.Token()),
1515
inheritedScopeKeys: [:],
1616
observers: [],
17+
scopedObservers: [],
1718
overrides: [:],
1819
enablesAssertion: true
1920
)

Sources/Atoms/Core/StoreContext.swift

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ internal struct StoreContext {
77
private let scopeKey: ScopeKey
88
private let inheritedScopeKeys: [ScopeID: ScopeKey]
99
private let observers: [Observer]
10+
private let scopedObservers: [Observer]
1011
private let overrides: [OverrideKey: any AtomOverrideProtocol]
1112
private let enablesAssertion: Bool
1213

@@ -15,26 +16,29 @@ internal struct StoreContext {
1516
scopeKey: ScopeKey,
1617
inheritedScopeKeys: [ScopeID: ScopeKey],
1718
observers: [Observer],
19+
scopedObservers: [Observer],
1820
overrides: [OverrideKey: any AtomOverrideProtocol],
1921
enablesAssertion: Bool = false
2022
) {
2123
self.weakStore = store
2224
self.scopeKey = scopeKey
2325
self.inheritedScopeKeys = inheritedScopeKeys
2426
self.observers = observers
27+
self.scopedObservers = scopedObservers
2528
self.overrides = overrides
2629
self.enablesAssertion = enablesAssertion
2730
}
2831

2932
func inherited(
30-
observers: [Observer],
33+
scopedObservers: [Observer],
3134
overrides: [OverrideKey: any AtomOverrideProtocol]
3235
) -> StoreContext {
3336
StoreContext(
3437
weakStore,
3538
scopeKey: scopeKey,
3639
inheritedScopeKeys: inheritedScopeKeys,
37-
observers: self.observers + observers,
40+
observers: observers,
41+
scopedObservers: self.scopedObservers + scopedObservers,
3842
overrides: self.overrides.merging(overrides) { $1 },
3943
enablesAssertion: enablesAssertion
4044
)
@@ -50,7 +54,8 @@ internal struct StoreContext {
5054
weakStore,
5155
scopeKey: scopeKey,
5256
inheritedScopeKeys: mutating(inheritedScopeKeys) { $0[scopeID] = scopeKey },
53-
observers: self.observers + observers,
57+
observers: self.observers,
58+
scopedObservers: observers,
5459
overrides: overrides,
5560
enablesAssertion: enablesAssertion
5661
)
@@ -612,13 +617,13 @@ private extension StoreContext {
612617
}
613618

614619
func notifyUpdateToObservers() {
615-
guard !observers.isEmpty else {
620+
guard !observers.isEmpty || !scopedObservers.isEmpty else {
616621
return
617622
}
618623

619624
let snapshot = snapshot()
620625

621-
for observer in observers {
626+
for observer in observers + scopedObservers {
622627
observer.onUpdate(snapshot)
623628
}
624629
}

Sources/Atoms/Deprecated.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,9 @@ public extension AtomScope {
2727
AtomRoot(storesIn: store, content: content)
2828
}
2929
}
30+
31+
@available(*, deprecated, renamed: "scopedObserve(_:)")
32+
func observe(_ onUpdate: @escaping @MainActor (Snapshot) -> Void) -> Self {
33+
scopedObserve(onUpdate)
34+
}
3035
}

Tests/AtomsTests/Core/StoreContextTests.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ final class StoreContextTests: XCTestCase {
1616
scopeKey: scopeKey,
1717
inheritedScopeKeys: [:],
1818
observers: [],
19+
scopedObservers: [],
1920
overrides: [
2021
OverrideKey(atom): AtomOverride<TestAtom<Int>> { _ in
2122
10

Tests/AtomsTests/Utilities/Util.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ extension StoreContext {
6868
scopeKey: ScopeKey(token: ScopeKey.Token()),
6969
inheritedScopeKeys: [:],
7070
observers: observers,
71+
scopedObservers: [],
7172
overrides: overrides
7273
)
7374
}

0 commit comments

Comments
 (0)