Why @Published updates Text but doesn't update List?

152 views Asked by At

I don't understand why the Text updates but the List does not.

I have a model with a @Published which is an array of dates. There's also a method to generate text from the dates.

Here's a simplified version:

class DataModel: NSObject, ObservableObject {
    
    @Published var selectedDates: [Date] = []

    func summary() -> String {
        var lines: [String] = []
        for date in selectedDates.sorted().reversed() {
            let l = "\(someOtherStuff) \(date.dateFormatted)"
            lines.append(l)
        }
        return lines.joined(separator: "\n")
    }

}

And I show this text in my view. Here's a simplified version:

struct DataView: View {

    @ObservedObject var model: DataModel

    var body: some View {
        ScrollView {
            Text(model.summary())
        }
    }

}

It works: when the user, from the UI in the View, adds a date to model.selectedDates, the summary text is properly udpated.


Now I want to replace the text with a List of lines.

I change the method:

    func summary() -> [String] {
        var lines: [String] = []
        for date in selectedDates.sorted().reversed() {
            let l = "\(someOtherStuff) \(date.dateFormatted)"
            lines.append(l)
        }
        return lines
    }

And change the view:

    var body: some View {
        ScrollView {
            List {
                ForEach(model.summary(), id: \.self) { line in
                    Text(line)
                }
            }
        }
    }

But this doesn't work: there's no text at all in the list, it is never updated when the user adds a date to model.selectedDates.

What am I doing wrong?

3

There are 3 answers

2
Nate On BEST ANSWER

I was able to find this answer to a previous post on SO that may be the solution to your problem. When you say that there was no text at all in the list, did you debug to verify that, or was there simply no data being shown on the screen?

When I made a copy of your code snippet and fiddled with it, it appeared that there actually was data in the list that the List was attempting to display on the screen, but the frame of the List was being overridden by the Scrollview. Nesting everything inside of a GeometryReader and giving a frame size to the List gave the program the functionality it sounds like you are looking for.

0
nicksarno On

Either use a ScrollView or a List for your data. If a List has multiple values, it automatically turns into a scrollable List. So, I believe you can just remove the ScrollView entirely.

0
Asperi On

Here is a fixed worked variant (with some replications - so you need to adapt it back to your project).

Tested with Xcode 12.1 / iOS 14.1

class DataModel: NSObject, ObservableObject {
    
    @Published var selectedDates: [Date] = []
    
    func summary() -> [String] {
        var lines: [String] = []
        for date in selectedDates.sorted().reversed() {
            let l = "someOtherStuff \(date)"
            lines.append(l)
        }
        return lines
    }
    
}

struct DataView: View {
    
    @ObservedObject var model: DateDataModel = DataModel()
    
    var body: some View {
        VStack {
            Button("Add") { model.selectedDates.append(Date())}
            List {
                ForEach(model.summary(), id: \.self) { line in
                    Text(line)
                }
            }
        }
    }
    
}