Skip to content

Async Effects

ChiefVenzox edited this page Jun 5, 2026 · 1 revision

Async Effects

Use an EffectReducer when an action needs to start async work.

When To Use Effects

Use effects for:

  • API requests
  • local database reads and writes
  • file operations
  • timers
  • authentication flows
  • async validation

Keep reducers responsible for state changes and effects responsible for asynchronous work.

Example

struct AppState: State {
    var isLoading = false
    var message: String?
}

enum AppAction: Action {
    case loadMessage
    case messageLoaded(String)
    case messageFailed(String)
}
let appEffectReducer: EffectReducer<AppState> = { state, action in
    guard let action = action as? AppAction else { return .none }

    switch action {
    case .loadMessage:
        state.isLoading = true
        state.message = nil

        return .run { dispatch in
            do {
                try await Task.sleep(nanoseconds: 500_000_000)
                await dispatch(AppAction.messageLoaded("Loaded"))
            } catch {
                await dispatch(AppAction.messageFailed(error.localizedDescription))
            }
        }

    case .messageLoaded(let message):
        state.isLoading = false
        state.message = message
        return .none

    case .messageFailed(let message):
        state.isLoading = false
        state.message = message
        return .none
    }
}

Create A Store With Effects

@StateObject private var store = Store(
    initialState: AppState(),
    effectReducer: appEffectReducer
)

Effect API

return .none

Use when no async work is needed.

return .run { dispatch in
    let value = try await client.load()
    await dispatch(AppAction.loaded(value))
}

Use for async work that dispatches follow-up actions.

return .merge(effectA, effectB)

Runs multiple effects in order.

Best Practices

  • Set loading state before returning the effect.
  • Always dispatch success or failure actions.
  • Keep network clients injected from outside the reducer.
  • Do not mutate state inside the effect closure.
  • Keep cancellation-heavy work as a future enhancement until your app needs it.

Clone this wiki locally