Stoic
Stoic

Reputation: 1216

When to use @State vs @Published

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

Answers (3)

malhal
malhal

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

DàChún
DàChún

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:

  • Use @State for managing simple, view-specific state that doesn't need to be shared with other views or persisted.
  • Use @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

Jorge Poveda
Jorge Poveda

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

Related Questions