I try to build a cascade of views that show a list of sections, a list of cards within each section, and a list of items within a cards.
The topmost view shall load some data when .onAppear
is triggered. Those data are loaded in a viewReducer
, that sets a state.mainSections
property. Each State
conforms to Identifiable
and Equatable
.
However, when the ForEachStore
statement in the view is performed, and when examining the state $0
content, it only shows empty content, i.e.: the state modification does not appear there.
Am I doing something wrong, or is this a kind of bug?
The essential code for the store:
struct MainOverview {
private init() {}
struct Environment {
var maintainableDataProvider: MaintainableDataProvider
}
struct State: Equatable, Identifiable {
init() {}
var id: UUID = .init()
var dataGroups: [MaintainableDataGroup] = []
var mainSections: IdentifiedArrayOf<MainSection.State> = []
static func ==(lhs: State, rhs: State) -> Bool {
return lhs.id == rhs.id &&
lhs.dataGroups == rhs.dataGroups &&
lhs.mainSections == rhs.mainSections
}
}
enum Action {
enum ViewAction {
case onAppear
case onDisappear
}
case viewAction(ViewAction)
case mainSections(UUID, MainSection.Action)
}
static let reducer = Reducer<State, Action, Environment>
.combine(
MainSection.reducer.forEach(
state: \State.mainSections,
action: /Action.mainSections,
environment: {
MainSection.Environment(
maintainableDataProvider: $0.maintainableDataProvider
)
}
),
viewReducer.pullback(
state: \.self,
action: /Action.viewAction,
environment: { $0 }),
generalReducer
)
static let generalReducer = Reducer<State, Action, Environment> { state, action, env in
switch action {
case let .viewAction(_):
return .none
case let .mainSections(uuid, action):
return .none
}
}
static let viewReducer = Reducer<State, Action.ViewAction, Environment> { state, action, env in
switch action {
case .onAppear:
debugPrint("Appearing: MainOverview")
let result = env.maintainableDataProvider.dataGroups
switch result {
case let .success(dataGroups):
state.mainSections = IdentifiedArrayOf(
dataGroups.map({ dataGroup in
MainSection.State(
dataGroup: dataGroup
)
})
)
// Should enforce the state update, but has no effect for ForEachStore:
state.id = UUID()
return .none
case .failure:
return .none
}
case .onDisappear:
return .none
}
}
}
The essential code for the view:
let store: Store<MainOverview.State, MainOverview.Action>
var body: some View {
WithViewStore(store) { viewStore in
VStack {
ScrollView(.vertical) {
ForEachStore(
self.store.scope(
state: { // This is empty:
debugPrint(">>>", $0)
return $0.mainSections
},
action: MainOverview.Action.mainSections
)
) { childStore in
MainSectionView(store: childStore)
}
.debug("MainOverviewView ForEachStore")
}
}
.debug("MainOverviewView redrawing")
.onAppear {
viewStore.send(.viewAction(.onAppear))
}
.onDisappear {
viewStore.send(.viewAction(.onDisappear))
}
}
.debug()
}
The debug output:
>>> MainOverview.State(id: 45DCC4F4-EE49-4CF9-89F2-75AB5335BA36, dataGroups: [], mainSections: [])
>>>MainOverview.State(id: 45DCC4F4-EE49-4CF9-89F2-75AB5335BA36, dataGroups: [], mainSections: [])