← Back to Articles
SwiftUI State Management Swift iOS Development MVVM Mobile Development

SwiftUI State Management Complete Guide: @State, @Binding, @StateObject, @ObservedObject, @Environment, and @Observable

Learn SwiftUI state management from beginner to advanced concepts. Understand @State, @Binding, @StateObject, @ObservedObject, @Environment, ObservableObject, @Published, and the modern Observation framework for building scalable iOS applications.

July 2026
40 min read
Written by Engineering Team

Introduction

State management is one of the most important concepts in SwiftUI development. Unlike traditional UIKit applications where developers manually update views, SwiftUI uses a declarative approach where the interface automatically changes when data changes.

Understanding how data flows through SwiftUI is essential for building reliable applications. Incorrect state management can lead to unexpected UI updates, duplicated data, memory issues, and difficult-to-maintain code.

SwiftUI provides multiple property wrappers for managing different types of state. The correct choice depends on ownership, lifecycle, sharing requirements, and application architecture.

SwiftUI state management data flow
SwiftUI automatically updates views when state changes.

How SwiftUI State Works

SwiftUI follows a declarative UI model. Instead of telling the framework how to update each component, developers describe what the UI should look like for a specific state.

When state changes, SwiftUI recalculates the affected views and updates only the required parts of the interface.

SimpleStateExample.swift
swift
              struct CounterView: View {


    @State
    private var count = 0



    var body: some View {


        VStack {


            Text(
                "Count: \(count)"
            )



            Button("Increase") {


                count += 1


            }


        }

    }

}

            

What Is @State?

@State is the simplest SwiftUI property wrapper for storing local view state. SwiftUI manages the storage automatically and recreates the view whenever the value changes.

@State should be used when a view owns its own private data and no other component needs direct access to it.

  • Local view state

  • Boolean values

  • Text fields

  • Selections

  • Animations

  • Small temporary values

@State Example

ToggleExample.swift
swift
              struct SettingsView:
    View {


    @State
    private var enabled =
        false



    var body: some View {


        Toggle(
            "Notifications",
            isOn:
            $enabled
        )


    }

}

            

Understanding View Identity

SwiftUI views are value types and can be recreated frequently. The @State property wrapper allows SwiftUI to preserve specific values between these recreations.

Without @State, changing a normal property inside a View would not trigger UI updates.

What Is @Binding?

@Binding allows one view to access and modify state owned by another view.

It creates a two-way connection between a parent view and a child view without transferring ownership of the data.

BindingExample.swift
swift
              struct ParentView:
    View {


    @State
    private var username = ""



    var body:
    some View {


        UsernameField(
            username:
            $username
        )


    }

}



struct UsernameField:
    View {


    @Binding
    var username:
    String



    var body:
    some View {


        TextField(
            "Name",
            text:
            $username
        )

    }

}

            

@State vs @Binding

@State @Binding
Owns data References existing data
Creates storage Shares storage
Used by parent Used by child
Private state Two-way communication

ObservableObject and @Published

For larger applications, state usually belongs inside dedicated objects called ViewModels. SwiftUI uses ObservableObject to observe these objects.

@Published marks properties that should notify SwiftUI when their values change.

UserViewModel.swift
swift
              class UserViewModel:
    ObservableObject {


    @Published
    var username = ""



    @Published
    var isLoading =
        false


}

            

@StateObject

@StateObject is used when a SwiftUI view creates and owns an ObservableObject instance.

SwiftUI keeps the object alive during view updates and manages its lifecycle.

StateObjectExample.swift
swift
              struct ProfileView:
    View {


    @StateObject
    private var viewModel =
        ProfileViewModel()



    var body:
    some View {


        Text(
            viewModel.name
        )

    }

}

            

@ObservedObject

@ObservedObject is used when a view receives an existing ObservableObject from another part of the application. The view observes changes but does not own the object lifecycle.

Unlike @StateObject, SwiftUI does not create or preserve the object. The ownership belongs to another component.

ObservedObjectExample.swift
swift
              struct ProfileScreen:
    View {


    @ObservedObject
    var viewModel:
    ProfileViewModel



    var body:
    some View {


        VStack {


            Text(
                viewModel.name
            )


        }

    }

}

            

@StateObject vs @ObservedObject

@StateObject @ObservedObject
Creates the object Receives the object
Owns lifecycle Does not own lifecycle
Used by parent/root views Used by child views
Initializes ViewModel Observes ViewModel

💡 Rule of Thumb

If the View creates the ObservableObject, use @StateObject. If the View receives it from somewhere else, use @ObservedObject.

@EnvironmentObject

@EnvironmentObject allows shared objects to be injected into the SwiftUI environment and accessed by many views without manually passing them through every screen.

It is useful for global application state such as authentication, settings, themes, or user preferences.

EnvironmentObjectExample.swift
swift
              class AppSettings:
    ObservableObject {


    @Published
    var darkMode =
        false


}



@main

struct MyApp:
    App {


    @StateObject
    private var settings =
        AppSettings()



    var body:
    some Scene {


        WindowGroup {


            ContentView()

            .environmentObject(
                settings
            )


        }

    }

}

            

Using EnvironmentObject in Views

SettingsView.swift
swift
              struct SettingsView:
    View {


    @EnvironmentObject
    var settings:
    AppSettings



    var body:
    some View {


        Toggle(

            "Dark Mode",

            isOn:
            $settings.darkMode

        )


    }

}

            

The Modern Observation Framework (@Observable)

Starting with iOS 17, Apple introduced the Observation framework as a modern replacement for many ObservableObject patterns.

The @Observable macro removes the need for @Published and provides automatic observation tracking.

ObservableExample.swift
swift
              import Observation



@Observable

class UserStore {


    var username =
        ""


    var isLoggedIn =
        false


}

            

@Observable vs ObservableObject

ObservableObject @Observable
Combine based Observation framework
@Published required Automatic tracking
Older SwiftUI pattern Modern iOS 17+ approach
Uses objectWillChange More efficient tracking

Using @Observable with SwiftUI

ObservableView.swift
swift
              struct ContentView:
    View {


    @State
    private var store =
    UserStore()



    var body:
    some View {


        Text(
            store.username
        )


    }

}

            

Understanding State Ownership

The most important SwiftUI state management concept is understanding who owns the data. Every piece of state should have a clear owner.

Situation Recommended Property
Small local value @State
Child modifies parent value @Binding
View creates ViewModel @StateObject
View receives ViewModel @ObservedObject
Global shared state @EnvironmentObject
iOS 17+ models @Observable

MVVM State Management in SwiftUI

The MVVM pattern is one of the most common architectures used with SwiftUI. The View displays state while the ViewModel manages business logic.

MVVMExample.swift
swift
              class ProductsViewModel:
    ObservableObject {


    @Published
    var products:
    [Product] = []



    func loadProducts() {


        // API call


    }

}



struct ProductsView:
    View {


    @StateObject
    var viewModel =
    ProductsViewModel()



    var body:
    some View {


        List(
            viewModel.products
        ) {

            product in

            Text(
                product.name
            )

        }

    }

}

            

State Management with Combine

Combine is deeply connected with SwiftUI state management. ObservableObject and @Published use Combine publishers internally to notify views about changes.

CombineStateExample.swift
swift
              @Published
var searchText = ""



$query

.debounce(
    for:
    .milliseconds(500),
    scheduler:
    DispatchQueue.main
)

.sink {

    value in

    search(value)

}

            

Form State Management

SwiftUI forms are a common place where state management becomes important. Text fields, toggles, validation messages, and submit actions all depend on state.

FormExample.swift
swift
              struct LoginView:
    View {


    @State
    private var email =
    ""


    @State
    private var password =
    ""



    var body:
    some View {


        Form {


            TextField(
                "Email",
                text:
                $email
            )


            SecureField(
                "Password",
                text:
                $password
            )


        }

    }

}

            

Navigation State Management

Modern SwiftUI navigation also depends on state. NavigationStack allows developers to control navigation using data-driven paths.

NavigationState.swift
swift
              @State
private var path =
NavigationPath()



NavigationStack(
    path:
    $path
) {


    HomeView()


}

            

State Restoration in SwiftUI

Applications often need to remember user progress after termination or when the system recreates views. SwiftUI provides several tools for preserving state.

Property Wrapper Purpose
@State Temporary view state
@SceneStorage Restore scene-specific values
@AppStorage Persist UserDefaults values

@AppStorage for Persistent State

@AppStorage connects SwiftUI state directly with UserDefaults. It is useful for simple settings and preferences.

AppStorageExample.swift
swift
              struct SettingsView:
    View {


    @AppStorage(
        "darkMode"
    )

    private var darkMode =
    false



    var body:
    some View {


        Toggle(

            "Dark Mode",

            isOn:
            $darkMode

        )

    }

}

            

@SceneStorage for Scene State

@SceneStorage stores state related to a specific scene. It is commonly used for restoring navigation or user interface state.

SceneStorageExample.swift
swift
              struct SearchView:
    View {


    @SceneStorage(
        "searchText"
    )

    private var searchText =
    ""



    var body:
    some View {


        TextField(

            "Search",

            text:
            $searchText

        )

    }

}

            

Dependency Injection and State Management

Large SwiftUI applications should avoid creating dependencies directly inside views. Dependency injection improves testing and keeps state management clean.

DependencyInjection.swift
swift
              protocol UserService {


    func users()
    async throws
    -> [User]


}



class UserViewModel:
    ObservableObject {


    private let service:
    UserService



    init(
        service:
        UserService
    ) {

        self.service =
        service

    }

}

            

State Management with Services

A common production pattern is keeping application-wide state inside dedicated services and exposing only required data through ViewModels.

SessionManager.swift
swift
              @Observable

class SessionManager {


    var currentUser:
    User?


    var isAuthenticated:
    Bool {

        currentUser != nil

    }

}

            

SwiftUI State Management Architecture

SwiftUI state management architecture
Recommended state flow in a scalable SwiftUI application.
Architecture Flow
text
              User Action


     ↓


SwiftUI View


     ↓


ViewModel


     ↓


Use Case / Service


     ↓


Repository


     ↓


Data Source

            

Common SwiftUI State Management Mistakes

  • Using @State for complex application data

  • Creating ViewModels inside body

  • Using multiple sources of truth

  • Passing too much data through views

  • Using EnvironmentObject everywhere

  • Ignoring ownership rules

  • Mixing UI and business logic

Creating Multiple Sources of Truth

One of the biggest SwiftUI architecture problems is storing the same information in multiple places.

When two different objects control the same value, they can easily become inconsistent and create unpredictable UI behavior.

💡 Best Practice

Each important piece of application data should have a single owner responsible for updating it.

SwiftUI State Performance Optimization

Good state management improves application performance because SwiftUI can efficiently determine which views need to be updated.

  • Keep state as local as possible

  • Avoid unnecessary object updates

  • Split large views into smaller components

  • Use Equatable views when needed

  • Avoid storing derived values

Derived State

Derived state is calculated from existing state instead of being stored separately.

DerivedState.swift
swift
              struct CartView:
    View {


    let products:
    [Product]



    var total:
    Double {


        products
            .reduce(0) {

                result,
                product in

                result +
                product.price

            }

    }

}

            

Testing SwiftUI State

State-driven architecture makes SwiftUI applications easier to test because behavior can be verified by changing state and checking output.

  • Test initial state

  • Test loading state

  • Test success state

  • Test error handling

  • Test user actions

Modern SwiftUI State Management Recommendation

Scenario Recommended Solution
Local UI value @State
Child modification @Binding
View-owned model @StateObject
External model @ObservedObject
Shared application data @EnvironmentObject
New iOS 17+ apps @Observable

Frequently Asked Questions

Should I use ObservableObject or @Observable?
For new applications targeting iOS 17 and newer, @Observable is usually preferred. ObservableObject remains widely used and is still supported.
What is the difference between @State and @StateObject?
@State stores simple local values while @StateObject manages the lifecycle of an ObservableObject created by the view.
When should I use EnvironmentObject?
EnvironmentObject is useful for shared application state such as authentication, themes, and settings, but should not replace normal dependency injection.
Is Combine required for SwiftUI?
No. Modern SwiftUI can use async/await and Observation. However, Combine remains important for reactive streams and existing applications.
Should ViewModels contain all application state?
No. ViewModels should manage screen-related state. Shared business state should usually live in services, repositories, or dedicated models.

Conclusion

SwiftUI state management is the foundation of modern iOS development. Understanding ownership, data flow, and property wrappers such as @State, @Binding, @StateObject, @ObservedObject, @EnvironmentObject, and @Observable allows developers to build scalable applications with predictable behavior. Combined with MVVM, Combine, async/await, and dependency injection, SwiftUI provides a powerful architecture for professional iOS applications.

We use cookies to improve your experience.