Redux-like state container in SwiftUI. Side effects.
Tags: swift, ios, mobile, programming
Published: 2023-02-11 15:55:41

We already implemented a [unidirectional](https://dev.to/sergeyleschev/redux-like-state-container-in-swiftui-basics-unidirectional-flow-3f2p) flow that accepts user actions and modifies the state, but what about `async actions` which we usually call **side effects**. How to bake a support for async tasks into our store type? I think it is a good time to introduce the usage of _Combine framework_ that perfectly fits async task processing.

```swift
typealias Reducer<State, Action, Environment> =
    (inout State, Action, Environment) -> AnyPublisher<Action, Never>?
```

We add support for `async tasks` by changing `Reducer` typealias, it has the additional parameter called `Environment`. `Environment` might be a plain struct that holds all needed dependencies like service and manager classes.

```swift
func appReducer(
    state: inout AppState,
    action: AppAction,
    environment: Environment
) -> AnyPublisher<AppAction, Never>? {
    switch action {
    case let .setSearchResults(repos):
        state.searchResult = repos
    case let .search(query):
        return environment.service
            .searchPublisher(matching: query)
            .replaceError(with: [])
            .map { AppAction.setSearchResults(repos: $0) }
            .eraseToAnyPublisher()
    }
    return nil
}
```

Side Effect is a sequence of `Actions` which we can publish using `Combine` framework’s `Publisher` type. It allows us to handle async job and then publish an action that will be used by `reducer` to change the current state.

```swift
final class Store<State, Action, Environment>: ObservableObject {
    @Published private(set) var state: State

    private let environment: Environment
    private let reducer: Reducer<State, Action, Environment>
    private var cancellables: Set<AnyCancellable> = []

    init(
        initialState: State,
        reducer: @escaping Reducer<State, Action, Environment>,
        environment: Environment
    ) {
        self.state = initialState
        self.reducer = reducer
        self.environment = environment
    }

    func send(_ action: Action) {
        guard let effect = reducer(&state, action, environment) else {
            return
        }

        effect
            .receive(on: DispatchQueue.main)
            .sink(receiveValue: send)
            .store(in: &cancellables)
    }
}
```

We build a `Store` type that supports async tasks. Usually, reducer resolves an action by applying it on top of the state. In case of an async action, reducer returns it as `Combine Publisher`, then Store run it and send result back to the reducer as a plain action.

---

- [Basics](https://dev.to/sergeyleschev/redux-like-state-container-in-swiftui-basics-single-source-of-truth-2lpc)
- [Reducer and Actions](https://dev.to/sergeyleschev/redux-like-state-container-in-swiftui-basics-reducer-and-actions-flg)
- [Unidirectional flow](https://dev.to/sergeyleschev/redux-like-state-container-in-swiftui-basics-unidirectional-flow-3f2p)
- [Side effects](https://dev.to/sergeyleschev/redux-like-state-container-in-swiftui-side-effects-3l23)
- [Usage](https://dev.to/sergeyleschev/redux-like-state-container-in-swiftui-usage-k1i)

---

**Contacts**
I have a clear focus on time-to-market and don't prioritize technical debt. And I took part in the Pre-Sale/RFX activity as a System Architect, assessment efforts for Mobile (iOS-Swift, Android-Kotlin), Frontend (React-TypeScript) and Backend (NodeJS-.NET-PHP-Kafka-SQL-NoSQL). And I also formed the work of Pre-Sale as a CTO from Opportunity to Proposal via knowledge transfer to Successful Delivery.

🛩️ #startups #management #cto #swift #typescript #database
📧 Email: sergey.leschev@gmail.com
👋 LinkedIn: https://linkedin.com/in/sergeyleschev/
👋 LeetCode: https://leetcode.com/sergeyleschev/
👋 Twitter: https://twitter.com/sergeyleschev
👋 Github: https://github.com/sergeyleschev
🌎 Website: https://sergeyleschev.github.io
🌎 Reddit: https://reddit.com/user/sergeyleschev
🌎 Quora: https://quora.com/sergey-leschev
🌎 Medium: https://medium.com/@sergeyleschev