SwiftUI Series 04|SwiftUI state management: selection of @State, @Binding, @ObservedObject, @StateObject
The real key is to first answer "Who owns this data and who drives the interface changes?"
The place where people are most likely to get “stuck” in SwiftUI is the state.
The most common questions are usually these:
- Should we use
@Stateor@Bindingfor this value? - ViewModel will be initialized repeatedly
- Why can’t the subview change the data of the parent view?
- The value has obviously changed, but the page is not refreshed as expected.
If you only rely on memorizing attribute wrappers for these problems, it is easy to become more and more confused. Because the real root cause is usually:
Without first thinking clearly about “who owns this state?”
1. Don’t rush to choose a wrapper. First ask: Whose data does this have?
This is the most important question.
In SwiftUI, every time you see a state value, ask yourself:
- Is this value owned by the current View?
- The parent view still owns it, and the subview is just borrowed and modified.
- Still owned by an external object, the current View is just observing
Once this question is answered clearly, many wrapper choices will come naturally. On the contrary, if you skip this step and directly ask “which one is commonly used in this scenario”, it will be easy to pile up code by trial and error.
2. @State is really suitable for “the current View’s own local state”
@State is most suitable for:
This state only belongs to the current View. Local existence, local consumption, and leaving this View are basically meaningless.
For example:
- The current content of the input box
- Whether to display sheet
- A certain partially expanded and collapsed state
- Current tab selection
What these states have in common is:
- Clear ownership
- The life cycle is strongly bound to this View
- Not worth upgrading to external business status
If a piece of state doesn’t make sense outside of the current View, it’s usually a good fit for @State.
3. The key point of @Binding is that “the ownership has not changed, but the write permission has been lent out”
A common situation is to understand @Binding as “subviews can also change the value of the parent view”.
This understanding is not wrong, but it is not accurate enough.
What’s more:
- This state still belongs to the parent view
- the subview doesn’t own it
- The subview only gets the read and write channels
This matter is critical because it determines whether the correct data attribution is retained when the component is scored.
That is to say:
@Stateis “my own”@Bindingis “still the same, but I am authorized to change it”
Once understood in terms of ownership, @Binding can no longer be mistaken for a “data sharing one-size-fits-all solution.”
4. @ObservedObject and @StateObject are most easily confused because people often mix “observation” and “possession”
The most common misuse of these two wrappers essentially comes from the same problem:
It is not clear whether “the current View observes an external object” or “the current View creates and stably holds this object”.
@ObservedObject
It is better to express:
- This object is passed in from outside
- The current View just observes it
- The current View is not responsible for creating it, nor is it responsible for stably holding it
@StateObject
It is better to express:
- The current View creates this object itself
- and want to hold it stably during View refresh
The fundamental difference between the two is not “which one is more advanced”, but rather:
- who created the object
- Who is responsible for object life cycle
This also shows that many ViewModel repeated initialization problems are essentially misplaced object ownership.
5. A very practical engineering judgment: local UI status and business status should be thought of separately
The status of many pages will be confusing, and the status of different levels will be lumped into a ball.
For example, a page may contain:
- Input box content
- Filter whether the pop-up window is open
- Network request results
- Page loading state
- Business object list
These values appear to be called “state”, but they are not the same type of state.
I usually roughly divide it into two categories:
1. Local UI state
For example:
- Whether to expand
- Current input content
- Currently selected item
- Partial display switch
This type of status is closer to @State / @Binding.
2. Business data status
For example:
- User information
- List data
- Loading process
- Request error
This type of state is usually better placed in an external observable object or higher-level state container.
Once these two types of states are not distinguished, the page will easily have local state, ViewModel state, and parent layer passing values. In the end, it is unclear who owns it.
6. Many problems of “the value has changed but the interface is wrong” are essentially caused by the state boundaries being mixed.
A common situation is that:
- I obviously changed the value, but why didn’t the page change as I thought?
This is of course sometimes the wrong wrapper, but more often:
- The state that should be owned by the parent layer is temporarily held by the child layer.
- Objects that should have existed for a long time are treated as temporary observation objects.
- Local UI status and business status overwrite each other
In other words, what looks like a “refresh problem” is often actually an ownership problem.
For many status problems in SwiftUI, once you check “who owns this data”, you can usually find the root cause faster than staring at the interface phenomenon.
7. A simplified judgment method that I often use
Although real projects will be more complicated, I often use this set of simple judgments in daily development:
1. Is this the simple state of the current View itself?
Yes, give priority to @State
2. Is this owned by the parent view and just borrowed and modified by the current subview?
Yes, give priority to @Binding
3. Is this an observable object passed in from outside?
Yes, give priority to @ObservedObject
4. Is this an object that the current View created and hopes to hold stably?
Yes, give priority to @StateObject
This set of judgments certainly does not cover all the details, but it is practical enough for a large number of business pages.
8. Conclusion: What we really need to understand first about state management is ownership.
To put it in shorter form, I would say:
In SwiftUI, the most important thing about state management is to first ask “Who owns this data and who is responsible for making the interface change with it.”
Once ownership is clear first, many wrapper choices will come naturally. On the other hand, if you don’t think clearly about the ownership and become familiar with the API later, it will be easy to write a page that “runs but becomes increasingly messy”.
读完之后,下一步看什么
如果还想继续了解,可以从下面几个方向接着读。