I have the following view model:
class ViewModel: ObservableObject {
@Published var brightnessValue: Double = 0.0
@Published var saturationValue: Double = 0.0
@Published var contrastValue: Double = 0.0
}
In the UI layer I then have 3 sliders, implemented as SwiftUI views, each bound to a Double from the above view model. Whenever a slider changes a property, all 3 sliders redraw. That's because a change to a Published property in an ObservableObject redraws all the views that reference it.
This is how the SwiftUI views looks like:
struct RootView: View {
@ObservedObject var viewModel: ViewModel
var body: some View {
VStack {
AdjustmentSlider(name: "brightness", sliderValue: $viewModel.brightnessValue)
AdjustmentSlider(name: "saturation", sliderValue: $viewModel.saturationValue)
AdjustmentSlider(name: "contrast", sliderValue: $viewModel.contrastValue)
}
.background(.random)
}
}
struct AdjustmentSlider: View {
let name: String
@Binding var sliderValue: Double
var body: some View {
Slider(value: $sliderValue)
.background(.randomColor) // I use this to visualize the redrawing.
}
}
Finally, this is the overall (sample) app design:
This is will be part of a very complex app, with dozens of sliders, so I'm afraid this constant redrawing could become a non-trivial performance bottleneck as the view model becomes more complex.
Any suggestions on how to design a more efficient view model (i.e., less redraws) for SwiftUI? And, more broadly, is this even a valid concern when using SwiftUI circa iOS 16?

In SwiftUI the
Viewstruct is a view model and holds the view data. So using an object for it instead is already inefficient and will lead to consistency bugs. We are supposed to useletfor data that doesn't change,@Statefor data that does change, and@State varstruct to group related vars together and you can usemutating funcfor any logic you'd like to test independently. Use computed var to transform data as you pass it into subview inits inbody, e.g.MySubView(myComputedVar), that is the point of theViewstruct hierarchy - to transform from rich model types to simple types on the way down. In your case it should be like this:View structs do not "draw" anything. These are super-fast lightweight value types, like
int x = 3. It is basically negligible to generate theViewstruct because it is stored on the memory stack not the heap. SwiftUI recomputes the parts ofViewhierarchy where it detected a data dependency change (it records what Views call the@Stategetters), it diffs the hierarchy from last time and it uses the result on that to add/remove/updateUIViewControllerandUIViewobjects automatically for us. So basically we don't need to worry about View init and body called often, but we need to use value types effectively like@Stateand@Bindingand try not to use objects and never init an object inside of a View struct, since the struct is constantly recreated, it can't hang on to an object - thus doing that is essentially is a memory leak and will slow it down constantly doing pointless heap allocations.To make SwiftUI more efficient, just break everything up into Views that are as small as possible and where the
bodyonly uses the lets/vars that are defined in that View. That is called having "more tightly scoped invalidation" (Data essentials in SwiftUI WWDC 2020 @ 12:21)