The code is identical to your approach expect for one difference: it doesn't nil out the state in AppAction.detail.onDisappear because doing so would not allow for AppAction.detail.avatar1.onDisappear and AppAction.detail.avatar2.onDisappear to run. In other words this causes crash:
case .detail(.onDisappear):
state.detail = nil
return .none
and this won't crash
case .detail(.onDisappear):
return .none
I am not 100% confident about this and I fear leaving the state "dangling" OR relying on the state to be "dangling" may cause new issues. Not sure. Would love to hear your thoughts on this.
I think this could definitely be improved. I experimented with an additional state wrapper (with a dedicated reducer) that holds the optional state and performs some actions when the wrapped state is set to nil. Found out it's not trivial to implement, as many corner cases should be rethought and handled gracefully. Something like this already exists in the Swift Composable Architecture repository - Lifecycle reducer example. It could be a good starting point.
Doesn't this solution assume there's just one "Permissions"-view? What if you had multiple instances of the Permissions-view at the same time? I think it is important that there's a cancellation id / token passed down.
Doesn't this solution assume there's just one "Permissions"-view? What if you had multiple instances of the Permissions-view at the same time? I think it is important that there's a cancellation id / token passed down.
Right! This is very important. Thanks for bringing it up here. For that reason, in my example project, I stored the effect id in the state struct. I don't think it's a perfect solution, but I couldn't find a better one.
For that reason, in my example project, I stored the effect id in the state struct
I fear it is a bit scary to store it in the state as the state tree is optional and leaving the state "dangling around" for a little while is a solution that might cause new issues.