Redux-like state container in SwiftUI. Derived stores.
Sergey Leschev
Posted on February 18, 2023
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")
}
}
}
}
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
}
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
Posted on February 18, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.