Alex.K
Alex.K

Reputation: 294

@State vs @ObservableObject - which and when?

I'm currently getting familiar with SwiftUI and Combine frameworks. And I'm not really getting the difference between these two approaches. When we have to keep track of some data (say, a list of tasks), we can declare a @State variable, and it's change will automatically send notification and update current view. However, it looks like it can also be done this way:

class TaskList: ObservableObject{
    //a list that's going to be modified and updated on different occasions
    @Published var list: [String]
}

class TodoListView {
    @ObservedObject var todoList = TaskList()
}

So, I missing a point - how are @State and @ObservedObject approaches different and which one is preferable under which circumstances?

Thanks!

Upvotes: 13

Views: 5886

Answers (2)

David Pasztor
David Pasztor

Reputation: 54735

If you mark any variables as @State in a SwiftUI View and bind them to a property inside the body of that View, the body will be recalculated whenever the @State variable changes and hence your whole View will be redrawn. Also, @State variables should serve as the single source of truth for a View. For these reasons, @State variables should only be accessed and updated from within the body of a View and hence should be declared private.

You should use @State when you are binding some user input (such as the value of a TextField or the chosen value from a Picker). @State should be used for value types (structs and enums).

On the other hand, @ObservedObject should be used for reference types (classes), since they trigger refreshing a view whenever any @Published property of the ObservableObject changes.

You should use @ObservedObject when you have some data coming in from outside your View, such as in an MVVM architecture with SwiftUI, your ViewModel should be stored as an @ObservedObject on your View.

A common mistake with @ObservedObjects is to declare and initialise them inside the View itself. This will lead to problems, since every time the @ObservedObject emits an update (one of its @Published properties gets updated), the view will be recreated - which will also create a new @ObservedObject, since it was initialised in the View itself. To avoid this problem, whenever you use @ObservedObject, you always have to inject it into the view. The iOS 14 @StateObject solves this issue.

Upvotes: 22

George
George

Reputation: 30411

The main difference is that @State is for structs, and @ObservedObject is for classes. Both @State and @ObservedObject achieve a similar thing, of updating you when something changes.

A struct changes when some property has been mutated, which means that it gets recreated, therefore the @State is updated. A class updates @ObservedObject when a property is changed - using @Published to listen for changes. When either @State or @ObservedObject is updated, the view body gets remade.

The question you are really asking here is when to use a struct vs a class if either would work in some situations.

In your case, since TaskList is only a basic data structure and doesn't require lots of properties you want to prevent from updating the view (by using/not using @Published), you should probably use a struct with @State instead.

Upvotes: 8

Related Questions