SwiftUI State and Data Flow
(공식문서 링크)
https://developer.apple.com/documentation/swiftui/state-and-data-flow
Apple Developer Documentation
developer.apple.com
Overview

- SwiftUI는 user interface 디자인을 선언적으로 제공하고 있음. (선언형 프로그래밍)
- 따라서 View를 계층적으로 구성할 때 의존성도 View의 계층 구조에 맞춰서 나타남.
- 외부 이벤트 혹은 user 액션에 의해 데이터의 변했을 때, SwiftUI에 영향을 받는 부분을 자동으로 업데이트.
- 값이 달라지지 않으면 렌더링 안하는데, 달라지지 않아도 POD 규칙에 의해서 렌더링 할 수도 있음.
- 결과적으로 이 프레임워크(SwiftUI)는 전통적으로 ViewController에서 하는 대부분의 일을 수행
UIKit으로 코드 작성하던거 SwiftUI로도 대부분 작성할 수 있다는 의미
- SwiftUI에는 @State 및 @Binding을 제공하여 user interface에 연결
- 이러한 것들은 앱의 모든 데이터에 대한 단일 소스를 유지하는데 도움을 주며, 작성해야 하는 로직의 양을 줄여줌
- 즉, 명령형은 데이터 바뀌고 레이아웃 업데이트 하라고 명령해야 하는데 그런게 없어진다는 의미

SwiftUI의 LifeCycle
- SwiftUI에는 View의 상태를 나타내는 함수가 단 2개 존재
- onAppear, onDisappear
- 대신에 상태를 나타내는 @State 등 다양한 프로퍼티 래퍼가 존재해 데이터 흐름에 대한 여러 상태를 대응할 수 있음.
.onAppear {
print("ViewAppeared") // viewDidAppear
}
.onDisappear {
print("ViewDisappeared")
}
@State
- struct는 value type이라서 값을 변경할 수 없으나, SwiftUI에서 @State를 제공해 strcut내의 값을 변경할 수 있음.
- mutating과 비슷.
- SwiftUI에서 일반적으로 View를 struct로 생성하는데 값을 저장해야 하는 경우에는 class로도 생성할 수 있음.
- 이 경우는 UIRepesentView로 사용
- 일반적으로 @State는 private으로 선언되고, 다른 View와 공유하지 않음.
- 공유 가능한 상태가 있으면 Store를 외부로 분리하고 거기서 관찰하는 구조가 더 나은 설계
- View와 공유하고 싶다면, @StateObject나 @ObservedObject를 사용하기
@Binding
- @Binding은 부모 View의 @State와 같은 같을 양방향으로 연결되게 도움.
- 양방향이라서 하위에서도 부모의 @State를 변경할 수 있음.
- (개인의견) 이러면 Action을 단방향으로 흘려서 관리하는게 아니라 상태 관리가 복잡해질 거 같음.
- @Binding의 경우에는 init을 통해서 사용하는게 개인적으로 깔끔한데,
- init에서 사용할 시 .constant()로 초기값을 지정할 수 있습니다.
샘플코드
샘플 코드는 Combine을 몰라도 간단히 사용할 수 있음.
최근에 SwiftUI와 MVVM에 대한 논의가 뜨거운데 이 부분이 다른 주제(AI, 블록체인 등)에 비해서 크게 논쟁할 일인지는 잘 모르겠음.
코드 스타일의 변화는 있을 수 있고 팀 내에서 서비스나 상황에 맞게 코드를 작성하면 되고 진짜 핵심인 AI나 블록체인 등에 기술에 대해서 더 학습하고 적용하는 부분에 더 많은 관심이 쏠렸으면 함.
struct ContentView: View {
@State private var showAddView = false
var body: some View {
VStack {
Text("Hello World.")
}
}
.sheet(isPresented: $showAddView) {
AddView(isPresented: self.$showAddView)
}
}
struct AddView: View {
@Binding var isPresented: Bool
var body: some View {
Button("Dismiss") {
self.isPresented = false
}
}
}
ObservableObject
Property Wrapper가 아닌 protocol임.
요즘 `@` 표시만 보이면 자꾸 헷갈려서 안에 한번 더 까고 들어가서 보는거 같음.

- ObservableObejct는 Protocol이며, Combine 프레임워크의 일부
- 이를 사용하기 위해서는 프로토콜을 준수하고, @Published를 사용하여 상태를 관찰 가능하게 함.
- @Published를 사용하면 변수의 값이 추가되거나 삭제되었다는 것을 view가 알 수 있게 해줌.
- ObservableObject protocol은 ObservedObeject property wrapper를 이용하여 외부 참조 모델에 연결
샘플 코드 1 (애플 공식문서)
class Contact: ObservableObject {
@Published var name: String
@Published var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
func haveBirthday() -> Int {
age += 1
return age
}
}
let john = Contact(name: "John Appleseed", age: 24)
cancellable = john.objectWillChange // 🟠 객체가 변경되기 전에 방출합니다.
.sink { _ in
print("\(john.age) will change")
}
print(john.haveBirthday())
// Prints "24 will change"
// Prints "25"
샘플 코드 12(애플 공식문서)
모델 자체에 바인딩하면 좋은데, 근데 북마크 같은 값이 바뀌어도 리스트 전체가 리렌더링 되어서 이건 별도로 모델을 분리하는 노력이 필요할 거 같음.
(웹에서 렌더링 개선할 때 주로 하던 작업들)
class MyViewModel: ObservableObject {
@Published var dataSource: MyModel
init(dataSource: MyModel) {
self.dataSource = dataSource
}
}
@StateObject
- WWDC2020에서 @StateObject를 추가적으로 공개
- @ObservedObject와 거의 같은 방식으로 작동
- SwiftUI가 View를 다시 렌더링 할 때, 실수로 취소되는 것을 방지
- View에서 @StateObject를 이용하여 직접 관찰 가능한 객체를 인스턴스화.
struct ContentView: View {
@StateObject var user = User()
}
@ObservedObject
ObservableObject랑 다르고, 얘는 구조체임.

- SwiftUI는 @ObservedObject를 통해 view가 외부 객체를 감지할 수 있게 해줌.
- 아래 코드에서 User Class는 ObservableObject를 준수하고 @Published 변수를 가짐
- @ObservedObject의 user 변수는 이러한 User class 객체를 담고
- SwiftUI는 이러한 user 객체의 @Published 변수 값이 변경될 때 view를 refresh
class User: ObservableObject {
@Published var name = "Hohyeon Moon"
}
struct ContentView: View {
@ObservedObject var user = User()
var body: some View {
VStack {
Text("Your name is \(user.name).")
}
}
}
@EnvironmentObject
- @EnvironObject는 보통 앱 전반에 걸쳐 공유되는 데이터에 사용
- @EnvironObject는 .environmnetObejct()를 통해 값이 전달할 수 있음.
- 전달하는 Object는 ObservableObject 프로토콜을 채택해야 함.
- 아래 코드와 같이 rootView를 제공하면, 어떠한 View에서도 사용이 가능
- EnvironmentObject property wrapper를 통해서 environmnet에 저장된 관찰 가능한 객체에 접근 가능
SwiftUI의 SceneDelegate는 더 이상 기본적으로 Xcode에서 생성되지 않음.
이거 근데 필요하면 WidnowGroup에서 modifier 이용하면 됨.
근데 AppDelegaet 필요하면 수동으로 생성해서 연결해야 함.

샘플 코드
간단한 예시.
import SwiftUI
// MySettings.swift
class Settings: ObservableObject {
@Published var version = 0
}
@main
struct CombinePracticeApp: App {
var body: some Scene {
WindowGroup {
ContentView().environmentObject(Settings())
}
}
}
struct ContentView: View {
@EnvironmentObject var settings: Settings
var body: some View {
Text("Hello, world!")
.padding()
Button {
settings.version += 1
} label: {
Text("version \(settings.version)")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
ObservableObject와 EnvironmentObject 비슷하지만 다른 차이점 보고가기
| 정의 위치 | 데이터 모델을 정의하는 프로토콜 | SwiftUI 환경(Environment)에 주입되는 객체 |
| 역할 | 데이터 변경을 감지하고 뷰를 업데이트 | 여러 뷰에서 공유 가능한 상태 객체 |
| 주입 방식 | @StateObject, @ObservedObject로 직접 주입 | 상위 뷰에서 .environmentObject(_:)로 한 번 주입 후 하위 뷰 어디서나 접근 가능 |
| 수명 주기 관리 | @StateObject가 수명 관리 담당 (init 한 번만 실행) | 수명은 상위 뷰에서 관리되며, 하위 뷰는 참조만 함 |
| 뷰 간 데이터 공유 | 한정적 — 뷰 간 전달 시 수동으로 넘겨줘야 함 | 전역적 — 한 번 환경에 올리면 하위 뷰 어디서든 접근 가능 |
| 에러 발생 시점 | 옵저버가 없으면 문제 없음 | .environmentObject() 주입 안 했는데 @EnvironmentObject로 접근하면 런타임 크래시 발생 ⚠️ |
| 사용 예시 | 특정 뷰 전용 상태 관리 | 전역 상태, 세션 정보, 사용자 설정 등 앱 전체 공유 상태 관리 |
PreferenceKey
- 하나의 뷰에 여러 하위항목(children)이 있는 경우 자동적으로 상위 항목에서 볼 수 있는 단일 값으로 결합
- key - value로 구성된 데이터 전달 수단
- 전달 방향은 하위뷰 -> 상위뷰.

전반적인 흐름 (다른 사람이 작성한 이미지 참고)

(참고)
https://www.hohyeonmoon.com/blog/swiftui-data-flow/
SwiftUI의 데이터 흐름 | Hohyeon Moon
Hohyeon Moon iOS developer. Code for a happier life. Resume
www.hohyeonmoon.com
https://velog.io/@budlebee/SwiftUI-ObservableObject
SwiftUI : ObservableObject 와 EnvironmentObject
여러 View 에서 사용될 State 를 한데 저장해 놓고 쓸 수 있는 기능.매 초마다 숫자가 1씩 늘어나는 어플리케이션을 만들어보자. 어플리케이션에는 두개의 View가 있고(ContentView, SecondView), 두개의 View
velog.io
https://velog.io/@kipsong/SwiftUI-Preference-Key-coordinatespace
[SwiftUI] Preference Key & coordinatespace
Preferencekey - value 로 구성된 데이터 전달 수단입니다.전달 방향은 하위뷰 -> 상위뷰 입니다.구현방법reduce 메소드 우리가 선언한 “PreferenceKey” 를 사용하는 하위뷰를 순회합니다. 동시에, 상위뷰
velog.io
https://protocorn93.github.io/tags/PreferenceKey/
Best Website Builder Online | Yoursite
Start Your Site & Connect With The World. Create your website and go online now! All plans include custom mailboxes and web hosting.
yoursite.com
'apple > SwiftUI, Combine' 카테고리의 다른 글
| Combine 빠르게 공부 정리 (0) | 2022.06.03 |
|---|---|
| SwiftUI @StateObject (0) | 2022.06.02 |
| iOS 화면 캡쳐 및 녹화 감지 (feat. SwiftUI) (0) | 2022.06.02 |
| SwiftUI ViewModifier (0) | 2022.06.02 |
| [SwiftUI] LazyVGrid (0) | 2022.05.25 |