2

Background: I have been stuck on this annoying problem for days, trying different solution and searching stackoverflow etc for answers; some have had similar problems but no suggested solution had the desired effect.

The problem is that when updating an observableObject or environmentObject down the navigation hierarchy view stack, the views get popped back to root. Viewing data from observableObject is fine, but not editing.

Scenario is:

  1. I navigate to: root -> view1 -> view2.
  2. I update the environmentObject in View2 but then I get pushed back to: root -> view1

I have simplified my app in order to make it more understandable. See below:

ObservableObject:

class DataStore: ObservableObject {
    static let shared = dataStore() 
    @Published var name   : String = ""
}

RootView:

struct ContentView: View {
   @StateObject var dataStore = DataStore.shared
   @State private var isShowingView1 = false
    var body: some View {
        NavigationView{
            VStack{
                Text(dataStore.name)
                NavigationLink(destination: View1(), isActive: $isShowingView1) { }
                Button(action: {
                    isShowingView1 = true
                })
            }
        }
    }
}

View1:

struct View1: View {
    @EnvironmentObject var dataStore: dataStore
    @State private var isShowingView2 = false
    var body: some View {
        ScrollView{
            VStack(alignment: .center) {
                Text(dataStore.name)
                NavigationLink(destination: View2(), isActive: $isShowingView2) { }
                Button(action: {
                    isShowingView2 = true
                }){
                    Text("Go to View2")
                }
            }
        }
    }
}

View2:

struct View2: View {
    @EnvironmentObject var dataStore: dataStore
    var body: some View {
        ScrollView{
            VStack(alignment: .center) {
                Text(dataStore.name)
                Button(action: {
                    dataStore.name = "updated value"
                }){
                    Text("Update data")
                }
                // When updating this environmentObject the viewstack will be pushed back to View1. If view2 had been navigated to view3 and the view3 had been updating the environmentObject, then it would also be pushed back to View1.
            }
        }
    }
}

Solution: I spent many hours searching for solutions and trying different approaches, but nothing I tried worked. There seemed to be a few other people that had the same problem as I experienced, but the suggested solutions didn't cut it. But then I stumbled on a solution for this problem when trying to implement another feature. So to be frank I am writing here now, not to ask this great community for help, but instead to give back to the community by providing the this solution to others that might need to see this.

The solution is really simple implement but was not so easy to come across. If you experience a problem similar to me then you will only need to update your rootView accordingly:

RootView Updated:

struct ContentView: View {
   @StateObject var dataStore = DataStore.shared
   @State private var isShowingView1 = false
    var body: some View {
        NavigationView{
            VStack{
                Text(dataStore.name)
                NavigationLink(destination: View1(), isActive: $isShowingView1) { }
                Button(action: {
                    isShowingView1 = true
                })
            }
        }
        .navigationViewStyle(.stack)
        //ADD THIS LINE ABOVE
    }
}

This one line .navigationViewStyle(.stack) fixed the problem of popping the viewstack for me. Unfortunately I can't provide you with the logic explanation for this behaviour, but it works and I am satisfied with that. Perhaps you are too, or perhaps you have insight on why this solution actually achieves the desired effect of allowing views down the hierarchy update observableObjects without being popped.

Wittenstam
  • 41
  • 5
  • Since it seems you already have an answer, you should update your question so that it includes just the question and then add an answer yourself. Then, you can mark it as accepted after 2 days. – jnpdx Oct 25 '21 at 21:15
  • 1
    Does this answer your question? [SwiftUI Navigation popping back when modifying list binding property in a pushed view](https://stackoverflow.com/questions/69143014/swiftui-navigation-popping-back-when-modifying-list-binding-property-in-a-pushed) – workingdog support Ukraine Oct 25 '21 at 23:52
  • What about iOS 14? Since .stack is not available in iOS 13 and 14. – Tanvirgeek Nov 01 '21 at 10:17
  • Are you sure? According to this site https://developer.apple.com/documentation/swiftui/navigationviewstyle/stack the .Stack view style of navigationView seems to be available since iOS 13.0. And according to this site https://developer.apple.com/support/app-store/ 90% of devices are iOS 14.0 and above. So I think this solution should cover it. Or do you have some other information that I’m missing? – Wittenstam Nov 01 '21 at 18:34

2 Answers2

2

The solution is really simple implement but was not so easy to come across. If you experience a problem similar to me then you will only need to update your rootView accordingly:

RootView Updated:

struct ContentView: View {
   @StateObject var dataStore = DataStore.shared
   @State private var isShowingView1 = false
    var body: some View {
        NavigationView{
            VStack{
                Text(dataStore.name)
                NavigationLink(destination: View1(), isActive: $isShowingView1) { }
                Button(action: {
                    isShowingView1 = true
                })
            }
        }
        .navigationViewStyle(.stack)
        //ADD THIS LINE ABOVE
    }
}

This one line .navigationViewStyle(.stack) fixed the problem of popping the viewstack for me. Unfortunately I can't provide you with the logic explanation for this behaviour, but it works and I am satisfied with that.

Wittenstam
  • 41
  • 5
  • just like the answer here: https://stackoverflow.com/questions/69143014/swiftui-navigation-popping-back-when-modifying-list-binding-property-in-a-pushed/69143161#69143161 and here: https://stackoverflow.com/questions/69290862/environment-dismiss-bug-causes-popped-view-to-load-a-new-version-of-itself-i/69294393#69294393 and many other places. – workingdog support Ukraine Oct 25 '21 at 22:27
  • Thats great. In my search for this problem I didn't come across those, maybe the keywords didn't align. Now with my addition on top of the two you mention, I hope that more people will find the solution they need when they search. – Wittenstam Oct 26 '21 at 21:37
  • What about in iOS 14? – Tanvirgeek Nov 01 '21 at 10:13
  • Thank you! for sharing this. I went down a 2 hour rabbit hole – Aaron A Apr 10 '23 at 01:38
1

NavigationView still has this strange behavior.

However, NavigationStack seems to have fixed this issue.

zzzwco
  • 184
  • 6
  • Thanks for the tip, even though I cannot confirm this since NavigationStack requires iOS16 (still need to support iOS15) – smat88dd Jul 19 '23 at 15:56