-
[SwiftUI] View Identity (feat. WWDC 2021)iOS 2024. 10. 31. 16:45
이 포스트는 WWDC2021: Demystify SwiftUI 세션 중 View Identity에 대한 내용을 공부하며 옮겨적은 글입니다.
오역이나 잘못 이해한 부분이 있을 수 있으니 틀린 부분은 댓글로 알려주세요 :)Explicit Identity
- View나 data의 type을 하나의 그룹으로 묶을 수 있다
- 특정 View를 참조하기 위한 custom identifer를 제공할 수 있다.
UIKit/Objective-C: Pointer Identity
- UIKit과 Objective-C에서 각각의 UIView/NSView 객체는 각자가 할당된 메모리 주소를 향한 유니크한 포인터를 갖고 있다.
- 이 포인터를 활용해 각각의 뷰를 참조할 수 있다.
- 만약 두 개의 UIView/NSView가 같은 포인터를 공유한다면 이 두 개의 뷰는 동일한 객체임이 보장된다.
SwiftUI
- 각각의 View는 값 타입인 struct이다.
- struct를 적용한 이유 (WWDC19 SwiftUI essentials)
- 메모리 주소 할당이 없고, 포인터를 갖고 있지 않다
- 경제적인 memory representation
- 소형의 단일목적을 가진 컴포넌트를 지원한다
- SwiftUI에서 어떤 뷰에 대한 지속적인 ID로 사용하는 canonical reference를 값 타입은 갖지 않는다
- 대신 Expicit Identifier의 다른 형식에 의존한다.
ForEach의 id 파라미터에 들어가는 keyPath가 가리키는 값
- 리스트 내에서 일치하는 View를 명시적으로 식별하는데 사용
- SwiftUI는 List 내에서 정확히 어떤 변화가 발생했는 지 이해하고 List에 올바른 애니메이션을 일으키는데 ID를 사용할 수 있다.
ScrollViewReader + ScrollView
- ScrollView내의 특정 View에 id를 부여해서 ScrollViewReader의 proxy.scrollTo 메서드를 이용해 해당 id로 위치를 옮길 수 있다
- ScrollView내의 모든 View 식별할 필요가 없다는 점이 쿨하다
- Explict Identifier를 갖지 않는 View는 식별할 수 없는가 하면 그건 아니다
Structural Identity
- SwiftUI에서 모든 View는 Identity를 갖고 있으며 Explicit Identity가 없더라도 structural identity를 가진다.
- View의 type과 view hierarchy상의 위치로 각 View를 식별한다
- structural identity는 SwiftUI의 API 전반에서 활용된다.
if 문의 true /. false에 맞춰 각기 다른 View, A / B를 반환하는 예시
- SwiftUI는 View 계층 구조의 type structure를 살펴봄으로써 true일 때 에만 AdoptionDirectory View가 되고 false일 때만 DogList View가 되는 것을 보장한다.
- SwiftUI는 A,B View에서 A, B라는 generict type을 바라보고 @ViewBuilder를 통해 if문의 코드블럭을 _ ConditionalContent<AdoptionDirectory, DogList View>라는 타입의 View로 변환한다
- View protocol은 묵시적으로 @ViewBuilder 안에서 if 구문으로 부터 단일 generic View를 생성하는 body 프로퍼티를 래핑한다
View Protocol
- body 프로퍼티의 return type some View는 static composite type인 _ConditionalContent<AdoptionDirectory, DogList View>를 대신하는 placeholder이며 그것을 숨김으로써 코드를 어지럽히는 일을 방지한다
- some View 타입을 통해 SwifUI는 true 일 때는 AdoptionDirectory View가, false일 때는 DogList View가 되는 것을 보장하고 내부적으로 묵시적이고 stable한 identity를 부여받도록 한다
Apple이 권장하는 structural identity의 사용방식
- 하나의 View를 일관된 identity로 modifying하는 방식
- 권장방식의 장점
- SwiftUI가 View의 identity를 보전한다
- View의 부드러운 변환을 보장
- View의 lifetime을 보전한다
- View의 State를 보전한다
some View vs AnyView
if 문의 각 조건절에서 각각 다른 종류의 AnyView를 return하는 view 메서드
AnyView를 사용한 조건별 View 생성 코드의 conditional structure
- AnyView는 type-erasing wrapper type, 자신의 generic signiture로 래핑한 View의 타입을 숨긴다
- 그러므로 SwiftUI가 코드의 conditional structure를 볼 수 없음
- 심지어 가독성 마저 떨어지는 문제가 있다
@ViewBuilder의 명시적 사용과 묵시적 사용
- View 프로토콜의 body 프로퍼티는 @ViewBuilder로 래핑되어 있으므로 묵시적으로 단일 generic View로 변환됨
- 반면 helper 함수들의 경우에는 컴파일러가 default로 @ViewBuilder가 될 것이라 추론하지 않으므로 직접 @ViewBuilder를 적용해주어야 한다
If문을 덜어내기
- if문을 switch 구문으로 변경하여도 @ViewBuilder를 적용할 수 있다
- switch 구문은 조건절을 위한 syntactic sugar(직관적으로 읽기 쉬운 코드로 만들어주는 문법) 그 자체이므로 읽기 쉽고 View가 만들어지는 모든 경우를 빠르게 이해할 수 있다.
- 우측에 있는 resulting View의 type signature 또한 switch문과 정확하게 일치한다
결론
- Apple은 언제든 가능하다면 AnyView의 사용을 피하는 것을 권장한다
- 대신 전통적으로 분기 제어문으로 사용한 if문이나 switch 문을 사용하는 편이 View의 잠재적인 상태변화를 보기에 훨씬 쉽다
- AnyView는 컴파일러가 View의 static type information를 볼 수 없게 만들고 때때로 유용한 진단오류와 경고가 코드에 표시되는 것을 차단한다
- 꼭 필요한 상황이 아닌데 AnyView를 사용하는 것은 낮은 성능을 야기할 수 있다는 것을 명심해야 한다.
- 가능하다면 AnyView를 다른 코드로 전달하는 대신 static type information을 보전하기 위해서 generic을 사용해야 한다.
'iOS' 카테고리의 다른 글
[SwiftUI] UI Update (feat. WWDC2021) (0) 2024.11.03 [SwiftUI] View의 Lifetime (feat. WWDC 2021) (0) 2024.11.01 [RxSwift] UILabel이 subscribe를 사용할 수 없는 이유 (0) 2024.07.31 Alamofire를 구조화된 코드로 사용하기 (feat. URLRequsetConvertible) (2) 2024.06.19 [UIKit] 화면전환 코드를 프로토콜로 사용하기 (0) 2024.06.04