Redux-like state container in SwiftUI. Derived stores.

sergeyleschev

Sergey Leschev

Posted on February 18, 2023

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

๐Ÿ’– ๐Ÿ’ช ๐Ÿ™… ๐Ÿšฉ
sergeyleschev
Sergey Leschev

Posted on February 18, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

ยฉ TheLazy.dev

About