Overview
The primary way to create anEffect in the library is via Effect.run, which takes a @Sendable, asynchronous closure. This restricts the types of closures you can use for your effects.
The closure can only capture
Sendable variables that are bound with let. Mutable variables and non-Sendable types are not allowed.- Accessing state from within an effect
- Accessing a dependency from within an effect
Accessing state in an effect
Reducers execute with a mutable,inout state variable, which cannot be accessed from @Sendable closures.
The problem
The solution
Explicitly capture the state as an immutable value:- Capture full state
- Capture specific values
Accessing dependencies in an effect
Dependencies can be used from asynchronous and concurrent contexts, so they must beSendable.
Registering dependencies
When extendingDependencyValues, you’ll get warnings if your dependency isn’t Sendable:
Making dependencies Sendable
To fix this, make sure the interface type only holds ontoSendable data, and annotate closure-based endpoints as @Sendable:
Common patterns
Passing state to async work
Using dependencies in effects
Shared state in effects
Shared state is alreadySendable, so you can capture it directly:
Actor isolation
Reducers in TCA are@MainActor isolated by default, which means:
State access is safe
All state mutations happen on the main actor
Effects run elsewhere
.run effects execute in the cooperative thread poolSend is main actor
Sending actions back always happens on the main actor
Views are synchronized
SwiftUI views observe state on the main actor
Crossing actor boundaries
When effects need to send actions, they automatically hop to the main actor:Testing concurrent code
When testing features with concurrency:Swift 6 considerations
In Swift 6, concurrency warnings become errors. To prepare:Enable strict concurrency checking
Enable strict concurrency checking
In your package or Xcode project, enable complete concurrency checking to get warnings now:
Make dependencies Sendable
Make dependencies Sendable
Ensure all dependencies conform to
Sendable and use @Sendable closures.Capture state explicitly
Capture state explicitly
Always use capture lists when accessing state in effects:
Avoid global state
Avoid global state
Don’t access global mutable state. Wrap it in dependencies.
Common errors and fixes
Best practices
Always capture state explicitly
Use capture lists even when it seems unnecessary
Make dependencies Sendable
Annotate all closures with
@SendableAvoid global state
Wrap globals in dependencies for testability
Use actor isolation wisely
Let TCA handle main actor isolation automatically
Resources
Swift Concurrency
Official Swift concurrency documentation
Dependencies
Learn more about dependency injection in TCA
Testing
Learn how to test concurrent features
Effects
Deep dive into the Effect type