Reputation: 1216
I feel like this is a question that would have already been asked somewhere, but I can't find much on it.
When using a variable for the purpose of updating the UI, when/why would we use @State
within our view as opposed to using @Published
within a ViewModel?
This is in the context of me trying to grasp MVVM architecture. I understand the difference generally, just not when it comes to something that both could easily accomplish the same way.
Below, I have 2 examples that do the same thing, but one uses @State
while the other uses @Published
and a ViewModel. Is one approach better than the other (for updating the UI purposes?)
@State
example:
struct MyView: View {
@State var backgroundIsRed = false
var body: some View {
ZStack {
if backgroundIsRed {
Color.red
} else {
Color.green
}
}
.onTapGesture { backgroundIsRed.toggle() }
}
}
@Published
example:
class ViewModel: ObservableObject {
@Published var backgroundIsRed = false
}
struct MyView: View {
@StateObject var viewModel = ViewModel()
var body: some View {
ZStack {
if viewModel.backgroundIsRed {
Color.red
} else {
Color.green
}
}
.onTapGesture { viewModel.backgroundIsRed.toggle() }
}
}
Upvotes: 18
Views: 12493
Reputation: 30582
@State
is for your view data.
ObservableObject
and @Published
is for your model data.
See Data Essentials in SwiftUI WWDC 2020 from 9:25
My name is Luca. My colleague, Curt, has just described how to use State and Binding to drive changes in your UI, and how these tools are a great way to quickly iterate on your view code.
But State is designed for transient UI state that is local to a view. In this section, I want to move your attention to designing your model and explain all the tools that SwiftUI provides to you.
Typically, in your app, you store and process data by using a data model that is separate from its UI.
This is when you reach a critical point where you need to manage the life cycle of your data, including persisting and syncing it, handle side-effects, and, more generally, integrate it with existing components. This is when you should use ObservableObject. First, let's take a look at how ObservableObject is defined.
Thus if you try to use ObservableObject
for view data it seems you'll be going off track.
Upvotes: -4
Reputation: 5146
@State
and @Published
serve different purposes in SwiftUI:
@State
: This property wrapper is used to declare state information within a SwiftUI view. It's typically used for local state within a view, meaning data that is relevant only to that specific view and doesn't need to be shared across multiple views or persisted beyond the lifetime of the view. @State
is useful for managing things like whether a button is currently pressed, the current selection in a picker, or whether a modal is presented.
@Published
: This property wrapper is used in conjunction with the ObservableObject protocol to create observable objects. It's commonly used in SwiftUI apps with the MVVM pattern to represent the ViewModel layer. @Published
is used to expose properties from the ViewModel that the View observes for changes. When a property marked with @Published changes, it automatically triggers the view to update any affected parts of its UI.
When to use each:
@State
for managing simple, view-specific state that doesn't need to be shared with other views or persisted.@Published
within a ViewModel when you have data that needs to be shared across multiple views or needs to persist beyond the lifetime of a single view.In summary, @State is used for managing local, view-specific state, while @Published
is used for managing shared state and facilitating communication between views and their associated ViewModels.
So, Example 1 might be considered better if the state is very localized and simple. Example 2 is better if you anticipate needing to share this state with other views or if you prefer to follow a more structured architectural pattern like MVVM.
Upvotes: 14
Reputation: 341
For example, I would say that the "Published" approach helps you create a test structure for your VM.
Taking your example, you could create a protocol:
protocol ViewModelProtocol {
var backgroundIsRed: Bool { get }
var date: Date { get } // Created for example purposes
}
And then:
class ViewModel: ViewModelProtocol, ObservableObject {
@Published var backgroundIsRed = false
@Published var date = Date()
}
class ViewModelMock: ViewModelProtocol, ObservableObject {
@Published var backgroundIsRed = true
@Published var date = Mock.Date
}
struct MyView: View {
@StateObject var viewModel: ViewModelProtocol = ViewModel()
//@StateObject var viewModel: ViewModelProtocol = ViewModelMock()
var body: some View {
ZStack {
if viewModel.backgroundIsRed {
Color.red
} else {
Color.green
}
}
.onTapGesture { viewModel.backgroundIsRed.toggle() }
}
}
On the other hand, the State approach presents a more straightforward way to implement the logic.
Apart from that, there isn't any particular reason to think that one is better than the other. Hope this helps you choose which one you should use in each situation.
Upvotes: 5