본문 바로가기
IOS🍎/WWDC

[WWDC 2019] Advances in UI Data Sources

by Jouureee 2022. 5. 10.

https://developer.apple.com/videos/play/wwdc2021/10252/

 

Make blazing fast lists and collection views - WWDC21 - Videos - Apple Developer

Build consistently smooth scrolling list and collection views: Explore the lifecycle of a cell and learn how to apply that knowledge to...

developer.apple.com

를 읽기 전에 DiffableDataSource에 대해 이해하고자 Advances in UI Data Sources를 먼저 보게 되었습니다. 공부해보고 싶었던 내용이라 쭉 흐름을 정리해보려고 합니다. 읽고 2021년도에 발표된 성능 개선 세션도 빨리 보고 싶군요 ㅎㅎ

 

UICollectionviewDatasource protocol을 준수할때 아래와 같이 3개의 함수를 구현해야 합니다.

이것은 작성이 간단하고 유연하고 익숙한 방식인데요.

그전에 UI와 Controller가 서로 소통하는 방법에 대해 봐봅시다.

우선, UI layer가 numberOfItemsInSection을 통해 렌더링할 item section과 cell 등을 달라고 합니다. 

 

web service로부터 response를 받아오고 이를 UI에게 알립니다. 그러면 UI는 받아들인 변경 사항을 업데이트 할지 말지 결정하게 되죠.

하지만 업데이트가 잘못된다면 !

​이런 에러가 발생하며 좌절하곤, reloadData()를 사용하게 됩니다.

하지만 realodData()시 애니메이션 효과가 없을 것이며, 사용자 경험을 저하시킬 것입니다.

 

Data source로 행동하는 controller는 truth에 계속해서 변화하는 자신의 고유한 버전을 가지고 있습니다. 그리고 UI도 truth에 자신의 고유한 버전을 가지고 있습니다. UILayer 코드는 항상 sync 상태를 유지 하도록 하는 책임을 가지고 있습니다. 현재는 Centralized truth된 개념이 없기 때문에, sync가 맞지 않아 에러가 발생하게 됩니다. 
그래서 새 접근 방식인 Diffable Datasource를 소개하고자 합니다.

 

performBatchUpdates()와 같은 메소드가 없으며 apply()라는 단일 메소드가 있습니다. apply()는 간단하고 자동으로 diffing합니다. 

그리고 snapshot이라는 개념을 도입하는데요
이것은 UI state의 truth입니다. 그리고 indexpath대신, unique한 identifier를 사용합니다. 

BAR, FOO, BIF는 indentifier로 현재 상태에서 apply() 하여 새 스냅샷으로 업데이트 됩니다. 이로 인해 apply()는 현재 state와 새 state를 알게 되고, 그것을 UI에 업데이트 시킵니다. 

 

이를 위한 클래스는 4가지가 있습니다.

demo를 보며 이를 사용해봅시다. searchBar에 산 이름을 검색하면 아래 collectionView에서 자동으로 filter되는 상황을 구현해볼 것입니다.

 

먼저 searchBar에 text를 입력하는 것을 감지하는 textDidChange 함수입니다. text가 입력되면 performQuery(with: searhText)가 실행되겠죠
performQuery() 메소드를 더 자세히 봅시다. 

우선 mountainsController.filteredMountains(with: filter)를 통해 filtering된 model data 이름 순으로 정렬해 가져옵니다. 
그리고 3가지 스텝을 거칩니다. 
1. NSDiffableDataSourceSnapshot을 생성 합니다. 
2. Snapshot은 초기에 empty입니다. 따라서 원하는 섹션과 항목으로 채우는 것은 우리에게 달려 있습니다. 현재 데모에서 우리는 보여줄 section이 하나이므로 item을 단일 section에 넣어줍니다. 
3. 다음으로 이 업데이트에서 표시하려는 item의 idenfifier를 추가합니다.
4. 그리고 datasource에게 이 snapshot을 apply()하는 메서드를 호출합니다.

 

NSDiffableDataSourceSnapshot은 Generic Class입니다. 그러므로, 우리가 사용하기로 결정한 Section IdentifierType 및 Item IdentifierType에 의해 매개변수화됩니다. 


먼저, SectionIdentifierType을 살펴보겠습니다. 이것은 하나의 섹션만 있는 일반적인 경우에 정말 편리한 기술입니다. section은 열거형으로 section case들을 명시해주기만 하면 됩니다.



Item IdentifierType인 mountains은 model data입니다. Mountain struct는 hasable로 따로 identifier를 명시하지 않아도 되게 합니다. 각각의 mountains는 각각의 unique한 identifier를 가지고 있습니다. 



그리고 datasource를 인식할 수 있도록 configureDataSource() 메서드를 구현해 놓았습니다.


제 깃헙에 예시 코드를 올려 놓았습니다.

https://github.com/spqjf12345/DiffableDataSource

또 다른 예시를 봅시다. wifiSettingViewController입니다.
이번에는 section이 두개 입니다. 첫번째 config 섹션은 와이파이를 끄고 켤 수 있으며, 그 밑에는 연결 할 수 있는 와이파이를 탐지해 동적으로 보여주는 섹션이 있습니다. 



변경 사항이 있을때마다 updateUI() 메서드를 호출해 변화를 감지합니다. 그리고 사용자가 와이파이 버튼을 토클시에도 이 메서드가 호출됩니다.



마찬가지로 snapshot을 생성합니다. 그리고 첫번째 섹션인 config를 추가하고 configItems 아이템을 config 섹션에 추가합니다. 그리고 wifiEnabled 상태값이 true라면 networks 섹션에 networkItems을 넣어줍니다. 
넣어준 snapshot에 apply() 함수를 호출해 datasource를 업데이트 해줍니다. 
아까와 달리 UITableViewDiffalbeDataSource을 생성하는 방법입니다. 언뜻 보기에는 더 복잡해 보이지만 실제로는 다양한 유형의 항목이 있기 때문에 그렇게 보일 뿐입니다.


우리의 마지막 예는 아마도 가장 재미있게 볼 수 있을 것입니다. 여기에 색상 견본으로 표시되는 항목을 표시하는 UICollectionView가 다시 있습니다. 그리고 그것들은 처음에 색상별로 무작위 순서로 되어 있습니다. 정렬 버튼을 탭하면 스펙트럼 순서로 반복적으로 정렬되는 것을 볼 수 있습니다.


이 예제는 업데이트를 구성(construct)하고 커밋(commit)하는 방식이 전 예제와 약간 다릅니다.  정렬 과정을 각 단계마다 볼 수 있는 메서드가 있는데 이 메서드에서 연속적인 새로운 상태를 제공하고 이때마다 snapshopt을 생성하고 apply() 합니다.
performSortStep() 메서드를 봐 봅시다. 반복해서 얘기했듯이 1. Snapshot을 얻고 2. 그것을 populate하고 3. Apply 할 것입니다. 하지만 이번 예제에선 새로운, 비어있는 snapshot을 생성하는 것이 아닌 DiffableDataSource에게 현재의 snapshot을 요청할 것입니다.


updateSnapshot은 현재 datasource를 보여주는 truth로 채워져 있을 것입니다.
 
예전에 봤던 appendItems() 메서드가 있고 추가로 deleteItems를 볼 수 있습니다. 그 외에도 다양한 메서드들이 있습니다. 


그리고 dataSource에 updateSnapshot을 apply 합니다.


그리곤 업데이트된 datasource에 대해 update UI 해주는 코드는 아래와 같습니다.


Consideration
reloadData(), intertItems()과 같은 메서드를 호출하지 않고 오직 apply()만으로 업데이트 합니다.



sanpshot을 생성하는 두가지 방법


더이상 indexPath에 의존하지 않습니다.


아직까지 tableview, collectionviewdatasource를 써온 사람이 처음 diffabledatasource를 공부해본 결과 달라진 점을 고찰해보고자 한다.
여태 tavleview, collectionvie의 indexPath에 의존하여 데이터를 인식하고 UI에 렌더링하였는데 이 작업에 의존도를 낮췄다는 생각이 든다. cell의 삭제 및 수정, 추가 등의 작업이 빈번하게 이뤄질 경우 cell에 개별 indexpath 프로퍼티를 통해 각각의 cell을 구분하였다. 하지만 이제 indexpath가 아닌 model data가 Hashable 프로토콜을 준수하여 모델 데이터가 구별할 identifier를 가지고 있기 때문에 의존도가 낮아질것 같다. 더욱이 마법 메소드(reloadData)를 더 이상 쓰지 않아도 된다는 점이 시스템에 부담이 덜어질 것 같다는 생각이 든다. 성능은 2021 WWDC 영상을 봐야 알겠지만 흐흐 3가지 단계만 지킨다면 controller와 UI 간의 sync를 고려하지 않아도 된다는 점 등등에서 효율적인 개선책이라고 생각 들었다. 


Main queue에서 벗어나 Background queue에서도 안전하게 작동할 수 있도록 고안했다고 하신다 .. !  하지만 Main queue에서만 또는 Background queue에서만 배타적으로 호출해야 한다 안그러면 log 찍힐 것 



추가) Airdrop extension에 사용됨 !!




댓글