Sectioned forEach reducer

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],
      .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.

Terms of Service

Privacy Policy

Cookie Policy