ForEach in List stops update propagation in DestinationView

Viewed 44

I think is very clear from this dummy example: if you remove the ForEach code row, magically, the propagation will flow and clock will tick, otherwise it will freeze once the detail view is presented.

class ModelView: ObservableObject {
    @Published var clock = Date()
    init() {
        Timer.publish(every: 1, on: .main, in: .default)
            .autoconnect()
            .print()
            .assign(to: &$clock)
    }
}

struct ContentView: View {

    @StateObject var viewModel = ModelView()
    
    var body: some View {
        NavigationView {
            List {
                NavigationLink("Clock", destination: Text(viewModel.clock, formatter: formatter))
                ForEach(0..<1) { _ in } // <- Remove this row and the clock will work
            }
        }
    }
    
    var formatter: DateFormatter {
        let formatter = DateFormatter()
        formatter.timeStyle = .medium
        return formatter
    }
}
1 Answers

I'd say it is rather some kind of coincidence that it works in first case, because destination is (or can be) copied on navigation (that's probably happens in second case).

The correct approach would be to have separated standalone view for details with own observer. Parent view should not update child view property if there is no binding.

Tested and worked with Xcode 12.4 / iOS 14.4

struct ContentView: View {

    @StateObject var viewModel = ModelView()
    
    var body: some View {
        NavigationView {
            List {
                NavigationLink("Clock", destination: DetailsView(viewModel: viewModel))
                ForEach(0..<1) { _ in } // <- Remove this row and the clock will work
            }
        }
    }
    
}

struct DetailsView: View {
    @ObservedObject var viewModel: ModelView
    
    var body: some View {
        Text(viewModel.clock, formatter: formatter)
    }

    var formatter: DateFormatter {
        let formatter = DateFormatter()
        formatter.timeStyle = .medium
        return formatter
    }
}
Related