Navigation Disappears after Updating EnvObject

Xcode: Version 12.1 (12A7403)
iOS: 14.x

I have run into this weird bug where the navigation bar "back button" disappears when a child view updates the list used by the navigation view's root list. I've put together some MVP code to demo the bug:

Code Block swift
import SwiftUI
class ItemStore: ObservableObject {
    @Published var items = [Int]()
    init(items: [Int]) { self.items = items}
}
struct ContentView: View {
    @EnvironmentObject var store: ItemStore
    var body: some View {
        NavigationView {
            List {
                ForEach(store.items, id: \.self) { label in
                    NavigationLink(destination: Child1(label: label)) {
                        Text(String(label))
                    }
                }
            }
        }
    }
}
struct Child1: View {
    var label: Int
    var body: some View {
        List {
            NavigationLink(destination: Child2(label: label)) {
                Text("Child1: \(label)")
            }
        }
    }
}
struct Child2: View {
    @EnvironmentObject var store: ItemStore
    var label: Int
    var body: some View {
        Text("Child2: \(label)")
        .toolbar {
            Button(action: {
let max = store.items.max()
        store.items.append(max == nil ? 1 : max! + 1)
}) {
                Image(systemName: "plus")
            }
        }
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
            .environmentObject(ItemStore(items: [1,2,3]))
    }
}


So far, I've only encountered it on the iPhone in landscape mode. The button doesn't disappear when in landscape mode (for iPhones that support the larger iPad-like layout), iPads in both orientations, and MacOS. I haven't tested on WatchOS. It happens in the Xcode live preview, simulator, and on device.

I have found some mitigations that don't work for my use case. For example, it works when I replace the button closure with a method on either the view or env object:

Code Block swift
struct Child2: View {
    @EnvironmentObject var store: ItemStore
    var label: Int
    func increment() {
        let max = store.items.max()
        store.items.append(max == nil ? 1 : max! + 1)
    }
    var body: some View {
        Text("Child2: \(label)")
        .toolbar {
            Button(action: increment) {
                Image(systemName: "plus")
            }
        }
    }
}


But it still breaks when calling the method through a closure:

Code Block swift
Button(action: { increment() }) {


It must have something to do with the closure updating the env object. I'm more just surprised to find this happing in one specific use case, but it happens to be exactly how most users would interact with the app.
Accepted Answer
An interesting example. Thanks for sharing the info with reproducible code.

In my opinion, this is a bug of SwiftUI, and you should better send a feedback to Apple.


it works when I replace the button closure with a method on either the view or env object

As far as I tried on some iPhone simulators, it did not work.
(Or the latter Child2 is another example of not working?)

And I do not understand why, but with specifying navigationViewStyle explicitly, Back button did not disappear.
Code Block
struct ContentView: View {
@EnvironmentObject var store: ItemStore
var body: some View {
NavigationView {
List {
ForEach(store.items, id: \.self) { label in
NavigationLink(destination: Child1(label: label)) {
Text(String(label))
}
}
}
}
.navigationViewStyle(StackNavigationViewStyle())
}
}

(I know this cannot be the right solution for some targets and screen sizes, just for exploring the behavior.)
Explicitly specifying the navigationViewStyle fixed this in the demo and my app. Thank you. Filling out a bug report now.
Navigation Disappears after Updating EnvObject
 
 
Q