1

I'm trying to use NavigationStack for Navigation for my SwiftUI Project. I have 4 Screens in total. The Main Page of my App has Login and Sign up Buttons. If the user signs in successfully the App takes the user to the Home Page where the User can do other things related to the App. The Sign up Button navigates them to the Sign Up Page. If they successfully Sign up, they're navigated to the Home Page of the App where they can continue using the Application.

On the Home Screen, I have a Toolbar with a Button that Navigates the users to another Page where they can see a list of items they have that are retrieved from a remote Database.

I'm Using NavigationStack along with NavigationPath for Navigation. I'm setting String values that get appended to the NavigationPath for Navigation.

However, when I use a String value to navigate the user from the Home Screen to the List screen, I get the following error:

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.

I was trying for hours to fix this issue but I'm unable to do so. Could somebody let me know where I'm going wrong. My Code is as follows:

struct MainScreen: View {
  @State private var path: NavigationPath = NavigationPath()
    
  var body: some View {
    /*
     Other code here
    */
    
    Button {              
      print("Show Sign Up View")
      path.append("ShowSignUp")              
    } label: {
      HStack {
        // Image(systemName: "person.fill.badge.plus")
        Text("Sign Up")                    
      }            
    }
    .navigationDestination(for: String.self) { value in                
      if(value == "ShowAddParking") {
        AddParkingView(path: $path)
      } else if(value == "ShowSignUp") {
        SignUpView(path: $path)
      }                
    }
  }
}

Sign Up Screen:

struct SignUpView: View {
  @Binding var path: NavigationPath
    
  var body: some View {
    
    /*
    Other Code here
    */
    
    Button("Register") {
      //I had to use an Int here since I was getting the same error 
      self.path.append(1)
    }
    .navigationDestination(for: Int.self) { value in
      if value == 1{
        AddParkingView(path: self.$path)
      }                    
    }
  }
}

The Home Screen of the App:

struct AddParkingView: View {    
  @Binding var path: NavigationPath
    
  var body: some View {    
    //code for this View
    
    .toolbar {   
      ToolbarItem(placement: .navigationBarTrailing) {
        Button("View Parking") {
          print("Show Parking Items")
          self.path.append("ParkingList")      
        }               
      }
    }
    .navigationDestination(for: String.self) { _ in
      ParkingListView(path: self.$path)
    }
  }
}

Could somebody please give me some suggestions. Thank You.

Fogmeister
  • 76,236
  • 42
  • 207
  • 306
Amey079
  • 131
  • 7
  • 1
    No worries, it can be tricky to edit. Def makes it easier if you use the three back ticks to edit code rather than relying on the indentation. – Fogmeister Mar 29 '23 at 17:21
  • 1
    Does this answer your question? [Only root-level navigation destinations are effective for a navigation stack with a homogeneous path](https://stackoverflow.com/questions/74362455/only-root-level-navigation-destinations-are-effective-for-a-navigation-stack-wit) – lorem ipsum Mar 29 '23 at 19:03
  • I liked that solution too @loremipsum, thanks will keep it in mind if I need to go that way. – Amey079 Mar 31 '23 at 18:30

1 Answers1

5

The navigationDestination modifier applies to the Navigation Stack's entire hierarchy, not just the singular view it's placed in. If you were to move your .navigationDestination(for: Int.self) {... modifier from SignUpView to MainScreen you should find that navigation will still work even though it isn't being applied directly to the View calling it.

With that in mind, your two .navigationDestination(for: String.self) {... modifiers are competing for all Strings that enter the navigation path. Both modifiers want to read that value, which is what Xcode's warning is trying to say.

An easy solution would be to combine the two modifiers at the root to something like:

.navigationDestination(for: String.self) { value in                
    if(value == "ShowAddParking") {
      AddParkingView(path: $path)
    } else if(value == "ShowSignUp") {
      SignUpView(path: $path)
    } else {
      ParkingListView(path: self.$path)
    }             
}

For simplicity's sake, you may want to place all of your navigationDestination modifiers within the same View as your Navigation Path, since that is where the navigation work is happening anyway.

Shawn
  • 571
  • 1
  • 8
  • 1
    Hey Shawn just tried doing what you said. Thanks a bunch that worked Flawlessly. I could see why it was getting a conflict the way I was doing it. Also using your solution, I just removed the Int Value and made them all Stings and since now I didn't have a bunch of navigationDestination() modifiers all over the place, the Navigation was very smooth. No more errors or conflicts. Cheers! – Amey079 Mar 31 '23 at 18:28