@State and @Published properties in child views, SwiftUI

Viewed 181

In MyController, I have the following property which is updated in other methods that are called:

@Published public var data = [Glucose]()

I also have a function, which limits this Published property by a given limit:

public func latestReadings(limit: Int = 5) -> [Glucose] {
        // return latests results
}

In a SwiftUI View, I consume this data by the following, which works fine and updates when MyController's data changes:

    @EnvironmentObject var data: MyController

    var body: Some View {
        ForEach(self.data.latestReadings(limit: 11), id: \.self) {
            /// Display Text etc.
        }
    }

But, I want to call the following here, which converts the Glucose readings into a DataPoint array which the Chart consumes:

Chart(
    data: self.data.latestReadings(limit: 37),
    formattedBy: { (readings) -> [DataPoint] in
        var result = [DataPoint]()
        var i = 0
        for reading in readings {
            result.append(DataPoint(x: Double(i), y: reading.mmol))
                i += 1
            }
            return result
        }
    )

...Which refers to another SwiftUI View defined as:

struct Chart: View {
    // Properties
    @State var data: [DataPoint] // I asusme this should be @State
    var opt: ChartOptions
    
    // Formatters
    private var fmt: Formatting = Formatting.shared
    
    // Init
    public init(data: [Glucose], formattedBy:ChartDataFormatter) {
        _data = State(wrappedValue: formattedBy(data)) // Again I assume this is probably wrong..
    }

    ...draw views etc.
}

This all works on the first time the Chart is drawn, but the data property on the Chart view doesn't re-draw as the MyController data property changes. I assume I'm doing something wrong with state and observing changes here?

2 Answers

If I understood your workflow correctly you don't need state wrapper in Chart, because it prevents value update... so try without it, like

struct Chart: View {
    // Properties
    var data: [DataPoint]

    // ... 
    
    // Init
    public init(data: [Glucose], formattedBy:ChartDataFormatter) {
        self.data = formattedBy(data)
    }

    // ...

@State breaks the connection with your Controller. Per the documentation @State should always be private.

Pass the data using @EnvironmentObject and manipulate it within the view or in the Controller.

Related