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.

Hi, I’m Mumthasir, the creator of iOSTutor.com.

With over 11 years of experience in software development — including 8 years focused on iOS application development — I’ve had the opportunity to build and contribute to a wide range of applications, from e-commerce and education to healthcare and government platforms.

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 *