I would like to use a struct
instead of a class
as a state for my View
, and as you may know, ObservableObject
is a protocol only classes can conform to.
Do I have to wrap my struct in a ViewModel
or some other similar type of object ? What happens if I don't ?
A sample on what that looks like now :
import Foundation
import SwiftUI
struct Object3D {
var x : Int
var y : Int
var z : Int
var visible : Bool
}
struct NumberView<Number : Strideable> : View {
var label : String
@State var number : Number
var body : some View {
HStack {
TextField(
self.label,
value: self.$number,
formatter: NumberFormatter()
)
Stepper("", value: self.$number)
.labelsHidden()
}
}
}
struct ObjectInspector : View {
@State var object : Object3D
var body : some View {
VStack {
Form {
Toggle("Visible", isOn: $object.visible)
}
Divider()
Form {
HStack {
NumberView(label: "X:", number: object.x)
NumberView(label: "Y:", number: object.y)
NumberView(label: "Z:", number: object.z)
}
}
}
.padding()
}
}
struct ObjectInspector_Previews: PreviewProvider {
static var previews: some View {
ObjectInspector(object: Object3D(x: 0, y: 0, z: 0, visible: true))
}
}
CodePudding user response:
You don't have to use @ObservedObject
to ensure that updates to your model object are updating your view.
If you want to use a struct
as your model object, you can use @State
and your view will be updated correctly whenever your @State
struct
is updated.
There are lots of different property wrappers that you can use to update your SwiftUI views whenever your model object is updated. You can use both value and reference types as your model objects, however, depending on your choice, you have to use different property wrappers.
@State
can only be used on value types and @State
properties can only be updated from inside the view itself (hence they must be private
).
@ObservedObject
(and all other ...Object property wrappers, such as @EnvironmentObject
and @StateObject
) can only be used with classes that conform to ObservableObject
. If you want to be able to update your model objects from both inside and outside your view, you must use an ObservableObject
conformant type with the appropriate property wrapper, you cannot use @State
in this case.
So think about what sources your model objects can be updated from (only from user input captured directly inside your View
or from outside the view as well - such as from other views or from the network), whether you need value or reference semantics and make the appropriate choice for your data model accordingly.
For more information on the differences between @ObservedObject
and @StateObject
, see What is the difference between ObservedObject and StateObject in SwiftUI.
CodePudding user response:
I would like to use a struct instead of a class as a state for my View, and as you may know, ObservableObject is a protocol only classes can conform to.
A model is usually shared among whichever parts of the app need it, so that they're all looking at the same data all the time. For that, you want a reference type (i.e. a struct), so that everybody shares a single instance of the model. If you use a value type (i.e. a class), your model will be copied each time you assign it to something. To make that work, you'd need to copy the updated info back to wherever it belongs whenever you finish updating it, and then arrange for every other part of the app that might use it to get an updated copy. It's usually a whole lot easier and safer to share one instance than to manage that sort of updating.
Do I have to wrap my struct in a ViewModel or some other similar type of object ? What happens if I don't ?
It's your code -- you can do whatever you like. ObservableObject provides a nice mechanism for communicating the fact that your model has changed to other parts of your program. It's not the only possible way to do that, but it's the way that SwiftUI does it, so if you go another route you're going to lose out on a lot of support that's built into SwiftUI.