iosswiftswiftuiswiftui-navigationstackswiftui-navigationpath

SwiftUI NavigationStack Navigation


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.


Solution

  • 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.