ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [SwiftUI] UI Update (feat. WWDC2021)
    iOS 2024. 11. 3. 19:34

     

     

    이 포스트는 WWDC2021: Demystify SwiftUI 세션 중 UI Update에 대한 내용을 공부하며 옮겨적은 글입니다.
    오역이나 잘못 이해한 부분이 있을 수 있으니 틀린 부분은 댓글로 알려주세요 :) 

     

     

    View의 구성

    • 의존성은 view의 input이며, 의존성이 변화할 때 새로운 body를 생성하기 위해 View가 필요하다
    • body는 View의 hierarchy를 구성하는 영역이다.
    • Action은 View의 종석성을 변경하는 트리거이다.

     

    DogView Diagram

    • Button을 클릭하면 개에게 보상을 주기위한 action을 전달한다.
    • 그 결과 개가 변경되고 의존성이 변경되었기 때문에 DogView는 새 body를 생성한다.
    • Data Flow에 대한 더 자세한 내용은 Data essentials in SwiftUI WWDC2020 참조

     

    Tree-like View hierarchy

    • View Hierarchy에 초점을 맞추면 View들이 어떻게 tree-like한 구조를 형성하는 지 주목할 필요가 있다.
    • 의존성을 상단에 추가해도 여전히 tree 구조이지만 DogView는 의존성을 갖는 유일한 View가 아니다.
    • SwiftUI에서 각 View는 각자의 의존성 세트를 가질 수 있으며, 하위 뷰가 의존성을 가져도 여전히 tree 구조이다.

     

    Non Tree-like View hierarchy

     

    • 같은 상태나 다른 데이터에 의존하는 여러 개의 View가 생기게 되면 tree-like한 구조가 깨지게 된다

     

    Dependency Graph

    • 이 구조는 SwiftUI가 새로운 body가 필요한 View들만 골라 효율적으로 update할 수 있게 해주므로 중요하다 
    • 특정 의존성에 변경이 발생했을 때 해당 의존성을 가진 View들만 유효하지 않은 상태가 된다.
    • SwiftUI는 새로운 body value를 생성하기 위해 유효하지 않은 각 View의 body를 호출한다.
    • SwiftUI는 유효하지 않은 각 View의 body value들을 인스턴스화 하고 이는 새로운 의존성의 변경을 불러일으킬 수도 있지만 항상 그렇지는 않다.
    • View는 값 타입이므로 SwiftUI는 update가 필요한 하위 뷰만 효율적으로 비교할 수 있기 때문이다.
    • 이는 View의 value가 갖는 short-lived한 특성을 바라보는 또 다른 방법이다.
    • 구조체의 값은 단지 비교를 위해서만 사용되지만 View 자신은 훨씬 더 긴 lifetime을 갖는다.
    • 이를 이용해 유효하지 않은 View의 body value를 인스턴스화 할 때 새롭게 의존성의 변경이 생겨 다른 View의 body가 새로 생성되는 것을 회피할 수 있다.

     

    Dependuncy Graph Summary

    • Identity는 의존성 그래프의 핵심이다
    • 모든 View는 명시적이거나 구조적인 identity를 가지며 이는 SwiftUI가 변경사항들을 올바른 View들로 라우팅하고 효율적으로 UI를 update하는 방법이다.

     

    의존성을 갖는 propertyWrapper 종류

     

    Identifier 안전성

    • View의 lifetime은 identity의 주기와 동일하며 이는 identifier의 안정성이 결정적인 요소임을 뜻한다.
    • 어떤 identifier가 불안정한 상태라면 더 짧은 View lifetime을 야기할 수 있다.
    • 또한 안정적인 identifier를 갖는 것은 SwiftUI가 계속해서 View를 위한 저장공간을 만들지 않아도 되고 의존성 그래프의 갱신을 거치지 않아도 되기 때문에 성능에 도움이 될 수 있다.
    • SwiftUI는 lifetime을 지속되는 저장소를 관리하는데 사용하기에 안정적인 identifier들은 상태의 소실을 회피하기 위해서도 중요하다. 

     

    불안정한 identifier를 사용한 케이스 1 - UUID

    • 새로운 펫을 추가할 때마다 화면 전체가 새로 그려지는 버그 발생
    •  Identifiable의 id 프로퍼티는 Animal이 변경될 때마다 새로운 identifier를 가지기 때문에 전체 화면이 새로 그려지는 것

     

    불안정한 identifier를 사용한 케이스 2 - indices

    • 0번 자리에 새로운 펫을 추가하여도 마지막 인덱스의 데이터가 추가되는 애니메이션이 실행되는 버그발생  
    • 원인은 연산된 random identifire(UUID)와 마찬가지로 indice들은 안정적인 identity 형식이 아니기 때문이다.

     

    안정적인 identifier를 사용하는 케이스

    • 데이터베이스에서 부여된 ID나 pet의 안정적인 프로퍼티로부터 유래한 안정적인 identifier를 쓸 필요가 있다.
    • 어떤 것이든 지속적인 identifier라면 최선의 선택이며 안정적인 identifier를 사용하면 애니메이션이 올바르게 작동한다.

     

    identifier의 유일성

    • 좋은 identifier를 위해 필요한 성질이 안정성만 있는 것은 아니며 유일성(uniqueness)또한 충족해야 한다.
    • 각 identifier는 하나의 단일 View와 짝지어져야 하고 이는 애니메이션이 멋지게 보이고, 원활한 성능과 View hierarchy가 가장 효율적인 형태를 반영함을 보장한다.

     

    유일하지 않은 identifier 사용 예시

    • treats가 같은 종류의 Treat을 복수로 갖게 되면 1개만 표시되는 버그가 발생한다 
    • 원인은 identifier로 사용하는 name 프로퍼티가 유일하지 않기 때문이다.

     

    유일한 identifier 사용 예시

    • Treat에 부여된 serialNumber나 또 다른 Treat 별로 유일한 ID를 사용해야 한다
    • 유일한 ID를 사용하는 것은 같은 타입인 데이터가 여러개 있더라도 모든 올바른 데이터가 보여지는 것을 보장한다.
    • 또한 더 나은 애니메이션과 성능을 보장한다.

     

    명시적 identity 권고사항

    • random identifier를 사용하는 것에 유의해야 한다. 특히 연산 프로퍼티를 조심해야 한다.
    • 일반적으로 모든 identifier들이 안정적이길 의도해야 한다.
    • idnetifier는 시간이 지나도 변경되서는 안 된다. 
    • 새로운 identifier는 새로운 lifetime을 가진 새로운 item을 대표한다.
    • identifier들은 유일할 필요가 있다. 여러 View들이 하나의 identifier를 공유할 수 없다.

     

    구조적 identity 사용 예시

    • branch는 구조적 identity의 형식이며, 이 경우 조건적으로 modify되는 Content의 복사본을 2개 갖고 있다.
    • 이 branch는 modifier의 내부에 있다는 점에 주목할 필요가 있다.
    • 이 경우 조건이 변경되고 Treat가 만료되어 불투명하게 변경되면 branch가 지금의 위치에 있기 때문에 새로운 identity를 갖게 되는 미묘한 문제가 있다.

     

    해결법

    • branch들을 삼항연산자로 중첩시키고 불투명도 수정자 내부로 조건들을 이동하는 방식으로 branch를 제거함으로써 단일 identity를 갖는 올바른 View로 구현할 수 있다.
    • 뿐만 아니라 조건들을 불투명도 수정자 내부로 이동하는 것은 종속 코드의 범위를 엄격하게 지정하기 때문에 성능에 도움이 된다.
    • 위 코드에서는 오직 opacity만이 변경될 필요가 있다.

     

    Inert modifier

    • date가 true일 경우엔 위 코드처럼 불투명도를 1로 설정하는 modifier만 남게 된다.
    • 위 modifier처럼 렌더링 결과에 영향을 미치지 못하는 modifier를 Inert modifiers라고 부른다.
    • SwiftUI의 modifier는 비용이 싸기에 Inert modifier는 inherent cost가 거의 없다
    • 왜냐하면 시각적인 효과가 야기되지 않고, 프레임워크가 효율적으로 Inert modifier를 제거하 비용을 더욱 절감할 수 있기 때문이다.

     

    구조적 identity 권고사항

    • branch는 훌륭하고 SwiftUI에 존재하는 이유가 있다.
    • 하지만 불필요하게 사용될 때 낮은 성능과 뜻밖의 애니메이션, 상태의 소실을 유발할 수 있다.
    • branch를 사용하기 전에 여러 View를 나타내는 건지 동일한 View의 여러가지 상태를 나타내는 것인지 점검해보아야 한다.
    • 단일 View를 식별하기 위해 branch를 사용하는 대신 inert modifier를 사용하는 것이 더 괜찮게 동작하는 경우가 잦다.
    • transformEnvironment: 환경값을 변경하기 위한 modifier

    댓글

All Posts are written by Tunastorm