Home > Software engineering >  Only root-level navigation destinations are effective for a navigation stack with a homogeneous path
Only root-level navigation destinations are effective for a navigation stack with a homogeneous path

Time:11-09

I am trying to integrate NavigationStack in my SwiftUI app, I have four views CealUIApp, OnBoardingView, UserTypeView and RegisterView. I want to navigate from OnBoardingView to UserTypeView when user presses a button in OnBoardingView and from UserTypeView to RegisterView when user presses a button in UserTypeView

Below is my code for CealUIApp

@main
struct CealUIApp: App {
    
    @State private var path = [String]()
    
    var body: some Scene {
        WindowGroup {
            NavigationStack(path: $path){
                OnBoardingView(path: $path)
            }
        }
    }
}

In OnBoardingView

Button {
                    path.append("UserTypeView")
                } label: {
                    Text("Hello")
                }.navigationDestination(for: String.self) { string in
                    UserTypeView(path: $path)
                }

In UserTypeView

Button {
                path.append("RegisterView")
            } label: {
                Text("Hello")
            }.navigationDestination(for: String.self) { string in
                RegisterView()
            }

When button in UserTypeView is pressed I keep getting navigated to UserTypeView instead of RegisterView with msg in Xcode logs saying Only root-level navigation destinations are effective for a navigation stack with a homogeneous path.

CodePudding user response:

You can get rid of Only root-level navigation destinations are effective for a navigation stack with a homogeneous path by changing the path type to NavigationPath.

@State private var path: NavigationPath = .init()

But then you get a message/error that I think explains the issue better A navigationDestination for “Swift.String” was declared earlier on the stack. Only the destination declared closest to the root view of the stack will be used.

Apple has decided that scanning all views that are available is very inefficient so they will use the navigationDestination will take priority.

Just imagine if your OnBoardingView also had an option for "RegisterView"

 .navigationDestination(for: String.self) { string in
        switch string{
        case "UserTypeView":
            UserTypeView(path: $path)
        case "RegisterView":
            Text("fakie register view")
        default:
            Text("No view has been set for \(string)")
        }
        
    }

How would SwiftUI pick the right one?

So how to "fix"? You can try this alternative.

import SwiftUI

@available(iOS 16.0, *)
struct CealUIApp: View {
    @State private var path: NavigationPath = .init()
    var body: some View {
        NavigationStack(path: $path){
            OnBoardingView(path: $path)
                .navigationDestination(for: ViewOptions.self) { option in
                    option.view($path)
                }
        }
    }
    //Create an `enum` so you can define your options
    enum ViewOptions{
        case userTypeView
        case register
        //Assign each case with a `View`
        @ViewBuilder func view(_ path: Binding<NavigationPath>) -> some View{
            switch self{
            case .userTypeView:
                UserTypeView(path: path)
            case .register:
                RegisterView()
            }
        }
    }
}
@available(iOS 16.0, *)
struct OnBoardingView: View {
    @Binding var path: NavigationPath
    var body: some View {
        Button {
            //Append to the path the enum value
            path.append(CealUIApp.ViewOptions.userTypeView)
        } label: {
            Text("Hello")
        }
        
    }
}
@available(iOS 16.0, *)
struct UserTypeView: View {
    @Binding var path: NavigationPath
    var body: some View {
        Button {
            //Append to the path the enum value
            path.append(CealUIApp.ViewOptions.register)
        } label: {
            Text("Hello")
        }
        
    }
}
@available(iOS 16.0, *)
struct RegisterView: View {
    var body: some View {
        Text("Register")
        
    }
}
@available(iOS 16.0, *)
struct CealUIApp_Previews: PreviewProvider {
    static var previews: some View {
        CealUIApp()
    }
}
  • Related