How do you correctly call onDisappear
action, when Sheet gets interactively dismissed (drag)?
Let's say I have parent state with Route
enum, indicating whether I should be displaying sheet. This child (sheet) state has some kind of side effects, in this case running Camera session for scanning codes. When we call onDisappear
on Camera view, it gets correctly deinitialized and camera stops running (green indicator in status bar disappears).
However if user dismisses entire sheet, before scanning any code, this camera session is still running and simply setting state.route = nil
erases the child state and subsequently the onDisappear
action is ignored and even throws a runtime error about state being nil
.
Camera indicator stays on until either app is closed or user reenters the Camera view and scans a valid code, which triggers a NavigationLink
and onDisappear
on CameraView gets called
Parent state:
public struct ParentState: Equatable {
public var route: Route?
public enum Route: Equatable {
...
case qrScannerSheet(SheetState)
...
}
}
View code:
.sheet(isPresented: viewStore.binding(
get: \.showingSheet,
send: ParentAction.setShowingSheet
)) {
IfLetStore(store.scope(
state: { (/ParentState.Route.sheet).extract(from: $0.route) },
action: ParentAction.sheetAction
), then: SheetView.init(store:))
}
Parent reducer:
case .setShowingSheet(true): return .none
case .setShowingSheet(false):
state.route = nil
return .none
EDIT:
Managed to solve it by changing the binding action on sheet to send a new child "clean" action on false
case, which first clears all state inside child state and then react to this action in Parent Reducer to set route to nil.
send: { $0 ? .setShowingSheet(true) : .sheet(.clean) })
and react to it in reducer with
case .sheet(.clean):
return .init(value: . setShowingSheet(false))
This feels like a hack tho, since the parent has to know about this "clean" action, and how to call and react to it.