Skip to content

Commit f1b426d

Browse files
committed
Cancel effects when root store deinits.
1 parent 8ffc4df commit f1b426d

File tree

3 files changed

+19
-13
lines changed

3 files changed

+19
-13
lines changed

Package.resolved

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Sources/ComposableArchitecture/RootStore.swift

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,14 @@ public final class RootStore {
5252
defer { index += 1 }
5353
let action = self.bufferedActions[index] as! Action
5454
let effect = reducer.reduce(into: &currentState, action: action)
55+
let uuid = UUID()
5556

5657
switch effect.operation {
5758
case .none:
5859
break
5960
case let .publisher(publisher):
6061
var didComplete = false
6162
let boxedTask = Box<Task<Void, Never>?>(wrappedValue: nil)
62-
let uuid = UUID()
6363
let effectCancellable = withEscapedDependencies { continuation in
6464
publisher
6565
.receive(on: UIScheduler.shared)
@@ -88,11 +88,13 @@ public final class RootStore {
8888
}
8989
boxedTask.wrappedValue = task
9090
tasks.withValue { $0.append(task) }
91-
self.effectCancellables[uuid] = effectCancellable
91+
self.effectCancellables[uuid] = AnyCancellable {
92+
task.cancel()
93+
}
9294
}
9395
case let .run(priority, operation):
9496
withEscapedDependencies { continuation in
95-
let task = Task(priority: priority) { @MainActor in
97+
let task = Task(priority: priority) { @MainActor [weak self] in
9698
let isCompleted = LockIsolated(false)
9799
defer { isCompleted.setValue(true) }
98100
await operation(
@@ -118,14 +120,18 @@ public final class RootStore {
118120
)
119121
}
120122
if let task = continuation.yield({
121-
self.send(effectAction, originatingFrom: action)
123+
self?.send(effectAction, originatingFrom: action)
122124
}) {
123125
tasks.withValue { $0.append(task) }
124126
}
125127
}
126128
)
129+
self?.effectCancellables[uuid] = nil
127130
}
128131
tasks.withValue { $0.append(task) }
132+
self.effectCancellables[uuid] = AnyCancellable {
133+
task.cancel()
134+
}
129135
}
130136
}
131137
}

Tests/ComposableArchitectureTests/StoreLifetimeTests.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@ final class StoreLifetimeTests: BaseTCATestCase {
6969

7070
@MainActor
7171
func testStoreDeinit_RunningEffect() async {
72-
XCTTODO(
73-
"We would like for this to pass, but it requires full deprecation of uncached child stores"
74-
)
72+
// XCTTODO(
73+
// "We would like for this to pass, but it requires full deprecation of uncached child stores"
74+
// )
7575
Logger.shared.isEnabled = true
7676
let effectFinished = self.expectation(description: "Effect finished")
7777
do {
@@ -99,9 +99,9 @@ final class StoreLifetimeTests: BaseTCATestCase {
9999

100100
@MainActor
101101
func testStoreDeinit_RunningCombineEffect() async {
102-
XCTTODO(
103-
"We would like for this to pass, but it requires full deprecation of uncached child stores"
104-
)
102+
// XCTTODO(
103+
// "We would like for this to pass, but it requires full deprecation of uncached child stores"
104+
// )
105105
Logger.shared.isEnabled = true
106106
let effectFinished = self.expectation(description: "Effect finished")
107107
do {

0 commit comments

Comments
 (0)