Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[승용] Demystify SwiftUI

Eric Kwon / 권승용 edited this page Jul 21, 2024 · 1 revision

SwiftUI는 우리 코드에서 무엇을 보고 있을까?

  • Identity
    • SwiftUI가 여러 번의 업데이트 동안 요소들이 동일한지 또는 서로 다른지 인식하는 방법
  • Lifetime
    • SwiftUI가 시간 경과에 따라 뷰와 데이터의 존재를 추적하는 방법
  • Dependencies
    • 인터페이스가 언제 왜 업데이트되어야 하는지 SwiftUI가 이해하는 방법
  • 요 세 가지 개념들이 언제 어떻게 무엇을 SwiftUI가 변경해야 하는지 결정하고, 온스크린에서 보이는 동적 유저 인터페이스를 생성한다.

Identity의 종류

  • Explicit identity
    • 커스텀 또는 데이터-드리븐 아이덴티파이어들
  • Structural identity
    • 뷰 계층의 위치와 뷰 타입으로 뷰들을 구분하는 아이덴파이어

Explicit 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)
			}
		}
	}
}

Structural Identity

  • 명시적으로 식별자를 부여하지 않아도 된다고 해서 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는 뷰 계층구조의 타입 구조를 통해 자리를 바꾸지는 않는지 살펴본다.

Lifetime & Identity

  • identity는 시간이 지나며 변하는 다양한 값에 대한 안정적인 요소를 정의할 수 있게 해 준다.
    • 값은 변해도 뷰는 element는 변하지 않음!
    • 즉 시간이 지남에 따른 연속성을 도입 가능 뷰가 처음 생성되고 나타날 때, SwiftUI는 이전에 설명한 기술들을 사용해 뷰에 identity를 부여한다.
  • 시간이 지남에 따라 뷰를 위한 새로운 값들이 생성된다.
  • 그러나 SwiftUI의 입장에서는 이 값들을 같은 뷰를 나타낸다.
  • 뷰의 identity가 변하거나 뷰가 제거될 때 뷰의 lifetime도 종료된다.
  • 따라서 뷰의 lifetime을 이야기할 때에는 해당 뷰의 identity가 살아있는 기간을 이야기하는 것이다.
  • 뷰의 identity와 lifetime을 연결짓는 것은 SwiftUI가 어떻게 우리의 state를 유지하는가를 이해하기 위해 필수적이다.

State는 Identity의 Lifetime과 묶여 있다.

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와 연결지을수 있는 강력한 컨셉이다.

    • 나머지 모든 것들은 이 개념으로부터 파생될 수 있다.

Data의 identity로 명시적 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 발생

Dependency Graph

  • 뷰 계층구조는 트리 형태이지만, 의존 관계는 그래프 형태이다.
  • 이러한 그래프 구조는 SwiftUI가 새로운 body를 필요로 하는 뷰들만을 효율적으로 업데이트하는 것을 가능하도록 해주기 때문에 중요하다.
  • 의존성 그래프의 중추는 identity이다.
  • 모든 뷰는 명시적으로든 구조적으로든 identity를 가지고, SwiftUI는 이 identity를 통해 변경사항을 올바른 뷰에 라우팅하고 UI를 효율적으로 업데이트한다.

Identifier 안정성

  • Identifier의 수명이 뷰의 수명과 직결되기 때문에, Identity의 안정성은 굉장히 중요하다.
    • 안정적이지 못한 Identifier는 짧은 뷰 수명을 야기할 수도 있다.
  • 안정적인 Identifier를 가지면 SwiftUI가 계속해서 뷰를 위한 저장소를 생성할 필요가 없고, 의존성 그래프를 업데이트하는 작업을 줄일 수 있기 때문에 성능 향상에도 도움이 된다.
  • 또한 안정적인 Identifier는 State를 잃는 상황을 방지하도록 해 준다.
  • 안정적인 Identifier는 랜덤하지 않고 안정적인, 고유한 Identifier이다.

Clone this wiki locally

AltStyle によって変換されたページ (->オリジナル) /