frzi / swiftui-router

Path-based routing in SwiftUI
MIT License
900 stars 43 forks source link

Navigate navigates into void sometimes #39

Open koraykoska opened 2 years ago

koraykoska commented 2 years ago

Very randomly, without any consistency, I see that Navigate tries to navigate to a route, then chooses to make a wrong turn to the default route in the last step (inside a SwitchRoutes) and then repeats itself a few times until SwiftUI decides to stop it and renders an empty screen.

I had this issue multiple times and most of the time it was an endless loop caused by my code. This time, however, I even checked the info parameter and can see that it took a wrong turn, even though the route is set correctly.

I can't really provide a sample project as it is anyways very random and happens just sometimes. I was just hoping that someone experienced this as well and knows where I can start debugging.

Just as a presentation of what I mean, see the below:

struct MainRouter: RouterView {
    var content: some View {
        Router(initialPath: "/sign-in") {
            SwitchRoutes {
                Route("/sign-up/*") {
                    SignUpRouter()
                }

                Route("/sign-in/*") {
                    SignInRouter()
                }

                Route("/home/*") {
                    HomeRouter()
                }

                Route {
                    Navigate(to: "/sign-in")
                }
            }
        }
    }
}

// --- Removing all the other Routers for brevity. This only happens on HomeRouter for some reason.

struct HomeRouter: RouterView {
    var content: some View {
        SwitchRoutes {
            Route("root") {
              SomeHomeView()
            }

            Route("profile") {
              SomeProfileView()
            }

            Route { info in
                // The case is described below. info tells me that the route is /home/profile but for some reason it lands here in the fallback.
                Navigate(to: "/home/root")
            }
        }
    }
}

So the issue arises if I navigate to "/home/profile" (or any other subview of home). What happens instead (again, just sometimes), is that the Navigator jumps to the fallback Route {}. The weird thing is that the info variable has the right path saved. For some reason, it just doesn't match the right one when selecting the path to navigate to.

I know that this can be caused by literally everything and probably depends on my app state. Still good to have this open and see if anyone else is experiencing this.

In general, I would like to understand how this situation CAN happen with SwiftUIRouter. Once I do, it will be much easier to debug and solve.

koraykoska commented 2 years ago

Oh, forgot to mention it. Once the app is in this state, it does that all the time. So e.g. in this example the default route should go back to "/home/root" but instead, again, navigates to /home fallback (endless loop). That's why I don't even fallback but rather go to an empty screen because of SwiftUI's optimizations.

frzi commented 2 years ago

Hmm it's very possible you're talking about a bug I've encountered very rarely - like only three times in total. Unfortunately so far I have been unable to successfully (and consistently) reproduce it.

The one time I did caught it, however, it looked like - for whatever reason - SwiftUI decided to render the children of SwitchRoutes in reverse order. Like some weird magic optimization SwiftUI decided to perform out of nowhere. Meaning the last route would render (and perform path matching) first, instead of last. Thus resulting in the behaviour you described.

My gut tells me this is some optimization introduced in SwiftUI 3, as I don't recall experiencing this bug in SwiftUI 2. But it's hard to tell.

It is certainly worrying, and I really hope to find a method/routine to reproduce this bug consistently to further investigate it. Any help on this matter would be greatly appreciated!

So far from the top of my head potential solutions could be making a custom ViewBuilder for SwitchRoutes (lord have mercy), or work with .id() for Routes views (yuck). 😞

koraykoska commented 2 years ago

So since using navigator.navigate instead of rendering a Navigate, this error doesn't happen anymore. Still a little annoying but it is a good hack at least.