iOS/SwiftUI

[SwiftUI] Making Classes Observable

itisjustK 2022. 5. 11. 12:03

*이 글을 애플 공식문서를 번역한 글입니다.

 

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
   // ...
}