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 | ✅ Owns data | Simple UI state (e.g. toggles, counters) |
@Binding |
Two-way connection to another value | ❌ Refers to external data | Pass state between parent and child views |
@StateObject |
Owns a reference-type observable object | ✅ Owns data | Create and manage ObservableObject instances |
@ObservedObject |
Observes external ObservableObject
|
❌ Refers to external data | Watch changes in shared objects |
@EnvironmentObject |
Access shared object from environment | ❌ Refers to external data | Share data across many views |
@Environment |
Read system/environment values | ❌ Refers to external data | Access traits like color scheme, locale |
@AppStorage |
Read/write to UserDefaults
|
✅ Owns data | Persist user settings (e.g. theme, login) |
@Published |
Notify views of changes inside ObservableObject
|
✅ Owns data | Mark properties that trigger view updates |
@Observable |
New macro replacing ObservableObject + @Published
|
✅ Owns data | Cleaner, more efficient state tracking |
@Bindable |
Enables bindings to @Observable properties |
❌ Refers to external data | 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. 🧵💬
Top comments (0)