Member-only story
Difference between @State, @StateObject, @Binding, @ObservedObject, @Environment, and @EnvironmentObject in SwiftUI
During the days of the 2020 lunar new year in Shanghai, under the shadow of the new COVID-19. I took my time to dive into SwiftUI.
I have to say, despite the truth that SwiftUI has made significant progress in user interface programming for iOS and macOS, it’s still a new approach that rapidly changing and poorly documented. I spend some time building a pilot project, tried a few things out, and took some notes.
Single source of truth
If you are familiar with React and Redux, you will see the resemblances in SwiftUI. It honors the principle of “Single source of truth”. When coding under SwiftUI, you don’t need to mix UI modules, logic, and rendering together. What you need to do, is define the layout in the form of data — a body
variable. It’s like JSX in react, separates data with logic by presenting interface from the single source of truth, avoids possible bugs.

Confusions
It’s not easy to understand all the jargon in SwiftUI, especially some of them even changed within a year since it’s released. Some attributes(kind of declaration in swift but start the line with an @
symbol that SwiftUI also called “property wrappers”), introduced on WWDC19, were already deprecated and changed their name by Apple.
For example, @ObjectBinding
changed to @Binding
, BindableObject
changed to ObservableObject
, etc. One thing that confused me most is the difference between @State, @Binding, @Environment, and @EnvironmentObject, and when to use each of them.
@State
This is the easiest part. Apple recommends using it as a single source of truth for local Views. It’s quite basic, so I won’t elaborate on it here. https://developer.apple.com/videos/play/wwdc2019/204/ has a very clear explanation.
But @State
only works within the local scope. So that is when we need @Binding
.
@Binding
When we need to pass a @State
variable to subviews, it’s time we need to use @Binding
to take the parent’s @State
variable as an argument, so the subview can render from, display by, and…