Right, well this is far from ideal so far... but it works 
I wrote this...
extension View {
func sheet<Enum, Case, Content, State, LocalState, Action, LocalAction>(
unwrapping enum: Binding<Enum?>,
case casePath: CasePath<Enum, Case>,
store: Store<State, Action>,
state: @escaping (State) -> LocalState?,
action: @escaping (LocalAction) -> Action,
onDismiss: (() -> Void)? = nil,
@ViewBuilder content: @escaping (Store<LocalState, LocalAction>) -> Content
) -> some View where Content: View {
self.sheet(unwrapping: `enum`.case(casePath), onDismiss: onDismiss) { _ in
SwitchStore(store) {
CaseLet.init(state: state, action: action, then: content)
}
}
}
}
Something I tried to do is remove the state parameter as it is essentially the same as the casePath.
But this works so far.
So now I have a route like...
enum Route {
case addPerson(AddPersonState)
}
And state...
struct PersonListState {
@BindableState var route: Route? = nil
}
This uses the binding action and reducer.
Then in my reducer I have...
case .addPersonTapped:
state.route = .addPerson(AddPersonState(.....))
return .none
And then in my view...
.sheet(
unwrapping: viewStore.binding(\.$route),
case: /PersonStateRoute.addPerson,
store: store.scope(state: \.route), // I have to scope this to get to the enum
state: /PersonStateRoute.addPerson,
action: PersonAction.addPersonAction,
content: AddPersonView.init(store:)
)
Which is a bit meh... from an interface point of view. But it does allow me to drive the navigation from state within TCA and then scope through the reducer at the same time.
And as you can see... it seems to work so far... Beginnings of an app written on my iPad! - YouTube