[SwiftUI] @ObservedObject vs @StateObject

"@ObservedObject vs @StateObject"

Posted by JacksonJang on August 5, 2024

핵심 정리

  • 공통점 : 객체의 상태 변화를 감지하고 뷰를 업데이트하는 데 사용되는 속성 래퍼입니다.
  • @StateObject는 부모 뷰가 직접 객체를 소유하고 관리합니다.
  • @ObservedObject는 부모 뷰에서 객체를 관찰하고 업데이트를 반영합니다.

ObservableObject 란?

@StateObject@ObservedObject에 대해 알기 전에 우선적으로 ObservableObject 라는 프로토콜에 대해 이해할 필요가 있습니다.

1
2
3
4
5
6
7
8
9
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public protocol ObservableObject : AnyObject {

    /// The type of publisher that emits before the object has changed.
    associatedtype ObjectWillChangePublisher : Publisher = ObservableObjectPublisher where Self.ObjectWillChangePublisher.Failure == Never

    /// A publisher that emits before the object has changed.
    var objectWillChange: Self.ObjectWillChangePublisher { get }
}

ObservableObject 는 AnyObject를 상속받은 프로토콜 입니다.
주로 상태 관리, 데이터 바인딩 역할을 하는 Publisher를 포함한 객체입니다.
그리고 여기서 상태 관리, 데이터 바인딩 에 대한 관리는 @StateObject, @ObservedObject를 사용해서 가능합니다.

예시를 진행하기 위해 간단한 모델을 생성하곘습니다

1
2
3
4
5
struct CustomModel: Identifiable {
    let id: String
    let name: String
    let age: Int
}

@StateObject

부모 뷰에서 해당 객체를 소유하고 관리합니다.
즉, 뷰가 생성될 때 객체도 생성되며, 뷰가 사라질 때 객체도 같이 소멸되며 부모 뷰의 생명주기와 함께 유지됩니다.

@StateObject 예시코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 뷰 모델
class ContentViewModel: ObservableObject {
    @Published var items: [CustomModel] = [
        CustomModel(id: UUID().uuidString, name: "JANG", age: 23),
        CustomModel(id: UUID().uuidString, name: "HYO", age: 29)
    ]
    
    func add() {
        items.append(CustomModel(id: UUID().uuidString, name: "ADD", age: Int.random(in: 0...100)))
    }
}

struct ContentView: View {
    @StateObject private var viewModel = ContentViewModel()
    
    var body: some View {
        VStack {
            Text("Total length : \(viewModel.items.count)")
            Button("ADD") {
                viewModel.add()
            }
        }
        .padding()
    }
}

@ObservedObject

부모 뷰에서 전달된 ObservableObject를 자식 뷰에서 감지할 때 사용합니다.
부모 뷰에서 자식 뷰에게 @StateObject를 전달하면, 자식 뷰에서는 @ObservedObject를 이용해서 참조하게 됩니다.

@ObservedObject 예시코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 뷰 모델
class ContentViewModel: ObservableObject {
    @Published var items: [CustomModel] = [
        CustomModel(id: UUID().uuidString, name: "JANG", age: 23),
        CustomModel(id: UUID().uuidString, name: "HYO", age: 29)
    ]
    
    func add() {
        items.append(CustomModel(id: UUID().uuidString, name: "ADD", age: Int.random(in: 0...100)))
    }
}

struct ContentView: View {
    @StateObject private var viewModel = ContentViewModel()
    
    var body: some View {
        VStack {
            Text("Total length : \(viewModel.items.count)")
            ChildView(viewModel: viewModel)
            Button("ADD") {
                viewModel.add()
            }
        }
        .padding()
    }
}

struct ChildView: View {
    @ObservedObject var viewModel: ContentViewModel
    
    var body: some View {
        List(viewModel.items) { item in
            Text("Name : \(item.name), Age : \(item.age)")
        }
    }
}

#Preview {
    ChildView(viewModel: ContentViewModel())
}

github 예시 파일

https://github.com/JacksonJang/ObservedVsState