Broken NavigationLink behavior when using @StateObject and a ForEach inside a Form

I'm having this strange issue when I use @StateObject + ForEach and NavigationLink Inside a Form. This breaks bindings in the child view shown.

Consider the following ViewModel class with one published property to use from a View:

Code Block
final class ViewModel: ObservableObject {
@Published var isActive = false
}


When using this view:
Code Block
struct MainView: View {
@StateObject var viewModel = ViewModel()
var body: some View {
NavigationView {
Form {
NavigationLink (
destination: ChildView(isActive: $viewModel.isActive),
isActive: $viewModel.isActive,
label: { Text("Go to child view") }
)
/* Adding this ForEach causes the NavigationLink above to have a broken binding */
ForEach(1..<4) {
Text("\($0)")
}
}
.navigationBarTitle("Test")
}
}
}


And this SubView:
Code Block
struct ChildView: View {
@Binding var isActive: Bool
var body: some View {
Button("Go back", action: { isActive = false })
}
}


The Issue


The expected result is when tapping on "Go to child view", navigating
to the subview and tapping "Go back" to return to the main view - it should navigate back using the isActive binding.
But actually, the button "Go Back" Doesn't work.

BUT! If I remove the ForEach element from the form in the main view, the button works again. And it looks like the ForEach breaks everything.

Additional findings:

  1. Changing Form to VStack fixes the issue

  2. Using a struct and a @State also fixes the issue

  3. Extracting the ForEach to a subview fixes the issue but

as soon as I pass the viewmodel or part of it to the subview as a binding or as an ObservedObject - it still broken

Can anything advise if there is a logical issue with the code or is it a SwiftUI bug?
Any suggestions for a workaround?
Answered by OOPer in 662611022

Can anything advise if there is a logical issue with the code or is it a SwiftUI bug?

Very likely a big flaw in the current implementation of SwiftUI. Better send a feedback to Apple.

Any suggestions for a workaround?

Moving the NavigationLink out of Form looks like working in a simple case.
Code Block
struct MainView: View {
@StateObject var viewModel = ViewModel()
var body: some View {
NavigationView {
ZStack {
NavigationLink (
destination: ChildView(isActive: $viewModel.isActive),
isActive: $viewModel.isActive,
label: { EmptyView() }
)
Form {
HStack {
Text("Go to child view")
Spacer()
Image(systemName: "chevron.forward")
}
.contentShape(Rectangle())
.onTapGesture {
print(viewModel.isActive)
viewModel.isActive = true
}
ForEach(1..<4) {
Text("\($0)")
}
}
}
.navigationBarTitle("Test")
}
}
}

There may be other better workarounds, but I could not find any till now.
Accepted Answer

Can anything advise if there is a logical issue with the code or is it a SwiftUI bug?

Very likely a big flaw in the current implementation of SwiftUI. Better send a feedback to Apple.

Any suggestions for a workaround?

Moving the NavigationLink out of Form looks like working in a simple case.
Code Block
struct MainView: View {
@StateObject var viewModel = ViewModel()
var body: some View {
NavigationView {
ZStack {
NavigationLink (
destination: ChildView(isActive: $viewModel.isActive),
isActive: $viewModel.isActive,
label: { EmptyView() }
)
Form {
HStack {
Text("Go to child view")
Spacer()
Image(systemName: "chevron.forward")
}
.contentShape(Rectangle())
.onTapGesture {
print(viewModel.isActive)
viewModel.isActive = true
}
ForEach(1..<4) {
Text("\($0)")
}
}
}
.navigationBarTitle("Test")
}
}
}

There may be other better workarounds, but I could not find any till now.
@OOPer thanks for the suggestion, thats the best we can get I assume

Update: It looks like the issue has been fixed in the latest iOS 14.5 Beta 2
Broken NavigationLink behavior when using @StateObject and a ForEach inside a Form
 
 
Q