Dev_Dylan

[iOS] Identity of SwiftUI (with Demystify SwiftUI_1) 본문

iOS/SwiftUI

[iOS] Identity of SwiftUI (with Demystify SwiftUI_1)

Dylan_21 2024. 1. 27. 20:26

WWDC21의 Demtystify SwfitUI의 

- Identity

- Lifetime

- Dependencies

중 Identity에 대한 내용 정리

https://developer.apple.com/wwdc21/10022

 

Demystify SwiftUI - WWDC21 - Videos - Apple Developer

Peek behind the curtain into the core tenets of SwiftUI philosophy: Identity, Lifetime, and Dependencies. Find out about common patterns,...

developer.apple.com

 

 

정리

아래내용이 매우 길기 때문에 개인적인 생각으로 정리해 봄.

(이상하거나 잘못된 정보는 제보 바람)

 

먼저,

Why? Identity를 명확하게 가져가야 할까? 에 대해 생각해 보면,

각 View들을 식별하고 올바른 데이터를 View에 원활하게 전달해 줄 수 있다.

 

SwiftUI는 UIKit과는 다르게 Struct로 구성되어있기 때문에, 각각 View에 대한 ID(식별자 identity)가 필요하다.

SwitUI에서는 어떻게 View에 Identity를 부여할까? 두 가지가 있다.

 

1. Explicit Identity 명시적

직접 해당 View에 ID를 Custom 하게 붙여준다.

 

2. Structural Identity 구조적

SwiftUI가 위치에 따라 즉 코드의 구조를 보고 해당하는 id를 부여함.

자동적으로 추론한다.

 

SwiftUI에서 모든 View는 ID를 가지고 있다.

 

AnyView는 최대한 지양해야 한다.

 

Brunch(분기)를 통해 View를 구성하기보단,

삼항 연산자를 이용하는 것이 성능적으로도 유리하다.


Identity

위 사진의 두 개는 같은 강아지 일까?

 

‘맞다’ ‘아니다’를 떠나서

‘모른다’, '알 수 없다'!

 

왜냐하면 정보가 충분하지 않기 때문이다.

 

즉 identity를 알 수 없음 (식별할 수 없음)

위의 예시처럼 SwiftUI가 앱을 이해하는 중요한 측면이다.

 

App을 통한 예시 (Good Dog, Bad Dog)

 

위와 같이 생긴 두 개의 뷰가 있다.

 

 

각각의 View에서 `발바닥 View`는 과연 같은 것일까?

 

2가지 생각을 할 수 있다.

  1. 같은 View이다 : 위에서 아래로 이동한 것일 수 있음 (slide across)
  2. 다른 View 이다 : 위아래로 2개의 발바닥 View를 생성 (fade in & out)

결론은 화면만 보고 알 수 없다.

 

이것이 SwiftUI가 View Identity를 가져야 하는 이유

 

같은 View 라면 아래와 같이 같은 ID를 가지게 될 것이고,

 

 

다른 View라면 각각의 ID를 가진다.

즉, 동일한 ID를 공유하는 뷰는 동일한 개념적 UI 요소의 서로 다른 상태를 나타낸다. (즉, 같은 View이다!)

 

 

각각(별개)의 UI 요소를 나타내는 뷰는 항상 다른 ID를 갖는다. (다른 View이다!)

 

 

Types of identity

SwiftUI에서 Identity를 식별하는 2가지 유형이 있다.

 

1. Explicit Identity (명시적)

맞춤형, 데이터 기반 식별자 사용

 

2. Structural Identity (구조적)

View Hierarchy에서 Type과 위치에 따라 View를 구별한다.

 

 

 

 왼쪽 강아지와 오른쪽 강아지를 보면서,

어떤 종류의 추가 정보가 식별에 도움이 될까?

 

만약 이름이 같으면 같은 강아지일 확률이 높다.

하지만 이름이 다르면 다른 강아지임을 보장할 수 있다.

더 명확하기 때문이다.

 

어떤 관점에서 바라보느냐에 따라 어떻게 식별할지 달라질 수 있다.

 

Explicit Identity

Explicit identity : 이름이나 식별자를 할당하는 것

먼저 명시적(Explicit)으로 ID를 가지는 것은

매우 강력하고, 유연하다.

 

하지만 어딘가에서 모든 이름 추적해야 한다.

 

 

 

 

AppKit과 UIKit에서는 Class를 사용하기 때문에 Pointer ID를 사용할 수 있지만,

SwiftUI는 Value Type이기 때문에 포인터를 사용할 수 없다.

Value Type에는 SwiftUI가 해당 View에 대한 지속적인 ID로 사용할 수 있는 정식 참조가 없다.

SwiftUI Essentials 19
View에 클래스 대신 값 유형을 사용하는 이유!!!! 확인!

 

 

어떻게 SwiftUI에서 명시적으로 식별할까?

한 가지 방법은

\. dogTagID 와 같이 keyPath를 사용하는 방법이다.

 

위 코드와 같이 keyPath인 \. dogTagID  사용하면

SwiftUI 가 ID를 활용해 어디에서 변경되었는지 확인해 상태를 변경하고,

각각 Animation을 설정할 수도 있다.

 

 

좀 더 깊숙이 이해해 보자!

 

간략하게 설명하자면

1. ScrollViewReader를 사용해 맨 아래 버튼이 눌리면 맨 위로 올라가는 코드임.

2. 4번 Line의. id(headerID)는 사용자 정의 식별자를 사용한 것이다.

3. scrollTo(_:) 특정 뷰로 이동하도록 한다.

 

위 코드의 장점은,

모든 뷰에 명시적으로 식별자를 사용할 필요 없고 필요한 곳에 즉, 참조할 뷰만 식별할 수 있다는 것

 

why?

HeaderView에 특정 ID를 주입하고,

Button Action의 scrollTo에 들어갈 ID를, HeaderView에 주입한 ID를 사용하기 때문에

이보다 더 명확한 식별은 없을 것이다.

 

 

즉 HeaderView, Button에 만 식별자가 필요하고
ScrollViewReader, ScrollView, Text 에는 식별자가 필요 없다.

 

ID가 명시적이지 않다고 해서 ID가 없는 것은 아님

모든 뷰는 ID를 가지고 있다. 명시적 ID가 아닐지라도!

 

Structural Identity

그렇다면 구조적 ID를 이해해 보자.

 

SwiftUI는 뷰 계층 구조를 사용해,

뷰에 대한 암시적 ID를 생성한다.

즉, 알아서 만들어준다.

 

 

구조적인 식별자는 무엇일까?

만약 오른쪽과 왼쪽 강아지가 가만히 있다면? 움직이지 않는다면?

우리는 왼쪽 강아지와 오른쪽 강아지!라고 위치에 따라 이름을 부여해 줄 수 있다.

 

이렇게 위치에 따라 식별이 가능하다

 

이렇게 구별하기 위해 상대적인 배열을 사용하고 이를 Structual Identity 라고 한다.

 

 

SwiftUI는 API 전체에서 Structual Identitiy를 활용함
ex) View코드 내에서 if, switch 등 조건 논리를 사용하는 경우

 

 

 

SwiftUI의 관점에서 보자.

좌측은 우리가 일반적으로 쓰는 코드이고

우측은 SwiftUI가 바라보는 우리의 코드를 변환한 것이다.

 

SwiftUI는 some View와 분기(branch, 조건문)를 우측과 같이 generic types으로 이해한다.

 

(View Protocol를 채택한 View가 body에서 암시적으로 @ViewBuilder 선언된다.)

이렇게 암시적으로 선언된 @ViewBuilder에 의해 SwiftUI에 번역이 된다.

 

아래와 같이 SwiftUI는

각각의 AdoptionDirectory, DogList에

ID를 할당할 수 있도록 한다.

 

 

 

다음은 위에서 진행한 Good Dog Bad Dog의 일부 코드다

ViewBuilder에 의해 if 문 내부의 2개의 Branch(분기)에서

각각의 View는 ID를 갖는다.

 

 

하지만 아래와 같이 삼항 연산자를 통해

하나의 ID를 가지고 있는 단일 View로 만들어 줄 수 있다.

 

 

SwiftUI는 후자를 선호한다.

  • ID를 유지함
  • 보다 유연한 전환을 제공함

이는 View의 수명과 상태를 보존하는데 도움이 된다.

 

 

AnyView

Structual Identity의 적수 AnyView

AnyView가 뭘까?
다른 타입으로 감싸주는 역할을 함.

AnyView | Apple Developer Documentation

 

AnyView | Apple Developer Documentation

A type-erased view.

developer.apple.com

 

 

 

아래와 같이 분기 처리를 한 2개의 View가 있다.

코드 & Generic Type Structure

 

 

 

AnyView는 이렇게 dogView 프로퍼티를 return 해야 하는데

담아 둘 프로퍼티가 없을 때

다음 로직들을 거친 후 특정 View를 담아줄 수 있다.

 

 

다른 종류의 View를 return 하기 때문에

AnyView로 Wrapping 해야 한다.

→ 이것은 SwiftUI가 위 코드의 조건부 구조를 볼 수 없다.

 

때문에 AnyView는 “type-erasing wrapper type” 으로 불린다.

어차피 우리도 보기 힘듦.. ㅋㅋ

 

AnyView 코드를 단순화 작업을 진행해 보자.

 

먼저, HStack을 사용해 조건단순화 시킨다.

HStack을 사용함으로써 중복된 코드를 제거한다.

 

 

 

다음은 위에서 HStack으로 묶어주게 되면서 각 branch들은 dogView로 return 된다.

모든 반환 타입이 dogView가 되기 때문에 return으로 바꿔준다.

 

 

 

만약 각 분기별로 다른 반환타입의 View를 가지게 된다면 오류가 발생한다

SwiftUI는 단일 반환 Type을 요구하기 때문이다.

 

 

하지만, 아래와 같이 @ViewBuilder를 명시해 준다면

오류를 피할 수 있다.

 

그렇게 된다면 SwiftUI가 보는 TypeSignature는 다음과 같다.

 

 

 

SwiftUI는 Switch를 ViewBuilder에서도 지원하기 때문에 권장한다.

 

 

Switch는 Syntactic Sugar 이므로

if문과 동일한 Type Signature를 보여주게 된다!

 

 

 

 

일반적인 상황에서는 AnyView를 피해라!

- Makes code harder to understand

- Fewer compile-time diagnostics

- Worse performance when not needed

AnyView는 컴파일러에서 Static Type 정보를 숨기기 때문에 적절하게 오류 및 경고가 나타나지 않는 경우가 있음

또한, 성능저하가 올 수 있음.


 

'iOS > SwiftUI' 카테고리의 다른 글

[iOS] Lifetime of SwiftUI (with Demystify SwiftUI_2)  (0) 2024.01.28
Comments