개요
SwiftUI에서 상태와 데이터 흐름에 대해 알아보고 상황에 맞는 데이터 흐름 도구들의 사용법을 익혀보겠다.
데이터 흐름
body 안에서 값을 변경하려고 하면 self는 변할 수 없다는 오류가 발생한다.
구조체 연산 프로퍼티의 getter 기본 속성이 nonmutating이기 때문이며
명시적으로 mutating get을 적어줘도 뷰 프로토콜의 body는 get으로 선언되었기 때문에 뷰 프로토콜을 준수하지 않게 된다.
우선 SwiftUI의 데이터 흐름부터 보자.
사용자의 액션 또는 외부의 이벤트에 의해 데이터가 바뀌었을 때, SwiftUI는 자동으로 UI, 상태가 바뀌는 부분을 업데이트한다.
![](https://user-images.githubusercontent.com/79088896/154618329-ec76841d-af6b-4596-b657-3776ba4f4507.png)
데이터 흐름의 원칙
Data Independence
뷰는 데이터가 변경될 때마다 그 값을 반영해야하므로 데이터에 대한 의존성을 가진다.
데이터가 변경되어 뷰를 새로 그려줄 때 body 프로퍼티를 다시 호출하지만
처음부터 다시 그리는게 아니라 뷰 계층을 내려가면서 유효성 검사를 통해 변경된 부분만 다시 렌더링한다.
Single Source of Truth
SwiftUI는 데이터를 크게 Source of Truth와 Derived data로 구분한다.
Source of Truth는 그 자체가 본질적인 데이터이고 Derived data는 Source of Truth에서 부차적으로 파생된 것이다.
Single Source of Truth는 본질적인 데이터는 여러 곳으로 중복되지 않고 한 곳에서 다루어지고 수정되어야 한다는 것을 의미한다.
SwiftUI는 앱의 데이터를 UI에 연결 시키기 위한 도구들을 제공한다.
View State
뷰를 재사용성 있게 하기 위해서 뷰 계층의 특정 데이터를 캡슐화하는 도구
State
: 변할 수 있는 UI의 상태를 Locally하게 관리한다.Binding
: source of truth의 State값을 참조한다.
![](https://user-images.githubusercontent.com/79088896/154621154-3ef4cd4f-5cd2-4952-8c51-8368efc60541.jpeg)
Model Data
앱의 data model과 View들을 연결시켜주는 도구
ObservedObject
: ObservableObject 프로토콜을 준수한 model data를 참조한다.EnvironmentObject
: environment에 저장되어있는 관찰 가능한 객체(observable object)에 접근한다.StateObject
: 뷰에서 관찰 가능한 객체(observabel object)를 직접 인스턴스화한다.
![](https://user-images.githubusercontent.com/79088896/154621533-3ab9b3d1-523e-4ed2-94e1-8a18ea34e56a.jpeg)
@State
@State를 사용하면 저장 방식이 달라진다.
@State를 사용해서 래핑된 프로퍼티는 처음 선언된 공간에서 초기값을 그대로 유지하고 있는다.
만약 변경이 발생하면 SwiftUI에서 제공하는 저장소에 그 값을 전달하고 참조하는 형태로 동작하게 된다.
@State로 래핑된 프로퍼티는 뷰 자체에서 가져야 할 상태 프로퍼티이자 Source of Truth에 해당하는 데이터이다.
@State는 뷰 자신의 UI 상태를 저장하기 위한 데이터로 설계되었으므로 private으로 선언되는게 일반적이다.
간단하게 얘기해서 우리가 SwiftUI에게 “이 데이터가 변하면 뷰에 바로 반영시켜줘!” 라고 말하는거라고 생각하면 된다.
1 |
|
@Binding
데이터를 직접 저장하는 것이 아니라 바인딩된 데이터의 Source of Truth의 값을 읽고 쓸 수 있게 하는 프로퍼티 래퍼이다.
간단하게 데이터를 저장하고 있는 프로퍼티와 데이터를 보여주고 변경하는 뷰를 양방향으로 이어주는 것이다.
@State 프로퍼티에 $접두어를 붙이면 다른 뷰에서 @Binding 프로퍼티에서 값을 변경할 수 있다.
1 |
|
ObservableObject, @ObservedObject
To make the data changes in your model visible to SwiftUI, adopt the ObservableObject protocol for model classes.
@State가 뷰의 Source of Truth로 사용되었다면 뷰 외부의 뷰모델 혹은 모델이 가진 Source of Truth를 참조 타입으로 다루기 위한 것들이다.
SwiftUI에 모델의 데이터가 바뀌게하고 싶다면 모델 클래스에 ObservableObject 프로토콜을 채택해야한다.
1 |
|
ObservableObject는 프로토콜로 AnyObject를 채택하므로 구조체나 열거형에는 사용할 수 없다.
ObservableObject로 선언된 클래스는 objectWillChange를 사용할 수 있다. 이름으로 알 수 있듯이 send를 이용해서 바뀌는 프로퍼티를 알려준다.
이것을 일일이 안해주려면 @published로 선언하면 된다. 그러면 값이 바뀌면 자동으로 알려준다.
1 |
|
@StateObject
SwiftUI는 시도때도 없이 뷰를 재생성한다. 그래서 주어진 입력에 따라서 초기화 되는 것은 중요한데 뷰안에서 observed object를 만드는 것은 안전하지 않다.
그래서 인스턴스화를 시킬 수 있는 @StateObject를 제공한다.
StateObject는 observed object같이 행동한다. 다른점은 각각의 뷰 인스턴스에서 별개의 object를 생성한다.
1 |
|
@EnvironmentObject
@ObservedObject가 모델에 대한 직접적인 의존성을 만드는 것이라면, 이는 간접적인 의존성을 만드는데 사용한다.
간단하게 모든 뷰가 읽을 수 있는 data이다.
뷰 계층 간의 데이터를 바인딩 해주고 데이터를 넘겨주는 것을 하기 싫다면 이거 쓰면 된다.
1 |
|