SwiftUI views presented via NavigationLink inside a List no longer renders an update in iOS 14.2

Originator:dessmith
Number:rdar://FB8892330 Date Originated:11/11/2020
Status:Open Resolved:
Product:SwiftUI Framework Product Version:14.2
Classification: Reproducible:Yes
 
As a developer when using iOS 14.2 if I have two views A and B, where view A holds an array of data and presents view B using a NavigationLink inside a List passing in an item from the data array.

When the user selects an item in the list. View B is displayed as expected, showing data from the data item passed to it via view A.

If any change is performed to the data array in view A, view B does not render an update.
In iOS 14.1 View B does render an update, however in iOS 14.2 view B does not render an update. 
Even if I use Xcode 12.2 RC (full version shown below) with iOS 14.1 simulator View B does render an update, but with 14.2 it does not render an update, but with breakpoints you can see the view model being updated.

I cannot see any reason for this change in the Release Notes of Xcode 12.2 RC (beta 4 (12B5044c), is this intentional or is there a bug in SwiftUI?

Below is the code for the example above.

import SwiftUI

struct NumberHolder: Identifiable {
    let id = UUID()
    let value:Int
}

struct Playground: View {
    @State var numbers:[NumberHolder] = [
        NumberHolder(value:1),
        NumberHolder(value:2),
        NumberHolder(value:3),
        NumberHolder(value:4),
    ]
    var body: some View {
        NavigationView {
            List(numbers.indices) { index in
                let number = numbers[index]
                NavigationLink(destination: VStack {
                    Text("Number: \(number.value)")
                    Button("Increment") {
                        numbers[index] = NumberHolder(value: number.value + 1)
                    }
                } ) {
                    Text("Number: \(number.value)")
                }
            }
        }
    }
}

struct Playground_Previews: PreviewProvider {
    static var previews: some View {
        Playground()
    }
}

Taken from https://stackoverflow.com/q/64611300/1132463. Note I am not the author of that Stack Overflow post

In addition …
I have the same behaviour in my own code that uses a NavigationLink inside of a List to present a detail view when you select a “row”, the main difference is the change is triggered by an Observable object’s @Published property being updated, which then kicks off the redraw for the whole view hierarchy (I can see this using breakpoints) as the main view is subscribing to it using @EnvironmentObject. 
When debugging I can see the view with the List updating with the updated model, and the detail view (that’s presented) update with the updated model (using breakpoints in the body property) but just like the previous example the view itself does not update. Also I’m sure I’ve observed view B being called first before view A, and also multiple times, but I might put that down to where I had my breakpoint in the body property, it’s called quite a bit when you have it one line below the body var declaration line. Anyway when an engineer looks into this in the framework implementation I’m sure they will find the issue.

/// I added the below one day later

I’ve since progressed in this somewhat.
On iOS 14.2 if I add a ForEach inside of the list, then add the NavigationLink, then the behaviour is as expected with the detail view updating.

However on WatchOS 7.1 the same does not work, and the detail view is not updated, however the code works fine in WatchOS 7.0

The code adjustment that I made (adding the ForEach is)

struct NumberHolder: Identifiable {
    let id = UUID()
    let value:Int
}

struct Playground: View {
    @State var numbers:[NumberHolder] = [
        NumberHolder(value:1),
        NumberHolder(value:2),
        NumberHolder(value:3),
        NumberHolder(value:4),
    ]
    var body: some View {
        NavigationView {
            List {
                ForEach(numbers.indices) { index in
                    let number = numbers[index]
                    NavigationLink(destination: VStack {
                        Text("Number: \(number.value)")
                        Button("Increment") {
                            numbers[index] = NumberHolder(value: number.value + 1)
                        }
                    } ) {
                        Text("Number: \(number.value)")
                    }
                }
            }
        }
    }
}

So in a nutshell adding ForEach inside the list works with iOS 14.2, but not with WatchOS 7.1

Comments


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!