본문 바로가기

iOS/SwiftUI

SwiftUI @Bindable VS @State 차이점 (iOS17+ Observable Macro)

 

 🔍 서론

 

iOS17 부터 변경된 Observable Macro에 대해 공부하면서 헷갈리면서 이해가 안 되는 부분이 있었다. 바로 @Bindable과 @State의 차이이다. 여기서의 @State는 iOS16 이하에 쓰였던 @State를 말하는 것이 아니라, iOS17에 @StateObject를 대신하여 변경된 @State를 말하는 것이다. 

 

물론 @State가 다른 기능(@StateObject)과 통합된 것이므로 이전 버전과 구분 짓는 게 의미가 없다. 그냥 @State가 새로운 역할을 하나 더 수행하는 것일 뿐이다.

 

따라서, 이제는 @State를 값 타입뿐만 아니라 참조 타입에도 사용이 가능하다. 여기서 말하는 참조 타입은 @Observable의 참조 타입이다.

 

아무튼 본격적으로 @Bindable과 @State의 차이점과 헷갈리는 부분에 대해 정리해 보겠다.

 

 

 

 

 📱 @Bindable vs @State

먼저 두 개가 각각 언제 쓰이는지 정리해 보겠다.

 

@State

  1. 과거의 @StateObject를 대신하여 사용

@Bindable

  1. 과거의 @StateObject로 선언된 객체 (현재는 @StateObject 대신 @State)를 하위 뷰에서 받을 때 과거의 @ObservedObject를 대신하여 사용
  2. '$' 키워드를 사용하는 Projected Value는 필요하지만, 인스턴스 보존이 필요 없을 때(@StateObject의 기능이 필요 없는 경우) @Observable 객체를 생성자로 생성하는 라인에서 과거의 @ObservedObject를 대신하여 사용

 

그런데 문제는 @Bindable의 1번 경우에 있다. @Bindable과 @State가 헷갈리는 주된 이유는 1번 경우의 @Bindable 자리에 @State를 써도 문제가 발생하지 않는다는 것이다. 아래 코드를 봐보자.

 

@Observable 
class ViewModel {
    var text = ""
}

struct ParentView: View {
    @State var viewModel = ViewModel()

    var body: some View {
        VStack {
            ChildView(viewModel: viewModel)

            Text("입력값: \(viewModel.text)")
        }
    }
}

struct ChildView: View {
    @State var viewModel: ViewModel

    var body: some View {
        TextField("입력하세요", text: $viewModel.text)
    }
}

 

여기서 상위 뷰의 @State로 선언된 객체를 하위 뷰에서 @Bindable이 아닌 @State로 받고 있지만, 동작에 있어서 아무 문제가 발생하지 않는다.

 

그렇지만, 상위 뷰에서 ViewModel을 생성할 때, @State로 인해 인스턴스가 뷰와 별개의 메모리에 등록되므로, 하위 뷰에서는 상위 뷰의 객체를 그냥 바인딩만 시켜주면 될 일이다. 

 

따라서 위의 경우는 아래와 같이 수정하는 게 맞을 것이다.

 

@Observable 
class ViewModel {
    var text = ""
}

struct ParentView: View {
    @State var viewModel = ViewModel()

    var body: some View {
        VStack {
            ChildView(viewModel: viewModel)

            Text("입력값: \(viewModel.text)")
        }
    }
}

struct ChildView: View {
    @Bindable var viewModel: ViewModel // @State -> @Bindable

    var body: some View {
        TextField("입력하세요", text: $viewModel.text)
    }
}

 

 

따라서, 이 경우만 구분을 잘해주면 더 이상 @Bindable과 @State는 헷갈리지 않을 것이다.

 

 

 

 

 

 

참고 자료

 

https://developer.apple.com/documentation/swiftui/migrating-from-the-observable-object-protocol-to-the-observable-macro

 

Migrating from the Observable Object protocol to the Observable macro | Apple Developer Documentation

Update your existing app to leverage the benefits of Observation in Swift.

developer.apple.com

 

 

https://stackoverflow.com/questions/77994209/in-swiftui-why-should-i-use-bindable-if-state-works-just-fine-in-its-place

 

In SwiftUI, why should I use @Bindable if @State works just fine in its place?

Please help me understand the difference between @Bindable and @State. If I take the following example from the Xcode Documentation and change @Bindable with @State like this: @State var book: Book...

stackoverflow.com