-
Hi, I have a global In the video, the toolbar back button dismissed the view successfully, but calling Am I missing something? Thank you! iShot_2023-08-25_17.15.20.mp4import ComposableArchitecture
import SwiftUI
struct Screen: Reducer {
struct State: Equatable {
let title: String
var isChild = false
}
enum Action {
case goToChildScreen
case dismiss
}
@Dependency(\.navManager) var navManager
@Dependency(\.dismiss) var dismiss
func reduce(into state: inout State, action: Action) -> Effect<Action> {
switch action {
case .goToChildScreen:
navManager.store.send(.goTo(.childScreen()))
return .none
case .dismiss:
return .run { _ in
await self.dismiss()
}
}
}
}
struct ScreenView: View {
let store: StoreOf<Screen>
var body: some View {
WithViewStore(self.store, observe: { $0 }) { viewStore in
VStack {
Text(viewStore.title).font(.largeTitle)
if viewStore.isChild {
Button("Dismiss") {
viewStore.send(.dismiss)
}
} else {
Button("Go to childScreen") {
viewStore.send(.goToChildScreen)
}
}
}
}
}
}
struct SwitchStoreTestCase: Reducer {
enum State: Equatable {
case loggedIn(Screen.State = .init(title: "Logged In"))
case loggedOut(Screen.State = .init(title: "Logged Out"))
}
enum Action {
case screenA(Screen.Action)
case screenB(Screen.Action)
}
var body: some ReducerOf<Self> {
Reduce { state, action in
switch (state, action) {
case (_, .screenA), (_, .screenB):
return .none
}
}
.ifCaseLet(/State.loggedIn, action: /Action.screenA) {
Screen()
}
.ifCaseLet(/State.loggedOut, action: /Action.screenB) {
Screen()
}
}
}
struct SwitchStoreTestCaseView: View {
let store = Store(initialState: .loggedIn()) {
SwitchStoreTestCase()
}
@Dependency(\.navManager) var navManager
var body: some View {
SwitchStore(self.store) {
switch $0 {
case .loggedIn:
CaseLet(
/SwitchStoreTestCase.State.loggedIn, action: SwitchStoreTestCase.Action.screenA
) { store in
NavigationStackStore(
navManager.store.scope(state: \.path, action: { .path($0) })
) {
ScreenView(store: store)
} destination: { state in
switch state {
case .childScreen:
CaseLet(
/Nav.Path.State.childScreen,
action: Nav.Path.Action.childScreen,
then: ScreenView.init(store:)
)
}
}
}
case .loggedOut:
CaseLet(
/SwitchStoreTestCase.State.loggedOut, action: SwitchStoreTestCase.Action.screenB
) { store in
NavigationStackStore(
navManager.store.scope(state: \.path, action: { .path($0) })
) {
ScreenView(store: store)
} destination: { state in
switch state {
case .childScreen:
CaseLet(
/Nav.Path.State.childScreen,
action: Nav.Path.Action.childScreen,
then: ScreenView.init(store:)
)
}
}
}
}
}
}
}
// NavManager
struct Nav: Reducer {
struct State: Equatable {
var path = StackState<Path.State>()
}
enum Action {
case path(StackAction<Path.State, Path.Action>)
case goTo(Path.State)
}
struct Path: Reducer {
enum State: Equatable {
case childScreen(Screen.State = .init(title: "Child", isChild: true))
}
enum Action {
case childScreen(Screen.Action)
}
var body: some Reducer<State, Action> {
Scope(state: /State.childScreen, action: /Action.childScreen) {
Screen()
}
}
}
var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case let .goTo(newState):
state.path.append(newState)
return .none
case .path:
return .none
}
}
.forEach(\.path, action: /Action.path) {
Path()
}
}
}
struct NavManager {
let store: StoreOf<Nav>
init() {
store = Store(initialState: Nav.State(), reducer: {
Nav()._printChanges()
})
}
}
extension NavManager: DependencyKey {
static let liveValue: Self = .init()
}
extension DependencyValues {
var navManager: NavManager {
get { self[NavManager.self] }
set { self[NavManager.self] = newValue }
}
} |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 2 replies
-
Hi @atsixian, I was able to run your demo and see the issue. It's hard to say currently whether there is an issue in the library or not. Your current set up is a bit non-standard. You have two stores running in the app (one in We need a bit more time to look into it and we will get back to you. |
Beta Was this translation helpful? Give feedback.
Hi @atsixian, we tracked down the problem. It's not an issue with the library, but instead it's due to sending an action to the
navManager
directly in theScreen
reducer:This is problematic because you are running the
Nav
reducer inside the context of theSwitchStoreTestCase
reducer, which means theNav
reducer is getting all of its dependencies fromSwitchStoreTestCase
. That messes things up because\.dismiss
works by communicating through the dependency system.So, in order to get back to
navManager
's default dependencies, you need to runstore.send
in a detached task (that resets the@TaskLocal
s that …