XLOG

[UIKit] DiffableDataSource 본문

Swift/UIKit

[UIKit] DiffableDataSource

X_PROFIT 2024. 6. 8. 15:15

1. DiffableDataSource 란?

Apple 이 WWDC19 에서 발표한 iOS 13 이상부터 사용이 가능한 UITableView, UICollectionView의 데이터 관리를 쉽게 하기 위한 도구이다.

2. DiffableDataSource 의 특징

  • 선언적 업데이트
    • DiffableDataSource 를 사용하게 되면 사용하고 싶은 데이터를 선언하여 적용할 수 있다.
  • Snapshopt 기반
    • 데이터 소스를 직접 관리하는 것이 아닌 Snapshopt을 활용하여 스냅샷의 변경사항을 단순한 UI 뿐만이 아닌 애니메이션까지 적용한다.
    • 프레임 워크에서 자동으로 비교하여 적용하기에 특별한 관리 없이 최적화된 변화를 적용한다.

3. 공식 문서

  • DiffableDataSource 는 class 객체로 collectionView(tableView)의 데이타와 UI를 간단하고, 효과적적인 관리를 제공해준다.
    • 먼저 CollectionView와 DataSource 를 연결
    • cell config
    • 현재 데이터 상태 생성
    • Display UI
  • 여기서 현재 데이터 상태는 snapShot 을 생성해서 적용한다.

4. 예시 코드
실제 DiffableDataSource 를 이해를 위해 간단한 TodoList project 에 적용을 해보기로 했다.

struct Todo: Identifiable, Equatable, Hashable {
    let id: String
    var title: String
    var deadline: String
    var isCompleted: Bool
}

먼저 Todo List 에 사용할 데이터 구조를 결정해준다.

이젠 DataSource와 SnapShot을 생성한다.

final class TodosViewModel {
    var todos: [Todo] = []
    
    var dataSource: UICollectionViewDiffableDataSource<CollectionViewSection, Todo>!
    
    init(_ collectionView: UICollectionView) {
        self.dataSource = .init(collectionView: collectionView) { (collectionView, indexPath, todo) -> UICollectionViewCell? in
            guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TodoCellViewCollectionViewCell.id, for: indexPath) as? TodoCellViewCollectionViewCell else { return UICollectionViewCell() }
            cell.configuration(todo: todo, delegate: self)
            return cell
        }
        fetchTodos()
    }
    
    func updateView() {
        let filtered = self.todos.filter({ !$0.isCompleted })
        var snapshopt = NSDiffableDataSourceSnapshot<CollectionViewSection, Todo>()
        snapshopt.appendSections([.main])
        snapshopt.appendItems(filtered)
        self.dataSource.apply(snapshopt, animatingDifferences: true)
    }
}

UICollectionViewDiffableDataSource 는 init 시 연결할 UICollectionView 를 받는다. 연결된 CollectionView 에 가지고 있는 snapshot을 기반으로 데이터를 collectionView의 cell에 전달하여 UI를 반영하는 방식이다.

기존 UICollectionViewDataSource protocol의 함수에 정의한것과 거의 비슷한 방식이다. 

해당 코드를 이용하여 Todo 항목의 변화가 있을 때마다 updateView를 호출하여 변경된 snapshot을 datasource 에 apply() 해주었다.

여기서 기존 방식과 차이를 보이는게 reloadData(), reconfigureItems(), performBatchUpdagtes() 를 해주는 것이 아닌 snapShot을 통해 별다른 관리 없이 snapshot의 변화된 부분을 framework에서 알아서 관리를 해준다는 점이다. 여기서 snapShot 은 section 과 해당 section의 해당하는 Item을 받는데, Item 은 Hashable을 준수해야 한다. 여기서 든 생각은 SwiftUI 에서 사용하던 ForEach의 경우에도 Hashable을 준수해야 하는데, state 의 변화를 캐치하는 방법이 비슷한 것이 아닌가 하는 생각이 들었다.

Github 주소: https://github.com/profit0124/TodoListWithUIKit/tree/main