Skip to content

Commit 302408c

Browse files
committed
Add threading warning to Store.send (#772)
* Add threading warning to Store.send Any async Effect (including fireAndForget) could result in a runtime crash when updating internal state like `effectCancellables` This adds a debug warning to let users know * Remove comment
1 parent c4134c9 commit 302408c

File tree

1 file changed

+33
-2
lines changed

1 file changed

+33
-2
lines changed

Sources/ComposableArchitecture/Store.swift

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -353,9 +353,9 @@ public final class Store<State, Action> {
353353
)
354354
localStore.parentDisposable = self.producer.startWithValues {
355355
[weak localStore] state in
356-
guard let localStore = localStore else { return }
356+
guard let localStore = localStore else { return }
357357
localStore.state = extractLocalState(state) ?? localStore.state
358-
}
358+
}
359359
return localStore
360360
}
361361
}
@@ -390,11 +390,42 @@ public final class Store<State, Action> {
390390
var didComplete = false
391391
let uuid = UUID()
392392

393+
#if DEBUG
394+
let initalThread = Thread.current
395+
initalThread.threadDictionary[uuid] = true
396+
#endif
397+
393398
let observer = Signal<Action, Never>.Observer(
394399
value: { [weak self] action in
395400
self?.send(action)
396401
},
397402
completed: { [weak self] in
403+
#if DEBUG
404+
if Thread.current.threadDictionary[uuid] == nil {
405+
breakpoint(
406+
"""
407+
---
408+
Warning: Store.send
409+
410+
The Store class is not thread-safe, and so all interactions with an instance of Store
411+
(including all of its scopes and derived ViewStores) must be done on the same thread.
412+
413+
\(debugCaseOutput(action)) has produced an Effect that was completed on a different thread \
414+
from the one it was executed on.
415+
416+
Starting thread: \(initalThread)
417+
Final thread: \(Thread.current)
418+
419+
Possible fixes for this are:
420+
421+
* Add a .receive(on:) to the Effect to ensure it completes on this Stores correct thread.
422+
"""
423+
)
424+
}
425+
426+
Thread.current.threadDictionary[uuid] = nil
427+
#endif
428+
398429
didComplete = true
399430
self?.effectDisposables.removeValue(forKey: uuid)?.dispose()
400431
},

0 commit comments

Comments
 (0)