I’m seeing this issue where a Form / NavigationLink set up seems to stop passing through the data on changes.
Expected behavior: Checkmarks should update when you pick a different food.
Observed behavior: You can see the favorite food changing outside the NavigationLink Destination, but not inside.
This setup mirrors a dynamic application where a ForEach is used to display various NavigationLinks in the Form based on parent data. Weirdly enough, this works if you replace Form with VStack, so I’m curious why this isn’t updating.
I have attached two minimum-setup example projects that replicate this issue where the destination of a NavigationLink is not receiving an update when data is changing. One with Binding, one with simpler passed properties.
Sample Project #1 with Binding - Dropbox
Sample Project #2 without Binding - Dropbox
Code #1:
//
// ContentView.swift
// Form Updating Example
//
// Created by Sahand Nayebaziz on 12/10/20.
//
import SwiftUI
struct ContentView: View {
@State var isPresentingMainView = false
@State var favoriteFood: FoodType = .bagel
var body: some View {
VStack {
Button(action: { isPresentingMainView = true }, label: {
Text("Present Main View")
})
}
.fullScreenCover(isPresented: $isPresentingMainView) {
MainView(favoriteFood: $favoriteFood)
}
}
}
struct MainView: View {
@Binding var favoriteFood: FoodType
var body: some View {
NavigationView {
HStack {
Spacer()
Text(favoriteFood.emoji)
.font(.title)
.foregroundColor(.secondary)
Spacer()
NavigationView {
Form {
List {
ForEach(["SomethingRepresentingShowingFood"], id: \.self) { _ in
NavigationLink(
destination: makeDetail(),
label: {
Text("Food Randomizer")
})
}
}
}
}
.navigationViewStyle(StackNavigationViewStyle())
.frame(maxWidth: 350)
}
.navigationTitle("Main")
.navigationBarTitleDisplayMode(.inline)
}
.navigationViewStyle(StackNavigationViewStyle())
}
func makeDetail() -> some View {
Form {
ForEach(FoodType.allCases) { foodType in
Button(action: { favoriteFood = foodType }, label: {
HStack {
Text(foodType.emoji)
Spacer()
if favoriteFood == foodType {
Image(systemName: "checkmark")
}
}
})
}
}
}
}
enum FoodType: String, Identifiable, CaseIterable {
case bagel, pizza, broccoli
var id: String { rawValue }
var emoji: String {
switch self {
case .bagel: return ""
case .pizza: return ""
case .broccoli: return ""
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
Group {
ContentView()
MainView(favoriteFood: .constant(.bagel))
MainView(favoriteFood: .constant(.bagel))
.makeDetail()
}
}
}
Code #2:
//
// ContentView.swift
// Form Updating Example
//
// Created by Sahand Nayebaziz on 12/10/20.
//
import SwiftUI
struct ContentView: View {
@State var isPresentingMainView = false
@State var favoriteFood: FoodType = .bagel
var body: some View {
VStack {
Button(action: { isPresentingMainView = true }, label: {
Text("Present Main View")
})
}
.fullScreenCover(isPresented: $isPresentingMainView) {
MainView(currentFavoriteFood: favoriteFood, onUpdateFavoriteFood: { favoriteFood = $0 })
}
}
}
struct MainView: View {
let currentFavoriteFood: FoodType
let onUpdateFavoriteFood: (FoodType) -> Void
var body: some View {
NavigationView {
HStack {
Spacer()
Text(currentFavoriteFood.emoji)
.font(.title)
.foregroundColor(.secondary)
Spacer()
NavigationView {
Form {
List {
ForEach(["SomethingRepresentingShowingFood"], id: \.self) { _ in
NavigationLink(
destination: makeDetail(),
label: {
Text("Food Randomizer")
})
}
}
}
}
.navigationViewStyle(StackNavigationViewStyle())
.frame(maxWidth: 350)
}
.navigationTitle("Main")
.navigationBarTitleDisplayMode(.inline)
}
.navigationViewStyle(StackNavigationViewStyle())
}
func makeDetail() -> some View {
Form {
ForEach(FoodType.allCases) { foodType in
Button(action: { onUpdateFavoriteFood(foodType) }, label: {
HStack {
Text(foodType.emoji)
Spacer()
if currentFavoriteFood == foodType {
Image(systemName: "checkmark")
}
}
})
}
}
}
}
enum FoodType: String, Identifiable, CaseIterable {
case bagel, pizza, broccoli
var id: String { rawValue }
var emoji: String {
switch self {
case .bagel: return ""
case .pizza: return ""
case .broccoli: return ""
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
Group {
ContentView()
MainView(currentFavoriteFood: .bagel, onUpdateFavoriteFood: { _ in })
MainView(currentFavoriteFood: .bagel, onUpdateFavoriteFood: { _ in })
.makeDetail()
}
}
}