This content originally appeared on DEV Community and was authored by Harsh Prajapat
SwiftUI’s property wrappers are like magical tools that help you manage state, data flow, and environment context in a declarative way. Let’s break down the most important ones so you can see how they fit together.
Core Property Wrappers in SwiftUI
Wrapper | Purpose | Ownership | Typical Use |
---|---|---|---|
@State |
Local value-type state | ![]() |
Simple UI state (e.g. toggles, counters) |
@Binding |
Two-way connection to another value | ![]() |
Pass state between parent and child views |
@StateObject |
Owns a reference-type observable object | ![]() |
Create and manage ObservableObject instances |
@ObservedObject |
Observes external ObservableObject
|
![]() |
Watch changes in shared objects |
@EnvironmentObject |
Access shared object from environment | ![]() |
Share data across many views |
@Environment |
Read system/environment values | ![]() |
Access traits like color scheme, locale |
@AppStorage |
Read/write to UserDefaults
|
![]() |
Persist user settings (e.g. theme, login) |
@Published |
Notify views of changes inside ObservableObject
|
![]() |
Mark properties that trigger view updates |
@Observable |
New macro replacing ObservableObject + @Published
|
![]() |
Cleaner, more efficient state tracking |
@Bindable |
Enables bindings to @Observable properties |
![]() |
Use $property syntax with @Observable
|
Example: Using @State
and @Binding
struct ParentView: View {
@State private var isOn = false
var body: some View {
ToggleView(isOn: $isOn)
}
}
struct ToggleView: View {
@Binding var isOn: Bool
var body: some View {
Toggle("Enable Feature", isOn: $isOn)
}
}
Example: Using @StateObject
and @ObservedObject
class CounterModel: ObservableObject {
@Published var count = 0
}
struct CounterView: View {
@StateObject private var model = CounterModel()
var body: some View {
VStack {
Text("Count: \(model.count)")
Button("Increment") {
model.count += 1
}
}
}
}
If you pass model
to another view, use @ObservedObject
there.
Example: Using @EnvironmentObject
class Settings: ObservableObject {
@Published var isDarkMode = false
}
struct RootView: View {
@StateObject private var settings = Settings()
var body: some View {
ContentView()
.environmentObject(settings)
}
}
struct ContentView: View {
@EnvironmentObject var settings: Settings
var body: some View {
Toggle("Dark Mode", isOn: $settings.isDarkMode)
}
}
Want to dive deeper into @Observable
, @Bindable
, or how these wrappers interact with SwiftUI’s rendering engine? I can walk you through advanced patterns or help refactor your code.
This content originally appeared on DEV Community and was authored by Harsh Prajapat