UI Diffable Data Source란?

ellyheetov·2021년 10월 12일
5
post-thumbnail

DiffableDataSource란?

TableView(또는 CollectionView)를 그리기 위한 데이터를 관리하고 UI를 업데이트 하는 역할을 한다. Data Source와 달리 데이터가 달라진 부분을 추적하여 자연스럽게 UI를 업데이트한다.

간단히 말하자면, DataSource란 TableView를 나타낼 데이터를 구성하고 UI를 업데이트 하는 역할을 한다. (이 글 참고) 기본적으로, Diffable Data Source와 DataSource의 역할은 같다.

그러나, Diffable Data Sources를 사용하면 tableView나 collectionView를 단순하게 업데이트가 가능하다. 이전 테이블과 달라진 부분을 자동으로 알아차리고, 새로운 부분만 다시 그리기 때문이다.

Diffable을 사용하여 얻게 되는 효과/장점

  • 추가적인 코드작업 없이도, 퀄리티 있는 에니메이션 적용이 가능하다.
  • 개선된 Data Source 매커니즘은 완벽하게 동기적인 버그나, 예외, 충돌 상황들을 피할 수 있게 해준다.
  • UI 데이터의 동기화 부분 대신 앱의 동적인 데이터와 내용에 집중할 수 있다.
  • identifiersnapshot을 사용하는 간소화 된 Data 모델을 정의 하고, 이를 이용하여 UI를 업데이트 한다.

위와 같은 효과를 어떻게 얻게 되는지 조금 더 자세히 알아보려고 한다.

이전에는 어떻게 사용했을까?

기존 tableView나 collectionView를 사용하기 위해서는 프로토콜의 세가지 메소드를 반드시 구현해 주어야 한다.

  • seciton의 개수 (the number of sections)
  • 각 section의 아이템의 개수 (the number of items)
  • cellForItemAt - cell의 render (content renders)

위와 같은 코드로 collectionview를 무리없이 사용할 수 있다.

Diffable Data Source가 왜 필요해?

그럼, 이전까지 잘 사용해 왔는데 왜 datasource가 필요한 걸까?

보통 tableView의 데이터를 구성하기 위해 Controller에게 numberOfSection을 물어본다. controller는 데이터를 채우기 위해서 API통신을 요청하고 응답을 받으면 tableView에게 변화를 알린다.

Diffable Data Source는 바로 다음 상황을 해결하기 위한 것이다.

섹션 수가 잘못되어 앱이 종료되는 경우이다. 업데이트 되기 전과 후의 section 수가 하나 이상 차이난다는 말인가? 잘 모르겠다

어쨌든, 해당 에러는 데이터의 변경 상황을 수동으로 관리하고 동기화 해야함을 말하고 있다. 따라서 데이터를 동기화 해주는tableView.reloadData()를 이용하여 해결 할 수 있다.

그렇다면, 다시 질문을 갖게 된다.

🤷🏻‍♀️ 그럼 DiffableDataSource를 사용 필요가 없는데?

reloadData()로 해결하는 것과 Diffable Data Source로 해결하는 것의 차이는 사용자 경험(UX) 에 있다. reloadData()의 경우 TableView를 한번에 업데이트 하므로 뚝 뚝 끊어지는 UI를 보게 된다. 반면, Diffable Data Source를 사용하는 경우 변경된 데이터 부분에 대하여 스르륵 사라지고 추가되는 UI 효과가 적용된다.

앞서 Diffable Data Source를 소개 할 때 "자연스럽게 UI를 업데이트 한다." 라고 설명한 이유이다.

DataSourceDiffableDataSource

달라진 부분을 어떻게 알아차리는 걸까?

결론부터 말하자면 Hash value를 사용한다.

Diffable Data Source를 사용하기 위해서 두가지 generic type을 가진다.

  • section identifier
  • item identifier

이 두 제네릭 변수는 반드시 Hashable해야한다.

class UITableViewDiffableDataSource<SectionIdentifierType, ItemIdentifierType> : NSObject 
  where SectionIdentifierType : Hashable, ItemIdentifierType : Hashable
struct NSDiffableDataSourceSnapshot<SectionIdentifierType, ItemIdentifierType> 
  where SectionIdentifierType : Hashable, ItemIdentifierType : Hashable

Diffable Data Source는 SectionIdentifierType, ItenIdentifierType 두개의 generic parameter로 결정된다. 이 두 파라미터는 반드시 Hashable 해야 한다.

apply시에 각 hash value를 비교하여 추가 or 삭제된 부분을 인지하게 된다.

ex) snapshot 생성 및 적용

var snapshot = NSDiffableDataSourceSnapshot<Int, MyModel>()
snapshot.appendSections(storage.sections)

for section in storage.sections {
  snapshot.appendItems(storage.modelsForSection(section), toSection: section)
}

datasource.apply(snapshot)

Hash Value가 같아서 구별 할 수 없는 경우

다음의 예를 보자.

let itemsBefore = [{ "name": "Donny Wals" }, { "name": "Donny Wals" }]
let itemsAfter = [{ "name": "Donny Wals" }, { "name": "Donny Wals" }]

변화되기 이전의 item과 변화 된 이후에 item이다. 눈으로 보기는 동일하지만, 내부의 두 객체는 순서가 바뀌었을 수도 있다. 확인이 불가능 하지만, 각 객체는 자신의 entity를 가진다.

itemsBefore == itemsAfter // True
itemsBefore.hashValue == itemsAfter.hashValue // True

하지만 Diffable Data Source는 이 둘을 같다고 볼 것 이다. name은 객체를 식별할 수 있는 고유 속성이 아니기 때문이다.

따라서, setion과 item의 identifier는 반드시 unique해야하며 hashable 프로토콜을 따라야 한다.

UUID나 back-end에서 제공하는 identifier를 이용한다면 위와 같은 경우를 막을 수 있다.

ex)

struct Mountain: Hashable {
    let name: String
    let height: Int
    let identifier = UUID()
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(identifier)
    }
}

성능

속도 개선

O(N) (N: the number of item)

모든 아이템의 hash 값을 비교해 보아야 하므로 선형 탐색에 걸리는 시간과 동일하다.

background queue에서 작업 가능

data source의 apply작업이 오래걸린다면, background queue에서 수행하면 된다. background queue에서 이뤄지더라도 안전성을 보장할 수 있기 때문이다. frmaework는 diffable을 계산이 완료되면 메인 큐로 돌아가서 결과를 적용하게 된다.

정리

  • Diffable Data Source란 tableview를 그리기 위한 데이터를 관리하고, 바뀐 부분에 해당하는 UI를 자연스럽게 업데이트 해주는 객체를 말한다.
  • 이때, Hash 값을 이용하여 변화된 부분을 인지한다.
  • Diffable Data Source를 이용하면 코드량이 줄고 UI transition이 추가될 뿐만아니라, 데이터 자동 동기화로 에러를 방지할 수 있다.

참고

profile
 iOS Developer 좋아하는 것만 해도 부족한 시간

2개의 댓글

comment-user-thumbnail
2022년 5월 28일

좋은 글 감사합니다! 글 깔끔하게 작성하시는것 같아요

1개의 답글