@Observable Macro
@Observable 매크로는 ObservableObject 및 해당 @Published 매개변수를 대체하기 위해 WWDC 2023에서 처음 도입되었습니다.
매크로를 사용하면 게시된 모든 속성을 제거하는 동시에 변경이 자동으로 발생할 때 View를 다시 그릴 수 있습니다.
이전 방식 ObservableObject | @Observable Macro |
@State, @Binding, @Environment | 변화 없음 |
ObservableObject 프로토콜 | @Observable 매크로로 대체 |
@Published, @ObservedObject | 삭제 |
@StateObject, @EnvironmentObject | @State, @Environment로 대체 |
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
ObservableObject를 @Observable Macro로 변경하기
이전 예제에서 ObservableObject 프로토콜을 채택했던 클래스에서 프로토콜을 삭제하고 매크로를 사용해줍니다.
그리고 뷰에 사용될 변수를 표시해주기 위한 이 Published와 ObservableObject를 생성하는 프로퍼티 래퍼를 삭제해줍니다.
이전에는 View에서 사용될 변수를 SwiftUI에 알려주기 위해 Published 프로퍼티 래퍼를 사용했습니다. 그렇게 알려준 변수가 변경이 된다면 View에서 사용이 안 되도 무조건 View를 다시 그렸습니다.
@Observable Macro는 클래스 내부의 변수가 View에서 어떤 것이 사용될지 표기해줄 필요가 없고 자동으로 View의 body에 사용됨을 감지해줍니다. 따라서 View에서 사용되는 변수가 변경될 때만 View를 다시 그려줍니다.
@Observable class NumberCounter {
var number: Int = 0 // @Published 삭제
// ...
}
struct ContentView: View {
var numberCounter = NumberCounter() // @ObservedObject 삭제
var body: some View {
VStack {
// ...
}
}
}
@StateObject를 @State로 변경하기
이전에 @StateObject로 사용되던 부분을 @State로 변경해줍니다.
@Observable class NumberCounter {
var number: Int = 0 // @Published 삭제
// ...
}
struct ContentView: View {
@State var numberCounter = NumberCounter() // @State로 변경
var body: some View {
VStack {
// ...
}
}
}
@EnvironmentObject를 @Environment로 변경하기
이전에 @EnvironmentObject로 사용되던 부분을 @Environment로 변경해줍니다.
하위 뷰에 .environmentObject로 전달해주던 부분도 .environment로 변경해줍니다.
@Observable class NumberCounter {
var number: Int = 0 // @Published 삭제
// ...
}
// 상위 뷰
struct ContentView: View {
var numberCounter = NumberCounter()
var body: some View {
VStack {
SubView()
.environment(numberCounter)
}
}
}
Environment는 Environment에 저장된 변수를 Type으로 꺼내기도 하고 저장하기도 하기 때문에 아래와 같은 형태로 구현해줍니다.
// 하위 뷰
struct SubView: View {
@Environment(NumberCounter.self) var numberCounter
var body: some View {
VStack {
// ...
}
}
}
@Bindable
@Bindable는 이전 방식에서는 필요 없었지만 매크로가 도입되면서 새로 필요해진 프로퍼티 래퍼입니다.
ObservableObject 프로토콜을 Observable 매크로로 바꾸게 되면 @ObservedObject 프로퍼티 래퍼를 삭제하게 됩니다.
하지만 여기서 에러가 발생합니다. $기호를 통해 바인딩 타입으로 전달해주던 코드에서 Binding Type을 찾을 수 없기 때문입니다.
Binding Type을 전달해주기 위해 이를 지원해주는 @Bindable을 사용할 수 있습니다.
Binding Type을 전달해준다는 것은 이 변수가 가진 원래 주소의 정보도 함께 전달해 준다는 것을 의미합니다. 원래 주소를 전달해 주면 그 변수에 접근해서 값을 수정을 할 수 있습니다.
@Observable class Book: Identifiable {
var title = "Sample Book Title"
var isAvailable = true
}
struct BookEditView: View {
@Bindable var book: Book
@Environment(\.dismiss) private var dismiss
var body: some View {
Form {
TextField("Title", text: $book.title)
Toggle("Book is available", isOn: $book.isAvailable)
Button("Close") {
dismiss()
}
}
}
}
@Bindable과 동일한 동작을 하는 @State 프로퍼티 래퍼가 있습니다.
@State var book: Book
@Bindable과 @State 중에 어떤 걸 사용해야 할까?
- @ObservableObject → @Bindable
- @StateObject → @State