Let me attempt a clarification.
Given these three
struct DashboardViewState: Equatable {
// ..
var categoryListViewState: CategoryListViewState?
var itemDetailViewState: ItemDetailViewViewState?
}
enum DashboardViewAction: Equatable {
case didTapViewAll(section: Int)
case didTapItem(IndexPath)
case categoryListViewAction(CategoryListViewAction)
case itemDetailViewAction(ItemDetailViewAction)
}
let dashboardReducer = Reducer<DashboardViewState, DashboardViewAction, Void> { state, action, _ in
switch action {
case let .didTapViewAll(section: section):
state.categoryListViewState = CategoryListViewState(category: state.categoryForSection(section))
return .none
case let .didTapItem(indexPath):
state.itemDetailViewState = ItemDetailViewState(item: state.itemForIndexPath(indexPath))
return .none
}
}
struct CategoryListViewState: Equatable {
// ...
var itemDetailViewState: ItemDetailViewViewState?
}
enum CategoryListViewAction: Equatable {
case .didTapItem(Item)
case itemDetailViewAction(ItemDetailViewAction)
}
let categoryListReducer = Reducer< CategoryListViewState, CategoryListViewAction, Void> { state, action, _ in
switch action {
case let .didTapItem(item):
state.itemDetailViewState: ItemDetailViewState(item: item)
return .none
}
}
struct ItemDetailViewViewState: Equatable {
var item: Item
}
enum ItemDetailViewAction: Equatable {
case didTapSaveFavorite
}
let itemDetailReducer: Reducer<ItemDetailViewViewState, ItemDetailViewAction, Void> { state, action, _ in
// ..
}
(Code written inside this text editor and not tested in any way)
Now, to make each of these state's respective view's fully work "individually" I have to pullback the itemDetailReducer
and combine it with both the dashboardReducer
and the categoryListReducer
.
Or the whole thing could be architected in such a way where each individual view does not know or care about navigation. Much like the "Coordinator pattern". Another higher order reducer or perhaps just some "app reducer" would listen for "navigation events" and manage the view controller flow.
So, the alternative would be something like this, with removing each individual "substatate" from the different states:
struct AppState: Equatable {
var dashboardViewState: DashboardViewState
var categoryListViewState: CategoryListViewState?
var itemDetailViewState: ItemDetailViewState?
}
enum AppAction: Equatable {
case dashboardAction(DashboardViewAction)
case categoryListAction(CategoryListViewAction)
}
let appReducer = Reducer<AppState, AppAction, Void> { state, action, _ in
switch action {
case .dashboardAction(.didTapCategory(let category)):
state.categoryListViewState = CategoryListViewState(category)
return .none
case .dashboardAction(.didTapItem(let item),
.categoryListAction(.didTapItem(let item)):
state.itemDetailViewState(item)
return .none
}
}
Does that make it clearer?
I guess I just find it a little odd that if one were to visualize the "network" of reducers, in the first case we'd end up with something like:
dashboardReducer
- listReducer
- itemReducer
- itemReducer
Which feels redundant. But maybe that's just the beauty of TCA? Being able to compose stuff together and not having to generalize everything. 