Skip to content

Commit 6de07bf

Browse files
authored
Fix bug where scoped overrides not used when calling refresh if cache is missing (#186)
* Refresh now uses a store with the current scope if cache is missing * Fix compilation in Swift 5
1 parent 9e59598 commit 6de07bf

File tree

3 files changed

+67
-14
lines changed

3 files changed

+67
-14
lines changed

Sources/Atoms/Core/StoreContext.swift

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ internal struct StoreContext {
7171
let (key, _) = lookupAtomKeyAndOverride(of: atom)
7272

7373
if let cache = lookupCache(of: atom, for: key) {
74-
switchContext(scope: cache.initializedScope)
74+
switchContext(with: cache)
7575
.update(atom: atom, for: key, cache: cache, newValue: value)
7676
}
7777
}
@@ -82,7 +82,7 @@ internal struct StoreContext {
8282

8383
if let cache = lookupCache(of: atom, for: key) {
8484
let newValue = mutating(cache.value, body)
85-
switchContext(scope: cache.initializedScope)
85+
switchContext(with: cache)
8686
.update(atom: atom, for: key, cache: cache, newValue: newValue)
8787
}
8888
}
@@ -131,7 +131,7 @@ internal struct StoreContext {
131131
func refresh<Node: AsyncAtom>(_ atom: Node) async -> Node.Produced {
132132
let (key, override) = lookupAtomKeyAndOverride(of: atom)
133133
let cache = lookupCache(of: atom, for: key)
134-
let localContext = switchContext(scope: cache?.initializedScope)
134+
let localContext = cache.map(switchContext) ?? self
135135
let context = localContext.prepareForTransaction(of: atom, for: key)
136136

137137
let value: Node.Produced
@@ -162,7 +162,7 @@ internal struct StoreContext {
162162
func refresh<Node: Refreshable>(_ atom: Node) async -> Node.Produced {
163163
let (key, _) = lookupAtomKeyAndOverride(of: atom)
164164
let cache = lookupCache(of: atom, for: key)
165-
let localContext = switchContext(scope: cache?.initializedScope)
165+
let localContext = cache.map(switchContext) ?? self
166166
let state = localContext.getState(of: atom, for: key)
167167
let currentContext = AtomCurrentContext(store: localContext)
168168

@@ -193,7 +193,7 @@ internal struct StoreContext {
193193
let (key, override) = lookupAtomKeyAndOverride(of: atom)
194194

195195
if let cache = lookupCache(of: atom, for: key) {
196-
let localContext = switchContext(scope: cache.initializedScope)
196+
let localContext = switchContext(with: cache)
197197
let newValue = localContext.getValue(of: atom, for: key, override: override)
198198
localContext.update(atom: atom, for: key, cache: cache, newValue: newValue)
199199
}
@@ -202,10 +202,12 @@ internal struct StoreContext {
202202
@usableFromInline
203203
func reset(_ atom: some Resettable) {
204204
let (key, _) = lookupAtomKeyAndOverride(of: atom)
205-
let cache = lookupCache(of: atom, for: key)
206-
let localContext = switchContext(scope: cache?.initializedScope)
207-
let currentContext = AtomCurrentContext(store: localContext)
208-
atom.reset(context: currentContext)
205+
206+
if let cache = lookupCache(of: atom, for: key) {
207+
let localContext = switchContext(with: cache)
208+
let currentContext = AtomCurrentContext(store: localContext)
209+
atom.reset(context: currentContext)
210+
}
209211
}
210212

211213
@usableFromInline
@@ -319,7 +321,7 @@ private extension StoreContext {
319321

320322
func updatePropagation(for key: AtomKey, cache: some AtomCacheProtocol) {
321323
// Dependents must be updated with the scope at which they were initialised.
322-
let localContext = switchContext(scope: cache.initializedScope)
324+
let localContext = switchContext(with: cache)
323325

324326
// Overridden atoms don't get updated transitively.
325327
let newValue = localContext.getValue(of: cache.atom, for: key, override: nil)
@@ -419,7 +421,7 @@ private extension StoreContext {
419421

420422
if let state, let cache {
421423
// It must call release effect with the scope at which they were initialised.
422-
let localContext = switchContext(scope: cache.initializedScope)
424+
let localContext = switchContext(with: cache)
423425
let currentContext = AtomCurrentContext(store: localContext)
424426
state.effect.released(context: currentContext)
425427
}
@@ -507,7 +509,7 @@ private extension StoreContext {
507509

508510
return AtomProducerContext(store: self, transactionState: transactionState) { newValue in
509511
if let cache = lookupCache(of: atom, for: key) {
510-
switchContext(scope: cache.initializedScope)
512+
switchContext(with: cache)
511513
.update(atom: atom, for: key, cache: cache, newValue: newValue)
512514
}
513515
}
@@ -644,11 +646,11 @@ private extension StoreContext {
644646
}
645647
}
646648

647-
func switchContext(scope: Scope?) -> StoreContext {
649+
func switchContext(with cache: some AtomCacheProtocol) -> StoreContext {
648650
StoreContext(
649651
store: store,
650652
rootScope: rootScope,
651-
currentScope: scope
653+
currentScope: cache.initializedScope
652654
)
653655
}
654656
}

Tests/AtomsTests/Attribute/RefreshableTests.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,34 @@ final class RefreshableTests: XCTestCase {
9696
}
9797
}
9898

99+
@MainActor
100+
func testCustomRefreshNotCached() async {
101+
let store = AtomStore()
102+
let dependencyAtom = TestValueAtom(value: 1)
103+
let atom = TestCustomRefreshableAtom { _ in
104+
0
105+
} refresh: { context in
106+
context.read(dependencyAtom)
107+
}
108+
let rootScopeToken = ScopeKey.Token()
109+
let scopeToken = ScopeKey.Token()
110+
let scopedContext =
111+
StoreContext
112+
.root(store: store, scopeKey: rootScopeToken.key)
113+
.scoped(
114+
scopeID: ScopeID(DefaultScopeID()),
115+
scopeKey: scopeToken.key,
116+
observers: [],
117+
overrideContainer: OverrideContainer()
118+
.addingOverride(for: dependencyAtom) { _ in
119+
2
120+
}
121+
)
122+
123+
let value = await scopedContext.refresh(atom)
124+
XCTAssertEqual(value, 2)
125+
}
126+
99127
@MainActor
100128
func testTransitiveRefresh() async {
101129
let parentAtom = TestTaskAtom { 0 }

Tests/AtomsTests/Core/StoreContextTests.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,29 @@ final class StoreContextTests: XCTestCase {
321321
)
322322
}
323323

324+
@MainActor
325+
func testRefreshNotCached() async {
326+
let store = AtomStore()
327+
let atom = TestAsyncPhaseAtom<Int, Never> { .success(0) }
328+
let rootScopeToken = ScopeKey.Token()
329+
let scopeToken = ScopeKey.Token()
330+
let scopedContext =
331+
StoreContext
332+
.root(store: store, scopeKey: rootScopeToken.key)
333+
.scoped(
334+
scopeID: ScopeID(DefaultScopeID()),
335+
scopeKey: scopeToken.key,
336+
observers: [],
337+
overrideContainer: OverrideContainer()
338+
.addingOverride(for: atom) { _ in
339+
.success(1)
340+
}
341+
)
342+
343+
let phase = await scopedContext.refresh(atom)
344+
XCTAssertEqual(phase.value, 1)
345+
}
346+
324347
@MainActor
325348
func testReset() {
326349
let store = AtomStore()

0 commit comments

Comments
 (0)