In my app, it's possible that delayed effects can be sent to items that have been removed from an IdentifiedArray.
This results in hitting the fatalError in IdentifiedArray.subscript(id:).set
because Reducer.forEach
assumes that the identified item will always be there.
Is there a better way of handling this activity? I've thought about:
Adding an optional
to my forEach reducer:
arrayReducer.forEach(state: \.array, action: /AppAction.arrayAction, environment: { () }).optional,
However, this doesn't work because AppState.array
is not an array of optionals.
If I added a guard to Reducer.forEach
, the problem is resolved:
public func forEach<GlobalState, GlobalAction, GlobalEnvironment, ID>(
state toLocalState: WritableKeyPath<GlobalState, IdentifiedArray<ID, State>>,
action toLocalAction: CasePath<GlobalAction, (ID, Action)>,
environment toLocalEnvironment: @escaping (GlobalEnvironment) -> Environment
) -> Reducer<GlobalState, GlobalAction, GlobalEnvironment> {
.init { globalState, globalAction, globalEnvironment in
guard let (id, localAction) = toLocalAction.extract(from: globalAction) else { return .none }
// Ensure that the localState is still present in the identified array
guard globalState[keyPath: toLocalState][id: id] != nil else { return .none }
return self.optional
.reducer(
&globalState[keyPath: toLocalState][id: id],
localAction,
toLocalEnvironment(globalEnvironment)
)
.map { toLocalAction.embed((id, $0)) }
}
}
However, this doesn't seem like a great change to make to forEach
— should this be a separate method? Am i just going about this all wrong?