iOS 13+ 부터 UITableViewDataSource
을 대신해 table view에 어떤 cell을, 얼마나(how many), 어느 section에 display 할 지 등을 configure 할 수 있는 UITableViewDiffableDataSource가 지원됩니다.
(UICollectionViewDiffableDataSource도 마찬가지)
기존 UITableViewDataSource를 사용하는 방식과 가장 큰 차이점은 reloadData
, performBatchUpdates
가 없다는 것입니다.
UITableViewDiffableDataSource은 data에 변화가 생길 경우, update 이전과 이후를 비교해서 animate 합니다.
The diffable part of UICollectionViewDiffableDataSource means that whenever you update the items you’re displaying, the collection view will automatically calculate the difference between the updated collection and the one previously shown. This will in turn cause the collection view to animate the changes, such as updates, insertions and deletions.
UITableViewDiffableDataSource를 사용해서 얻는 이점은
- Automatic data change animations: Whenever you add, update or delete data, you can get the data change animation automatically.
- Automatic data synchronization: To utilize collection view’s standard animation without UICollectionViewDiffableDataSource, you’d have to manually manage and synchronize data changes between the collection view and the data source. If you have a misalignment in one of the synchronization operations, you’d see an error like this:
Assertion error because the collection view update is invalid- Reduced code: Overall, you can write less code and benefit from the collection view’s data change animations and data synchronization.
- Hashable section, item
- UICollectionViewDiffableDataSource
- NSDiffableDataSourceSnapshot
1️⃣ Hashable Section type, Item type 선언
UITableViewDiffableDataSource<SectionIdentifierType, ItemIdentifierType> : NSObject where SectionIdentifierType : Hashable, ItemIdentifierType : Hashable
UITableViewDiffableDataSource은 위와 같은 형태의 generic class이므로, section과 item이 될 Hashable한 객체를 선언해야 합니다.
UITableViewDiffableDataSource은 Snapshot 객체를 이용해 data의 변화를 감지하는데, Snapshot이 indexPath 대신 unique identifiers에 의존하기 때문에 section과 item type은 Hashable protocol을 준수하는 타입이어야 합니다.
class Section: Hashable {
var id = UUID()
var title: String
var item: [Dish]
init(title: String, item: [Dish]) {
self.title = title
self.item = item
}
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
static func == (lhs: Section, rhs: Section) -> Bool {
lhs.id == rhs.id
}
}
class Dish: Decodable {
let detailHash: String
let title: String
}
extension Dish: Hashable {
static func == (lhs: Dish, rhs: Dish) -> Bool {
return lhs.detailHash == rhs.detailHash
}
func hash(into hasher: inout Hasher) {
hasher.combine(detailHash)
}
}
2️⃣ UITableViewDiffableDataSource 생성
UITableViewDiffableDataSource
의 initializer로
UITableViewDataSource
의 cellForRowAt:
메서드를 구현하는 것과 유사합니다.func makeDataSource() -> UITableViewDiffableDataSource<Section, Dish> {
let reuseIdentifier = cellReuseIdentifier
return UITableViewDiffableDataSource(
tableView: tableView,
cellProvider: { tableView, indexPath, dish in
let cell = tableView.dequeueReusableCell(
withIdentifier: reuseIdentifier,
for: indexPath)
cell.textLabel?.text = dish.title
return cell
}
)
}
3️⃣ NSDiffableDataSourceSnapshot 생성
NSDiffableDataSourceSnapshot 객체를 통해 dataSource가 data의 변화를 감지합니다.
func updateSnapshot(animatingChange: Bool = false) {
var snapshot = NSDiffableDataSourceSnapshot<Section, Dish>()
snapshot.appendSections(sections)
sections.forEach { section in
snapshot.appendItems(section.item, toSection: section)
}
dataSource.apply(snapshot, animatingDifferences: animatingChange)
}
https://www.appcoda.com/diffable-data-source/
https://www.raywenderlich.com/8241072-ios-tutorial-collection-view-and-diffable-data-source