I have a SwiftUI project using MVVM structure but I have lots of variables in View now, is that right?How can I improve this?
For example, this is my add record view
@State private var money: String = ""
@State private var desc: String = ""
@FocusState var isDescFieldFocused: Bool
@State private var showDatePicker: Bool = false
@State private var showingAlert: Bool = false
@State private var showConstantlyAlert: Bool = false
@State private var showKeyPad: Bool = false
@State private var showCatesPanel: Bool = false
@State private var selectedCate: String = ""
Should I put all these in View Models?
2
Answers
Like you said, you could create a view model for that view, which would either be a
@StateObject
or@ObservedObject
(depends on whether you want to create the view model within the view or inject it from outside).I tend to create view models for my views if they require any logic and not only display information which is passed down from parent views.
Thus, you could do this:
and use it within your view like so:
Apple addresses this in Data Essentials in SwiftUI WWDC 2020 at 4:18
So in your case it would look like:
Just to let you know, MVVM (i.e. using objects for view data) is not suitable for SwiftUI because the View struct and property wrappers is equivalent to a view model object but faster and less error-prone. If you use actual objects instead of learning SwiftUI’s features you’ll have problems.
The time we use an
ObservableObject
in SwiftUI is for model data, i.e. the environment object/singleton that stores the arrays of model structs in@Published
properties and is responsible for loading/saving/syncing etc. the model data etc. That is covered in the same video later on, from about 9mins 30secs.For write-access to model data we use
@Binding
.We used to also use
@StateObject
when we need a reference type as state, e.g. for async features with lifetime tied to something on screen like networking, but that is pretty much redundant now we have the.task(id:)
modifier which achieves the same thing. Because we can simply set the result (or thrown error) of await to an@State
and the task will be automatically cancelled when the view disappears (and also cancelled and restarted if an id passed totask(id:)
changes). It would take a lot of code and testing to reimplement that behaviour in a@StateObject
.