DEV Community

Cover image for Redux-like state container in SwiftUI. Derived stores.
Sergey Leschev
Sergey Leschev

Posted on • Edited on

Redux-like state container in SwiftUI. Derived stores.

Another way of composition that should simplify our architecture is derived stores. I don’t want to expose the whole app state to every view or update views on not related state updates.

import SwiftUI

struct RootView: View {
    @EnvironmentObject var store: Store<AppState, AppAction>

    var body: some View {
        TabView {
            NavigationView {
                SummaryContainerView()
                    .navigationBarTitle("today")
                    .environmentObject(
                        store.derived(
                            deriveState: \.summary,
                            deriveAction: AppAction.summary
                        )
                )
            }.tabItem {
                Image(systemName: "heart.fill")
                    .imageScale(.large)
                Text("today")
            }

            NavigationView {
                TrendsContainerView()
                    .navigationBarTitle("trends")
                    .environmentObject(
                        store.derived(
                            deriveState: \.trends,
                            deriveAction: AppAction.trends
                        )
                )
            }.tabItem {
                Image(systemName: "chevron.up.circle.fill")
                    .imageScale(.large)
                Text("trends")
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Every tab of my app gets its part of the state via the derived store. We still use the global store to handle all the state mutation. Derived store works as a pipeline that allows us to transform the state from the global store and redirect actions to the global store. Let’s take a look at how we can implement the derived method for our Store class.

func derived<DerivedState: Equatable, ExtractedAction>(
    deriveState: @escaping (State) -> DerivedState,
    embedAction: @escaping (ExtractedAction) -> Action
) -> Store<DerivedState, ExtractedAction> {
    let store = Store<DerivedState, ExtractedAction>(
        initialState: deriveState(state),
        reducer: Reducer { _, action, _ in
            self.send(embedAction(action))
            return Empty().eraseToAnyPublisher()
        },
        environment: ()
    )
    $state
        .map(deriveState)
        .removeDuplicates()
        .receive(on: DispatchQueue.main)
        .assign(to: &store.$state)
    return store
}
Enter fullscreen mode Exit fullscreen mode

Redux provides a single source of truth, which eliminates tons of bugs produced by multiple states across the app. Best practices. Normalization and composition keep our app state simple and maintainable.


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

Top comments (0)