애플의 WWDC19에서 iOS 13부터 도입된 새로운 데이터 소스 UICollectionViewDiffableDataSource
를 발표하였다.
이전에는 UICollectionViewDataSource
프로토콜을 채택하여 UICollectionView
의 데이터 소스를 관리했다.
이 방법은 간단하고 유연하다.보통 Controller 가 데이터 받아와서, UI 에게 변경을 알려줌.
최근의 앱들은 점점 복잡해지고, 기존에 사용하던 UIViewFlowLayout
으로는 구현하기 어려운 부분이 생겨서 CustomLayout
으로 관리를 해줘야 했다.
점점 복잡한 구현이 생기면서, 기존방식 사용시 사진과 같은 이슈가 생길때가 있는데,
Controller, UI 가 들고 있는 데이터 사이에서 일치 하지 않을때, 앱에서는 어느것이 더 맞는 데이터인지 판단하기 어렵다.
기존의 구현 방식에서 어떤 데이터(Controller, UI가 각각 데이터를 들고 있음)가 참인지 알기 어려웠다.
따라서, 근본적인 문제 해결 방식은 참인 데이터를 한개만 두도록 함 → Single Source of Truth
그렇게 제안된 방법이 Diffable Datasource 이다.
위 사진 처럼 performBatchUpdates()
라는 메소드를 기존 CollectionView
에서 사용했는데, Diffable Datasource에서는 apply() 메소드만 사용하면 된다.
Snapshot이라는 개념도 같이 도입 되었다.
한가지 참인 데이터를 관리하는 객체인데,
기존에 쓰던 Indexpath 를 쓰지 않고, section과 item에 대해 Unique identifiers로 업데이트 한다.
-> apply()
하면 새로운 snapshot이 적용된다.
//Empty snapshot
let snapshot = NSDiffableDataSourceSnapshot<Section, UUID>()
//Current data source snapshot copy
let snapshot = dataSource.snapshot()
// Snapshot State
var numberOfItems: Int { get }
var numberOfSections: Int { get }
var sectionIdentifiers: [SectionIdentifierType] { get }
var itemIdentifiers: [ItemIdentifierType] { get }
// Configuring Snapshots
func insertItems(_ identifiers: [ItemIdentifierType],
beforeItem beforeIdentifier: ItemIdentifierType)
func moveItem(_ identifier: ItemIdentifierType,
afterItem toIdentifier: ItemIdentifierType)
func appendItems(_ identifiers: [ItemIdentifierType],
toSection sectionIdentifier: SectionIdentifierType? = nil)
func appendSections(_ identifiers: [SectionIdentifierType])
UICollectionViewDiffableDataSource
의 정의를 확인해보면,
이를 대략적으로 해석해보면,
SectionIdentifierType 및 ItemIdentifierType이 Hashable 및 Sendable 프로토콜을 채택해야 함
CellProvider: CollectionView, indexPath 및 itemIdentifier를 받아 해당 위치에 대한 셀을 반환.
SupplementaryViewProvider: 콜렉션 뷰, elementKind, indexPath를 받아 해당 위치에 대한 보충 뷰를 반환.
생성자: UICollectionView 및 CellProvider 클로저를 인수로 받아 데이터 소스를 설정.
apply: NSDiffableDataSourceSnapshot을 사용하여 데이터 소스에 변경 사항을 적용.
snapshot: 변경 사항이 포함된 스냅샷
animatingDifferences: 변경 사항을 애니메이션으로 보여줄지 여부 (기본값은 true)
completion: 적용이 완료된 후 실행할 클로저
로 해석 가능하다.
Diffable Datasource를 구현 하는 순서는 다음과 같다.
- Connect a diffable data source to your collection view.
- Implement a cell provider to configure your collection view's cells.
- Generate the current state of the data.
- Display the data in the UI.
DiffableDataSource
인스턴스 생성.var datasource: UICollectionViewDiffableDataSource<Section, Item>!
여기서 Section과 Item을 넣어주면 된다.
Section:
enum Section {
case main
}
Item:
struct Framework: Hashable {
let name: String
let imageName: String
let urlString: String
let description: String
}
extension Framework {
static let list = [...중략...]
}
typealias Item = Framework
✅ 여기서 두개의 타입은 Hashable을 준수하는 타입만 넣어줄 수 있다.
CollectionView
의 cell을 구성한다. dequeueReusableCell
을 호출.datasource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView, cellProvider: { collectionView, indexPath, item in
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FrameworkCell", for: indexPath) as? FrameworkCell else {
return nil
}
cell.configure(item)
return cell
})
}
configure():
func configure(_ framework: Framework) {
thumbnailImageView.image = UIImage(named: framework.imageName)
nameLabel.text = framework.name
}
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
snapshot.appendSections([.main])
snapshot.appendItems(list, toSection: .main)
datasource.apply(snapshot)
collectionView.collectionViewLayout = layout()
layout():
private func layout() -> UICollectionViewCompositionalLayout {
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.33), heightDimension: .fractionalWidth(1))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalWidth(0.33))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, repeatingSubitem: item, count: 3)
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)
let layout = UICollectionViewCompositionalLayout(section: section)
return layout
}
지금까지 알아본 Diffable Datasource
는 여러 장점이 있다.