SwiftUI Half-swipe back from navigation causes error

I noticed issue in SwiftUI when using NavigationStack

Once I swipe-back on a half and revert it -> it stops working

enter image description here

Also I attached sample code if you want to try it

import SwiftUI

struct ContentView: View {
    var body: some View {
        NavigationStack {
            ListView()
        }
    }
}

struct ListView: View {
    var body: some View {
        List {
            NavigationLink(destination: ViewA(viewModel: .init()), label: {
                Text("A")
            })
                
            NavigationLink(destination: ViewB(), label: {
                Text("B")
            })
        }
    }
}

struct ViewA: View {
    @StateObject var viewModel: Observed
    
    var body: some View {
        ZStack {
            List {
                Button(action: {
                    viewModel.action()
                }, label: {
                    Text("label")
                })
            }
            
            NavigationLink(isActive: $viewModel.shouldShowViewB, destination: {
                ViewB()
            }, label: {EmptyView()})
        }
        .navigationTitle("view a")
    }
}

struct ViewB: View {
    var body: some View {
        List {
            Button(action: {
                print("actionb")
            }, label: {
                Text("labelb")
            })
        }
        .navigationTitle("view b")
    }
}

class Observed: ObservableObject {
    @Published var shouldShowViewB = false
    
    func action() {
        print("action from model")
        shouldShowViewB = true
    }
}

Expected: whatever I do it should work as expected - when I tap it should open new view Anyone else found this issue? How to fix it?

Upvotes: 1

Views: 187

Answers (1)

ChrisR
ChrisR

Reputation: 12125

Issue 1 is you create the ObservedObject inside the NavigationLink with .init and then have a @StateObject declaration in the Subview ViewA(). That doesn't feel right. Create the Object with @StateObject in the parent view and pass it down.

Issue 2 is the new SwiftUI Navigation model, with NavigationLink)destination: label:) being deprecated. I adapted your code to the new navigation logic:

struct ContentView: View {
    var body: some View {
        NavigationStack {
            ListView()
        }
    }
}

struct ListView: View {
    
    @StateObject var viewModel = Observed() // create ObservedObject here
    
    var body: some View {
        List {
            NavigationLink("A") {
                ViewA(viewModel: viewModel) // pass down
            }
            NavigationLink("B") {
                ViewB()
            }
        }
    }
}

struct ViewA: View {
    
    @ObservedObject var viewModel: Observed // passed down Object
    
    var body: some View {
        ZStack {
            List {
                Button(action: {
                    viewModel.action()
                    print(viewModel.shouldShowViewB)
                }, label: {
                    Text("label")
                })
            }
            .navigationDestination(isPresented: $viewModel.shouldShowViewB, destination: { ViewB() })
        }
        .navigationTitle("view a")
    }
}

struct ViewB: View {
    var body: some View {
        List {
            Button(action: {
                print("actionb")
            }, label: {
                Text("labelb")
            })
        }
        .navigationTitle("view b")
    }
}

class Observed: ObservableObject {
    @Published var shouldShowViewB = false
    
    func action() {
        print("action from model")
        shouldShowViewB = true
    }
}

Upvotes: 1

Related Questions