Posted in

A Simple Guide to SwiftUI Property Wrappers

A Simple Guide to SwiftUI Property Wrappers
Spread the love
Reading Time: 3 minutes

SwiftUI is all about data-driven UI. Property wrappers help SwiftUI decide when and how a view should update when the underlying data changes. This post covers the five key wrappers you’ll use most often — with short examples.

1. @State — Local State for a View

  • Use case: Simple values owned by a view (Bool, Int, String).
  • Behavior: When the value changes, SwiftUI re-renders the view.
struct CounterView: View {
    @State private var count = 0

    var body: some View {
        VStack {
            Text("Count: \(count)")
            Button("Increase") { count += 1 }
        }
    }
}

Here we have a CounterView with a Text("\(count)") and a Button, when count changes, SwiftUI calls body again, notices only the Text has different content, and updates just that label — the Button isn’t redrawn unnecessarily.

2. @Binding — Share and Mutate Parent State

  • Use case: Let a child view read and write a parent’s @State without copying it.
  • Behavior: @Binding provides a two-way connection.
struct ParentView: View {
    @State private var isOn = false

    var body: some View {
        VStack {
            Text(isOn ? "On" : "Off")
            ToggleView(isOn: $isOn)
        }
    }
}

struct ToggleView: View {
    @Binding var isOn: Bool

    var body: some View {
        Toggle("Enable", isOn: $isOn)
    }
}

🔑 Use $ to pass a binding ($isOn) and receive it with @Binding in the child.

3. ObservableObject — The Observable Class Protocol

  • Use case: For reference-type models that broadcast changes to views.
  • Behavior: Conform a class to ObservableObject so views can observe it.
class UserSettings: ObservableObject {
    @Published var username: String = "Guest"
}

ObservableObject works together with @Published, @StateObject, @ObservedObject, and @EnvironmentObject.

4. @Published — The Change Notifier (used inside ObservableObject)

  • Use case: Mark properties inside an ObservableObject that should trigger view updates.
  • Behavior: When a @Published property changes, the object emits updates.
class UserSettings: ObservableObject {
    @Published var username = "Guest"
    @Published var isLoggedIn = false
}

Note: @Published notifies when the property is set. Mutating internal state of reference types may not trigger updates unless the @Published property itself changes or you manually send objectWillChange.

5. @StateObject — Create & Own an ObservableObject

  • Use case: View creates and owns an ObservableObject instance (lifecycle tied to view).
  • Behavior: SwiftUI instantiates it once for the view’s lifetime.
class TimerModel: ObservableObject {
    @Published var seconds = 0
    init() {
        Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
            self.seconds += 1
        }
    }
}

struct TimerView: View {
    @StateObject private var timer = TimerModel()

    var body: some View {
        Text("Time: \(timer.seconds)")
    }
}

6. @ObservedObject — Observe an External ObservableObject

  • Use case: The view does not own the object; it only observes changes.
  • Behavior: Use when an ObservableObject instance is created elsewhere and injected into the view.
struct ProfileView: View {
    @ObservedObject var settings: UserSettings

    var body: some View {
        Text("Hello, \(settings.username)")
    }
}

7. @EnvironmentObject — App- or Hierarchy-wide Shared Data

  • Use case: Share a model across many views without passing it explicitly.
  • Behavior: Provide it high in the view tree with .environmentObject() and access via @EnvironmentObject.
@main
struct MyApp: App {
    @StateObject private var settings = UserSettings()

    var body: some Scene {
        WindowGroup {
            RootView()
                .environmentObject(settings)
        }
    }
}

struct ChildView: View {
    @EnvironmentObject var settings: UserSettings

    var body: some View {
        Toggle("Dark Mode", isOn: $settings.isLoggedIn) // example
    }
}

Quick Recap — When to Use Which

  • @State → local view-only state.
  • @Binding → child modifies parent state (two-way).
  • ObservableObject → make a class observable.
  • @Published → mark properties inside ObservableObject to notify changes.
  • @StateObject → view creates & owns the observable object.
  • @ObservedObject → view observes an externally created object.
  • @EnvironmentObject → share a single object across many views.

Final Tips & Common Mistakes

  • Don’t use @ObservedObject to create objects — use @StateObject when the view is responsible for creation.
  • Prefer @Binding over passing closures for simple two-way updates.
  • @EnvironmentObject is handy, but avoid overusing it for everything — explicit dependencies are easier to reason about.
  • Remember @Published notifies on set — if you mutate a property deeply (e.g., an array element), consider reassigning the published property or use methods that trigger changes.

I'm a passionate iOS Developer with over 8 years of experience building high-quality iOS apps using Objective-C, Swift, and SwiftUI. I created iostutor.com to share practical tips, tutorials, and insights for developers of all levels.

When I’m not coding, I enjoy exploring new technologies and writing content — from technical guides to stories and poems — with the hope that it might help or inspire someone, somewhere.

Leave a Reply

Your email address will not be published. Required fields are marked *