Should EnvironmentObject pass through NavigationLink

I am writing a SwiftUI App in Xcode 12.0 beta (12A6159), the app is intended to work on iOS13.5 and up.

I am instantiating ObservableObjects at one level of my View hierarchy in the View's init() function, passing them as appropriate using .environmentObject, then attempting to pick them up in @EnvironmentObject in Views several layers deeper.

The consuming Views are getting the error:
A View.environmentObject(_:) for ********* may be missing as an ancestor of this view.

After careful testing, I find that the EnvironmentObjects are not being passed through NavigationLink.

We already have the well known issue of these not being passed through .sheet presentation (a major inconvenience), now we are contending with the same issue with NavigationLink?

Is this intentional or a bug? Is there a workaround?
It is definitely not what I would consider my preferred design.

Replies

Hmm. I wrote a mock app that has multiple NavigationLinks and I can read an EnvironmentObject down multiple layers. However, I'm running this in Xcode 11.5, on the simulator running iOS 13.5. So if you're not able to do this, perhaps it's a bug in Xcode 12 and not iOS? Or Maybe you're running it from the Xcode 12 beta on an iOS 14 device, and you're hitting an iOS 14 beta bug?

Code Block
import SwiftUI
struct ContentView: View {
// PassedObject is just a class that is an ObservableObject with one @Published property, name: String
// it is injected into the environment in the SceneDelegate
    @EnvironmentObject var nameObject: PassedObject
    var body: some View {
        NavigationView{
            VStack {
                Text("Hello, \(nameObject.name)!")
            NavigationLink(destination: SecondView()) {
                Text("Click!")
            }.buttonStyle(PlainButtonStyle())
            }
        }
    }
}
struct SecondView: View {
    var body: some View {
        VStack {
            Text("Second View!")
            NavigationLink(destination: ThirdView()) {
                Text("Click Again!")
            }.buttonStyle(PlainButtonStyle())
        }
    }
}
struct ThirdView: View {
    var body: some View {
        VStack {
            Text("Third View")
            NavigationLink(destination: FourthView()) {
                Text("Once More!")
            }.buttonStyle(PlainButtonStyle())
        }
    }
}
struct FourthView: View {
    @EnvironmentObject var passedName: PassedObject
// Reading from the environment here, despite multiple views in-between
    var body: some View {
        VStack {
            Text("Hello \(passedName.name)")
            TextField("Change Name", text: $passedName.name)
.textFieldStyle(RoundedBorderTextFieldStyle())
        }
    }
}


Hello,

I have the exact same problem with one of my apps. All works without any problems on iOS 13 but in the latest Xcode beta and iOS 14 it won't work, no @EnvironmentObject is working. Is there anyway to escalate this?
You probably did something like this:

Code Block swift
NavigationView {
ContentView()
.environmentObject(yourObject)
}


whereas you have to do this instead (i.e. put the environmentObject *outside* the NavigationView

Code Block swift
NavigationView {
ContentView()
}
.environmentObject(yourObject)


  • The solution provided by @Gargoyle was indeed the issue I had, and placing the environment object on the NavigationView solved the problem for me. :)

  • Will this work in a NavigationLink. Suppose I want to initialize and pass an environment object from an intermediatory view in the navigation stack. Is this possible?

  • This was it. I can imagine now why this would be the case (subviews presented through a NavigationView are descendants of the NavigationView and not the view that contains the NavigationLink), but wow this is an insidious gotcha even for SwiftUI.

Add a Comment

I'm able to read the environment object variable just fine, but writing to it triggers a dismiss of the current view. I'm beating my head against the wall on that.