I've been bending my mind trying to figure out a way to create a forEach variant that can handle sectioned content. But perhaps it's getting too late here.. or maybe it's just a bad idea.
Given a state similar to this:
struct AppState: Equatable {
struct Section: Equatable {
var id: UUID
var items: IdentifiedArray<UUID, Item>
}
var sections: [Section]
}
Is it possible to write a reducer that works like forEach but handles this nested state?
I've got this but haven't tested it. I just realised that reducer is private on Reducer, so I haven't gotten further yet.
extension Reducer {
public func forEach<GlobalState, GlobalAction, GlobalEnvironment, Section, ID>(
sectionState toSectionState: WritableKeyPath<GlobalState, Section>,
state toLocalState: WritableKeyPath<Section, State>,
action toLocalAction: CasePath<GlobalAction, (Section, ID, Action)>,
environment toLocalEnvironment: @escaping (GlobalEnvironment) -> Environment,
_ file: StaticString = #file,
_ line: UInt = #line
) -> Reducer<GlobalState, GlobalAction, GlobalEnvironment> {
.init { globalState, globalAction, globalEnvironment in
guard let (section, id, localAction) = toLocalAction.extract(from: globalAction) else {
return .none
}
return self.reducer(
&globalState[keyPath: toSectionState][keyPath: toLocalState],
localAction,
toLocalEnvironment(globalEnvironment)
)
.map { toLocalAction.embed((section, id, $0)) }
}
}
}
Or is this just a bad idea alltogether and what I should instead do is add the required data to my "cell state" and do sectioning in the view layer? I'm using a diffable data source so I could just group items when I create the snapshot, almost as easily. I guess that would work with an identified array as I'd just care about the ID of the item, section/indexPath doesn't matter.