Bahn-X/swift-composable-navigator

goBack fails to dismiss multiple views correctly.

andrewjmeier opened this issue · 1 comments

Bug description

[//]: # Using goBack to navigate back to a previous screen doesn't dismiss all of the views when each view is presented modally.

Steps to reproduce

[//]: # Open a series of at least 3 modal views and then try to navigate back to the original view. One modal view will remain.

struct RootView: View {
    
    var body: some View {
        let dataSource = Navigator.Datasource(root: MainScreen())
        let navigator = Navigator(dataSource: dataSource)
        
        return Root(dataSource: dataSource, navigator: navigator, pathBuilder: MainScreen.Builder())
    }
    
}

struct MainScreen: Screen {
    
    var presentationStyle: ScreenPresentationStyle = .push
    
    struct Builder: NavigationTree {
        var builder: some PathBuilder {
            Screen(
                content: { (_: MainScreen) in MainView() },
                nesting: { ModalScreen.Builder().eraseCircularNavigationPath() }
            )
        }
    }
}

struct MainView: View {
    @Environment(\.navigator) private var navigator
    @Environment(\.currentScreenID) private var currentScreenID
    
    var body: some View {
        VStack {
            Button {
                navigator.go(to: ModalScreen(viewCount: 1, onDismiss: {
                    print(currentScreenID)
                    navigator.goBack(to: currentScreenID)
                }), on: currentScreenID)
            } label: {
                Text("Show new view")
            }
        }
    }
}

struct ModalScreen: Screen {
    
    var presentationStyle: ScreenPresentationStyle = .sheet(allowsPush: true)
    var viewCount: Int
    var onDismiss: () -> Void
    
    struct Builder: NavigationTree {
        var builder: some PathBuilder {
            Screen(
                content: { (screen: ModalScreen) in ModalView(viewCount: screen.viewCount, onDismiss: screen.onDismiss) },
                nesting: { ModalScreen.Builder().eraseCircularNavigationPath() }
            )
        }
    }
}

extension ModalScreen: Equatable {
    
    static func == (lhs: ModalScreen, rhs: ModalScreen) -> Bool {
        return lhs.viewCount == rhs.viewCount
    }
    
}

extension ModalScreen: Hashable {
    func hash(into hasher: inout Hasher) {
        hasher.combine(viewCount)
    }
}

struct ModalView: View {
    @Environment(\.navigator) private var navigator
    @Environment(\.currentScreenID) private var currentScreenID
    
    var viewCount: Int
    var onDismiss: () -> Void
    
    var body: some View {
        VStack {
            Text("View \(viewCount)")
            Button {
                navigator.go(to: ModalScreen(viewCount: viewCount + 1, onDismiss: onDismiss), on: currentScreenID)
            } label: {
                Text("Show new view")
            }
            Button {
                onDismiss()
            } label: {
                Text("dismiss all views")
            }
        }
    }
    
}

Expected behavior

[//]: I would expect the original screen to be fully visible and all of the modal views to be dismissed.

Screenshots

[//]: Simulator Screen Recording - iPod touch (7th generation) - 2021-05-14 at 10 13 21

Environment

  • Xcode 12.5
  • Swift 5.4
  • iOS 14

Hm, interesting. I will look into this. 🧐