NavigationLink Back Button Bug

When using one of the NavigationLink initializers  available to programatically push a view into the NavigationView, the Back button malfunctions every other time. The first time it is tapped, the view pops and pushes again immediately. The second time around, it works fine (only pops). The third time the problem starts all over again.

The affected initializers are:

NavigationLink(destination: MyView(), isActive: $active) { ... }
NavigationLink(destination: MyView(), tag: 1, selection: $selection) { ... }

If we pop the view programatically, it all works fine.

Bug NavigationLink

Bug Report: FB6869419

Workaround: Yes, see below.

Fixed On: iOS 13.1

Stackoverflow Question: https://stackoverflow.com/questions/57273717/swiftui-navigationdestinationlink-deprecated

Code Sample (to reproduce)

window.rootViewController = UIHostingController(rootView: ContentView().environmentObject(Model()))
class Model: ObservableObject {
    @Published var pushed = false
}

struct ContentView: View {
    @EnvironmentObject var model: Model
    
    var body: some View {
        NavigationView {
            VStack {
                Button("Push") {
                    self.model.pushed = true
                }
                
                NavigationLink(destination: DetailView(), isActive: $model.pushed) { EmptyView() }
            }
        }
    }
}

struct DetailView: View {
    @EnvironmentObject var model: Model

    var body: some View {
        Button("Bring me Back") {
            self.model.pushed = false
        }
    }
}

Workaround

Removing the default back button and adding our own will let us get through, until the bug gets fixed by Apple.

class Model: ObservableObject {
    @Published var pushed = false
}

struct ContentView: View {
    @EnvironmentObject var model: Model
    
    var body: some View {
        NavigationView {
            VStack {
                Button("Push") {
                    self.model.pushed = true
                }
                
                NavigationLink(destination: DetailView(), isActive: $model.pushed) { EmptyView() }
            }
        }
    }
}

struct DetailView: View {
    @EnvironmentObject var model: Model

    var body: some View {
        Button("Bring me Back") {
            self.model.pushed = false
        }
        .navigationBarBackButtonHidden(true)
        .navigationBarItems(leading: MyBackButton(label: "Back!") {
            self.model.pushed = false
        })
    }
}

struct MyBackButton: View {
    let label: String
    let closure: () -> ()

    var body: some View {
        Button(action: { self.closure() }) {
            HStack {
                Image(systemName: "chevron.left")
                Text(label)
            }
        }
    }
}

12 thoughts on “NavigationLink Back Button Bug”

  1. Hi guys and thank you, Javier for spotting that bug!
    It seems that it’s back again on 13.3, on 13.1 “standard back button” works prefect.

    Does anybody know other workarounds in this case? Switching “back button” to “custom back button” works for me only in case of two screens connected. If I push one more view on the top of the navigation stack, @State flag isPresented switches back to false, and “custom back button” stops working. Would appreciate any ideas.

    Reply
    • Andrey my recommendation is use https://github.com/biobeats/swiftui-navigation-stack or at least read over it and see if that helps you (though I’m over a month after your post and you’ve probably figured out a fix by now) — apparently using @State with navigation is just overall a bad idea (though interestingly enough, I’m seeing the same behavior you describe even when using ObservableObject and @Published in an “env” state class)

      Reply
  2. Yes navigationlink is very buggy. Example If I have multiple NavigationLinks deep into navigation hierarchy. Then I use presentationMode.wrappedValue.dismiss(), and on dismiss I am also passing ID to navigate to other screen from intermediate screen B (A -> B -> C/D, here back from D to B and based on id to C) then navigation link jumps all the stack to the root A with jumping screens. I need to add delay of 1 second when setting in B screen ID value to prevent this

    Reply
  3. There is another bug where the back button completely disappears.
    using this works only once. After which the backbutton disappears.

    
    NavigationLink(destination: Text("Hi"), tag: "y", selection: $navigateToCell) {
        EmptyView()
    }.isDetailLink(false)
    

    however, using this (with exact same code) works all the time

    
    NavigationLink(destination: Text("Hi"), isActive: $shouldNavigateToCell) {
       EmptyView()
    }
    
    Reply
  4. iOS 14.2 / Xcode 12

    Experiencing exactly same bug. Works every other time. Also changing binding value for isActive from DetailsScreen does nothing, but after that pressing back just pops screen.

    Reply
    • It’s so frustrating to struggle with what should be an improving language and platform. I don’t know when I’m at fault or Apple is. Has this one been fixed? I’m running into a number of weird things and some are centred around Navigation, so I’m either paranoid or on to something.

      Reply

Leave a Comment

By continuing to use the site, you agree to the use of cookies. more information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close