Chapters

Hide chapters

SwiftUI Cookbook

Live Edition · iOS 16.4 · Swift 5.8.1 · Xcode 14.3.1

Best Practices for State Management in SwiftUI
Written by Team Kodeco

Effective state management is key to building stable and responsive apps in SwiftUI. Below are some best practices for managing state in your SwiftUI apps:

  1. Use State and Binding for simple local state. State and Binding are perfect for managing simple state that is local to a view or can be passed from a parent view to a child view. Keep in mind that these property wrappers are designed to work with value types.
  2. Use ObservedObject and Published for complex state. When you have more complex state that can be shared across multiple views, consider using ObservedObject and Published in combination with a separate state management class.
  3. Use EnvironmentObject for shared state across unrelated views. If you need to share state across multiple views that aren’t directly related through a parent-child relationship, EnvironmentObject can be a good choice.
  4. Avoid large State variables. Storing large amounts of data in State variables can lead to performance issues, as SwiftUI recreates your view whenever state changes.
  5. Defer complex computation and side effects. Avoid running complex computations or side effects, like network requests, directly in your view structures.

Let’s illustrate these best practices with an example of a simple task manager app:

class TaskManager: ObservableObject {
  @Published var tasks = [String]()

  func addTask(_ task: String) {
    tasks.append(task)
  }
}

struct TaskListView: View {
  @EnvironmentObject var taskManager: TaskManager
  @State private var newTask = ""

  var body: some View {
    NavigationStack {
      VStack {
        TextField("New task", text: $newTask)
          .onSubmit {
            if !newTask.isEmpty {
              taskManager.addTask(newTask)
              newTask = ""
            }
          }
          .padding()
        List(taskManager.tasks, id: \.self) { task in
          Text(task)
        }
      }
      .navigationTitle("Task List")
    }
  }
}

struct ContentView: View {
  @StateObject var taskManager = TaskManager()
  
  var body: some View {
    TaskListView()
      .environmentObject(taskManager)
  }
}

Your preview should look like this:

This simple task manager illustrates several SwiftUI state management techniques.
This simple task manager illustrates several SwiftUI state management techniques.

In this example, you create a TaskManager class that manages a list of tasks. It’s an ObservableObject, which means that SwiftUI will watch for changes to its Published properties and update any views that depend on those properties. The TaskListView uses EnvironmentObject to access the shared TaskManager, and State for the newTask property that’s local to that view. When a new task is committed in the text field, the task is added to the task manager and the text field is cleared.

For more best practices, see the section on “SwiftUI Best Practices & Tips”.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.