[WWDC] Demystify SwiftUI — Identity

WWDC2021 Demystify SwiftUI 중 Identity 정리글

SwiftUI 는 우리의 코드에서 아래의 세가지 요소를 확인합니다

이 세 가지 개념을 통해 SwiftUI는 변경해야 할 사항(what), 변경 방법(how), 변경 시기(when)를 결정할 수 있습니다.

오늘은 이 중 첫번째 개념인 Identity 에 대해 살펴보겠습니다.

Identity

여기 두개의 발바닥 view가 있습니다. 이것들은 완전히 다른 뷰일까요, 아니면 색깔과 위치만 다른 동일한 뷰일까요? 이러한 구분이 어떠한 차이를 만들어낼까요?

만약 다른 뷰라면? fade in/out 과 같은 독립적인 방식으로 전환 됩니다.

만약 동일한 뷰라면? 화면을 가로질러 이동하는 식으로 전환 됩니다.

이렇듯 SwiftUI는 view identity의 구분에 따라 인터페이스가 한 상태에서 다른 상태로 전환되는 방식을 변화 시킵니다. (key concept)

indenty가 같는 것은, 서로 동일한 element라는 것을 뜻하고, identity가 다르다는 것은, 그 반대를 뜻합니다.

참고로 위의 예시에서 SwiftUI는 같은 identity를 사용해서 보다 유동적인 전환을 제공하는 것을 권장합니다. 같은 identity를 사용하면 view의 lifetime 과 state를 유지하는데도 도움이 됩니다.

Type of Identity

Identity 에는 두가지 타입이 있습니다.

먼저 Explicit Identity 부터 자세히 살펴보겠습니다.

Explicit Identity

이미 사용 중인 Explicit Identity의 한 가지 예시는 UIKitAppKit 에서 사용되는 pointer identity 입니다.

UIViewNSView클래스이기 때문에 각각 메모리 할당에 대한 고유한 포인터가 있습니다. 포인터를 사용하여 개별 뷰를 참조할 수 있으며, 두 뷰가 동일한 포인터를 공유하면 동일한 뷰임을 보장할 수 있습니다.

하지만 SwiftUI의 view는 struct 로 표현 되는 value type 입니다. 따라서 포인터를 사용하지 않습니다.

참고: view 를 value type으로 표현하는 이유 — 1. Not allocated, no pointers / 2. Efficient memory representation / 3. Supports small, single-purpose components

대신 SwiftUI는 다른 형태의 Explicit Identity에 의존합니다. 아래의 예시에서 사용된 id 매개 변수는 Explicit Identity의 한 형태로, 목록에서 해당 view를 명시적으로 식별하는 데 사용됩니다. 만약 목록이 바뀌면 해당 id 를 사용하여 무엇이 변경된 것인지 파악하고 올바른 애니메이션을 생성할 수 있습니다.

바로 밑의 예시에서 알 수 있듯이, id(_:) modifier 을 사용해서 custom identifier을 설정할 수도 있습니다. 여기서 주목할 점은 HeaderView 와 같이 다른 곳에서 참조할 element를 제외하고는 모든 뷰를 명시적으로 식별할 필요가 없다는 것입니다.

하지만 명시적으로 식별되지 않았을지라도, 모든 뷰는 identity가 존재 합니다. 바로 SwiftUI는 뷰 hierarchy 를 사용하여, 뷰에 대한 implicit identitiy를 생성하기 때문입니다. 이것이 바로 두번째로 설명할 structural identity 입니다.

Structural identity

위에서 잠깐 언급했듯, Structural identity는 뷰의 상대적인 위치에 따라 결정됩니다.

SwiftUI는 API 전반에 걸쳐 Structural identity를 사용하는데, 일반적인 예는 View 코드에서 if 와 같은 조건부 로직을 사용하는 경우입니다.

아래에서 확인할 수 있듯 조건문의 구조를 통해 각 view을 명확하게 식별할 수 있습니다. (True 일때 표시되는 뷰 vs False 일때 표시되는 뷰)

하지만 사실 이 기능은 view들이 해당 위치에 계속 stay하고, 자리를 변경(swap)하지 않을 것 이라는 사실을 SwiftUI가 정적으로 보장할 수 있을 때만 작동합니다. 그리고 SwiftUI는 view hierarchy의 type 구조를 확인하여 이 작업을 수행합니다.

내부적으로 위의 코드는 참/거짓 content 정보를 가진 하나의 generic view_ConditionalContent변환됩니다. 이 변환은 Swift의 result builder 유형인 ViewBuilder를 통해 수행됩니다. 참고로 View protocol에서는 body property를 ViewBuilder에 암시적으로 래핑하기 때문에 우리가 따로 명시해주지 않아도 됩니다.

generic type을 사용하여, SwiftUI는 true view 가 항상 AdoptionDirectory 이고 false view는 항상 DogList라는 것을 보장할 수 있으므로, 암시적이고 안정적인 identity를 할당할 수 있습니다.

반대로 AnyView를 사용하는 것은 지양해야하는데, 그 이유는 1. 읽기 어렵고, 2. 컴파일러가 static type 정보를 알 수 없기 때문에 도움이 되는 에러나 경고를 숨길 수 있고, 3. 퍼포먼스 저하가 일어날 수 있기 때문입니다. 따라서 AnyView 대신 static type 정보를 보존하는 제네릭을 사용합니다.

마무리

SwiftUI는 view identity의 구분에 따라 인터페이스가 한 상태에서 다른 상태로 전환되는 방식을 변화 시킵니다. 그리고 identity 에는 두가지 종류가 있습니다.

Explicit identity — 뷰의 identity를 데이터에 연결하거나, 특정 뷰를 참조할 수 있도록 custom identifier를 제공하는 방법을 알아봤습니다.

Structural identity — SwiftUI가 어떻게 view hierarchy 내에서 타입 및 위치를 기준으로 view 식별하는지 알아봤습니다.

이렇게 SwiftUI가 확인하는 세가지 요소 (Identity, Lifetime, Dependency) 중 첫번째 Identity 에 대해 살펴보았습니다.

나머지 두개는 귀찮아서 쓸지 안쓸지 모르겠습니다. 쓰게 된다면 밑에 링크를 추가하겠습니다.

다음 글

출처

 https://github.com/sujinnaljin/TIL