[SwiftUI] Making Classes Observable
*이 글을 애플 공식문서를 번역한 글입니다.
Working with Reference Types
우리는 프로퍼티의 SOT를 식별하기 위해 @State를 썼다. 하지만 @State wrapper는 오직 value type(값 타입)에서만 적용된다. 값 타입은 Struct, Enum(구조체, 열거형)을 말한다. 그래서 SwiftUI는 SOT를 참조타입으로 선언하기 위한 프로퍼티 wrapper를 제공한다 : @ObservedObject, @StateObject, @EnvironmentObject
이 wrapper들을 사용하기 위해서는, 우리의 class를 observable하게 만들어줘야 한다
Making a Class Observable
ObservableObject 프로토콜을 통해 class를 observable하게 만든다. 클래스의 프로퍼티 중 변화를 바로 감지해야 하는 것들 (변화를 감지하여 UI를 바로 바로 그려줘야 하는 것들)은 속성 앞에 @Published를 붙여준다.
class ScrumTimer: ObservableObject {
@Published var activeSpeaker = ""
@Published var secondsElapsed = 0
@Published var secondsRemaining = 0
// ...
}
ScrumTimer란 클래스는 여러 published 프로퍼티를 선언했고, 이 변화들을 관찰하며 감지한다.
Monitoring an Object for Changes
이 클래스들을 뷰에서 선언하기 위해서 @StateObject, @ObservedObject, @EnvironmentObject wrapper들을 사용한다. 이를 사용하면 우리의 뷰 내에서 새로운 SOT를 생성한다
@StateObject wrapper를 사용하면, 뷰 안에서 관찰되는 객체를 생성한다. 뷰가 생성될 때 객체를 초기화 및 생성하고, 객체가 이 뷰 안에서 또는 넘겨주는 다른 뷰에서 사용할 수 있게 유지한다.
struct MeetingView: View {
@StateObject var scrumTimer = ScrumTimer()
// ...
}
다른 뷰로부터 객체를 전달할 때는 @ObservedObject wrapper를 사용하면 된다. 왜냐하면 이미 다른 뷰에서 객체를 생성했고, 이 객체에 대한 초기값을 제공할 필요가 없기 때문이다.
struct ChildView: View {
@ObservedObject var timer: ScrumTimer
// ...
}
그냥 관찰되는 객체의 인스턴스만 전달해주면 된다.
struct MeetingView: View {
@StateObject var scrumTimer = ScrumTimer()
var body: some View {
VStack {
ChildView(timer: scrumTimer)
}
}
// ...
}
각각의 뷰에 객체를 전달해주는 것 대신, environment에 객체를 위치시키면 된다. environmentObject 뷰 modifier는 객체를 부모 뷰의 environment에 위치시킨다.
struct ParentView: View {
@StateObject var scrumTimer = ScrumTimer()
var body: some View {
VStack {
ChildView()
.environmentObject(scrumTimer)
}
}
// ...
}
자식 뷰에서 scrumTimer를 받기 위해서는 @EnvironmentObject wrapper를 사용하면 된다. 그리고 다른 뷰에서 이 객체에 대한 참조가 없을 지라도 이 객체에 접근할 수 있는데, SwiftUI는 오직 데이터들의 의존성만 추적하기 때문이다.
struct ChildView: View {
@EnvironmentObject var timer: ScrumTimer
// ...
}