-
Notifications
You must be signed in to change notification settings - Fork 3
[승용] Demystify SwiftUI
Eric Kwon / 권승용 edited this page Jul 21, 2024
·
1 revision
- Identity
- SwiftUI가 여러 번의 업데이트 동안 요소들이 동일한지 또는 서로 다른지 인식하는 방법
- Lifetime
- SwiftUI가 시간 경과에 따라 뷰와 데이터의 존재를 추적하는 방법
- Dependencies
- 인터페이스가 언제 왜 업데이트되어야 하는지 SwiftUI가 이해하는 방법
- 요 세 가지 개념들이 언제 어떻게 무엇을 SwiftUI가 변경해야 하는지 결정하고, 온스크린에서 보이는 동적 유저 인터페이스를 생성한다.
- Explicit identity
- 커스텀 또는 데이터-드리븐 아이덴티파이어들
- Structural identity
- 뷰 계층의 위치와 뷰 타입으로 뷰들을 구분하는 아이덴파이어
- 명시적으로 아이덴티티를 할당하는 방법
- 강력하고 유연하지만, 어디선가 아이덴티티를 추적하는 존재가 필요
- SwiftUI는 아래와 같이 명시적 아이덴티티를 사용한다.
List {
Section {
ForEach(resqueDogs, id: \.dogTagID) { rescueDog in
ProfileView(rescueDog)
}
}
Section("Status") {
ForEach(adoptedDogs, id: \.dogTagID) { rescueDog in
ProfileView(rescueDog, foundForeverHome: true)
}
}
}
ScrollViewReader { proxy in
ScrollView {
HeaderView(rescueDog)
.id(headerID)
Text(rescueDog.backstory)
Button("Jump to Top") {
withAnimation {
proxy.scrollTo(headerID)
}
}
}
}
- 명시적으로 식별자를 부여하지 않아도 된다고 해서 identity가 없는 뷰가 존재하는 것은 아니다.
- 명시적이지는 않아도 모든 뷰가 식별자를 가지고 있다.
- SwiftUI는 뷰 계층구조를 사용해 뷰들에 대한 암시적 식별자(implicit identity)를 생성한다.
- SwiftUI는 API 전반에서 구조적 아이덴티티를 사용하며, 그 대표적인 예시로 View 코드 내에서 if문과 같은 조건문을 사용하는 경우가 있다.
// Structural Identity in SwiftUI
var body: some View {
if rescueDogs.isEmpty {
AdoptionDirectory(selection: $rescueDogs)
} else {
DogList(rescueDogs)
}
}
- 조건문의 구조는 각 뷰를 식별하는 명확한 방법을 제공한다.
- 첫 번째 뷰는 조건문이 참일 때만 보여지고, 두 번째 뷰는 조건문이 거짓일 때만 보여진다.
- 이는 두 뷰가 비슷해 보여도, 항상 두 뷰를 구분 가능하다는 의미이다.
- 다만 이는 SwiftUI가 이러한 뷰들이 항상 제자리에 위치하고 자리를 바꾸지 않음을 보장할 수 있을 때만 제대로 작동한다.
- SwiftUI는 뷰 계층구조의 타입 구조를 통해 자리를 바꾸지는 않는지 살펴본다.
- identity는 시간이 지나며 변하는 다양한 값에 대한 안정적인 요소를 정의할 수 있게 해 준다.
- 값은 변해도 뷰는 element는 변하지 않음!
- 즉 시간이 지남에 따른 연속성을 도입 가능 뷰가 처음 생성되고 나타날 때, SwiftUI는 이전에 설명한 기술들을 사용해 뷰에 identity를 부여한다.
- 시간이 지남에 따라 뷰를 위한 새로운 값들이 생성된다.
- 그러나 SwiftUI의 입장에서는 이 값들을 같은 뷰를 나타낸다.
- 뷰의 identity가 변하거나 뷰가 제거될 때 뷰의 lifetime도 종료된다.
- 따라서 뷰의 lifetime을 이야기할 때에는 해당 뷰의 identity가 살아있는 기간을 이야기하는 것이다.
- 뷰의 identity와 lifetime을 연결짓는 것은 SwiftUI가 어떻게 우리의 state를 유지하는가를 이해하기 위해 필수적이다.
var body: some View {
if dayTime {
CatRecorder()
} else {
CatRecorder()
.nightTimeStyle()
}
}
-
위 예제에서는 두 갈래로 분리된 같은 뷰가 있다.
-
이전에 배운 내용을 떠올려보면, 구조적 identity로 인해 두 뷰는 서로 다른 identity를 가지게 된다.
- 이러한 identity의 다름은 애니메이션에도 영향을 미치지만, State의 지속성에도 영향을 미친다.
-
body를 계산하고 true 분기점으로 진입하면 SwiftUI는 State의 초기값을 사용해 영구 저장소를 할당한다.
-
뷰의 lifetime동안 State 값은 다양한 action들에 의해 변경되고, 저장소는 지속적으로 유지된다.
-
그러나 dayTime이 false로 변경되고 false 분기점으로 진입하면 무슨 일이 일어날까?
- State의 초기값을 사용해 else 아래의 뷰를 위한 새로운 저장소를 할당하게 된다.
- 왜냐하면 두 뷰는 서로 다른 identity, 즉 서로 다른 lifetime을 가지는 개별적인 뷰이기 때문.
-
그리고 첫 번째 true 뷰를 위한 저장소는 곧바로 할당 해제 된다. 그런데 만약 다시 dayTime이 true로 변경된다면 어떻게 될까?
-
다시 true 뷰를 위한 새로운 초기값을 가진 저장소가 생기고, false 뷰를 위한 저장소가 할당 해제 된다.
-
요점은 identity가 변경되면 State도 대체된다는 점을 유의해야 한다는 것이다.
-
굉장히 중요한 포인트 : State의 지속성은 View의 lilfetime과 연결되어 있다.
- State lifetime = View lifetime
-
이는 뷰의 본질인 State를 명확하게 분리해 identity와 연결지을수 있는 강력한 컨셉이다.
- 나머지 모든 것들은 이 개념으로부터 파생될 수 있다.
- 데이터는 너무나도 중요해서, SwiftUI는 데이터의 identity를 명시적 identity의 형태로 사용하는 data-driven 구조체들을 가지고 있다.
- ForEach
- confirmationDialog() / alert()
- List / Table / OutlineGroup
- 그 전형적인 예시는 ForEach!
- ForEach에 대해 잘 알아보기 위해 ForEach를 초기화할 수 있는 모든 방법들을 알아보자.
- 가장 간단한 형태는 constant range를 가지는 것
ForEach(0..<5) { offset in
Text("\(offset)")
}
- SwiftUI는 range 내의 offset을 사용해 뷰 빌더가 생산한 각각의 뷰들을 식별한다.
- 이는 뷰의 수명동안 identity가 안정적으로 제공되는 것을 보장한다.
- 정수 리터럴이 아닌 변수나 상수를 사용해 constant range가 아닌 dynamic range를 초기자에 집어넣으면 warning 발생
- 뷰 계층구조는 트리 형태이지만, 의존 관계는 그래프 형태이다.
- 이러한 그래프 구조는 SwiftUI가 새로운 body를 필요로 하는 뷰들만을 효율적으로 업데이트하는 것을 가능하도록 해주기 때문에 중요하다.
- 의존성 그래프의 중추는 identity이다.
- 모든 뷰는 명시적으로든 구조적으로든 identity를 가지고, SwiftUI는 이 identity를 통해 변경사항을 올바른 뷰에 라우팅하고 UI를 효율적으로 업데이트한다.
- Identifier의 수명이 뷰의 수명과 직결되기 때문에, Identity의 안정성은 굉장히 중요하다.
- 안정적이지 못한 Identifier는 짧은 뷰 수명을 야기할 수도 있다.
- 안정적인 Identifier를 가지면 SwiftUI가 계속해서 뷰를 위한 저장소를 생성할 필요가 없고, 의존성 그래프를 업데이트하는 작업을 줄일 수 있기 때문에 성능 향상에도 도움이 된다.
- 또한 안정적인 Identifier는 State를 잃는 상황을 방지하도록 해 준다.
- 안정적인 Identifier는 랜덤하지 않고 안정적인, 고유한 Identifier이다.
고병학 | 권승용 | 김대황 | 김인환 | 유정주 | 윤동주 | 이준복 | 이창준 | 정종인 | 홍승현 |
---|---|---|---|---|---|---|---|---|---|
bengHak | ericKwon95 | qwerty3345 | loinsir | jeongju9216 | yoondj98 | junbok97 | SwiftyJunnos | chongin12 | WhiteHyun |