@StateObject vs @ObservedObject

Ujjwal chafle
3 min readFeb 3, 2025

--

In SwiftUI, @StateObject and @ObservedObject are both used for managing reference-type data in a view, but they have different use cases.

@StateObject

  • Used when creating a new instance of an ObservableObject inside a view.
  • The view owns the object, meaning SwiftUI manages its lifecycle.
  • It is retained when the view is recreated, preventing the object from being reset.

class ViewModel: ObservableObject {
@Published var count = 0
}

struct MyView: View {
@StateObject private var viewModel = ViewModel() // Owned by this view

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

Use @StateObject when a view is responsible for initializing and owning an observable object.

@ObservedObject

  • Used when passing an existing ObservableObject instance into a view.
  • The view does not own the object but just observes it.
  • If the parent view recreates the view, the @ObservedObject may get reset.

struct ChildView: View {
@ObservedObject var viewModel: ViewModel // Reference from parent

var body: some View {
Text("Count: \(viewModel.count)")
}
}

struct ParentView: View {
@StateObject private var viewModel = ViewModel() // Owned by the parent

var body: some View {
ChildView(viewModel: viewModel) // Passing down
}
}

Use @ObservedObject when a view receives an observable object from elsewhere, but does not own it.

🆚 Key Differences

🔥 Common Mistake: Using @ObservedObject instead of @StateObject

If you mistakenly use @ObservedObject when initializing an object inside a view, SwiftUI may recreate it every time the view refreshes, leading to unexpected resets.


struct MyView: View {
@ObservedObject var viewModel = ViewModel() // ❌ Wrong! Should be @StateObject

var body: some View {
Text("Count: \(viewModel.count)")
}
}

Corrected:


struct MyView: View {
@StateObject private var viewModel = ViewModel() // ✅ Correct

var body: some View {
Text("Count: \(viewModel.count)")
}
}

🎯 Rule of Thumb

  • Use @StateObject when creating an observable object inside the view.
  • Use @ObservedObject when receiving an observable object from another view/wireframe (or coordinator).

🎯 Best Practice for Wireframe/Coordinator-Managed ViewModels:

📱 Real-World Example: Task Manager App (Using @StateObject & @ObservedObject)

Let’s build a simple Task Manager app where:

  • The MainView owns a TaskViewModel (so it uses @StateObject).
  • The TaskListView receives the same TaskViewModel and observes it (so it uses @ObservedObject).
  • Users can add tasks, and the list updates in real-time.

🏗 Step 1: Create the TaskViewModel

This class will store a list of tasks and allow adding new ones.

import SwiftUI

class TaskViewModel: ObservableObject {
@Published var tasks: [String] = []

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

📜 Step 2: Create the TaskListView (Uses @ObservedObject)

This view will display the list of tasks and observe changes from TaskViewModel.

struct TaskListView: View {
@ObservedObject var viewModel: TaskViewModel // Receiving an existing object

var body: some View {
List(viewModel.tasks, id: \\.self) { task in
Text(task)
}
}
}

🏠 Step 3: Create the MainView (Uses @StateObject)

This view owns the TaskViewModel and passes it down to TaskListView.

struct MainView: View {
@StateObject private var viewModel = TaskViewModel() // Owned by this view

@State private var newTask = ""

var body: some View {
VStack {
TextField("Enter task", text: $newTask)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()

Button("Add Task") {
viewModel.addTask(newTask)
newTask = "" // Clear input field
}
.padding()

// Pass viewModel to the TaskListView
TaskListView(viewModel: viewModel)
}
}
}

🎯 How It Works?

MainView uses @StateObject because it creates and owns the TaskViewModel.

TaskListView uses @ObservedObject because it receives the TaskViewModel from MainView but does not own it.

🏆 Final Result

  • Users type a task into the TextField and tap "Add Task."
  • The task is stored in TaskViewModel and displayed in the list.
  • Because TaskListView observes TaskViewModel, it updates automatically when new tasks are added.

🚀 This ensures data persistence and correct ownership of the observable object.

--

--

Ujjwal chafle
Ujjwal chafle

Written by Ujjwal chafle

🚀iOS Developer👨🏻‍💻. Here to share my learnings and learn from the community🤝.

No responses yet