[TIL]WWDC20 - Advances in diffable data sources

rbw·2022년 10월 6일
0

TIL

목록 보기
47/98

WWDC 보면서 컬렉션 뷰의 바뀐점 등을 작성해보려고 함니다아

참조

https://developer.apple.com/videos/play/wwdc2020/10045

Advances in diffable data sources

섹션 스냅샷

iOS14에서 추가 되었다. 이름에서 알 수 있듯이 섹션 스냅샷은 UICollectionView의 단일 섹션에 대한 데이터를 캡슐화 합니다. 이것을 추가한 이유로는 두가지가 있습니다.

  1. 데이터 소스가 섹션 크기 청크로 더 잘 구성될 수 있도록 합니다.
  2. 아웃라인 스타일 UI 렌더링을 지원하는데 필요한 계층적 데이터 모델링을 허용합니다. (위 사진 참고)

아웃라인 스타일이란 노션의 토글을 생각하면 될 듯? 셀을 클릭 시 해당 셀 하위의 여러 셀들이 보임, 아래 나오는 확장 상태의 의미도 셀을 클릭해서 하위 셀들이 보이는 상태를 의미함.

struct NSDiffableDataSourceSectionSnapShot<Item: Hashable> {
    func append(_ items: [Item], to parent: Item? = nil)

    func insert(_ items: [Item], before item: Item)
    func insert(_ items: [Item], after item: Item)

    func delete(_ items; [Item])
    
    func expand(_ items: [Item])
    func collapse(_ items: [Item])

    var items: [Items] { get }
    var rootItems: [Items] { get }
    func level(of item: Item) -> Int
}

extension UICollectionViewDiffableDataSource

다음으로 이 새로운 섹션 스냅샷 유형을 사용하기 위해 UICollectionViewDiffableDataSource에 두가지 새로운 API를 추가 하였습니다.

extension UICollectionViewDiffableDataSource<Item, Section> {
    
    func apply(_ snapshot: NSDiffableDataSourceSectionSnapshot<Item>,
               to section: Section,
               animatingDifferences: Bool = true,
               completion: (()-> void)? = nil)

    func snapshot(for section: Section) -> NSDiffableDataSourceSectionSnapshot<Item>
}

스냅샷과 섹션 스냅샷을 함께 사용하는 코드

이제 스냅샷과 섹션 스냅샷을 함께 사용하는 예시 코드를 보겠습니다.

func update(animated: Bool=true) {
    // Add our sections in a specific order
    let sections: [Section] = [.recent, .top, .suggested]
    var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
    snapshot.appendSections(sections)
    dataSource.apply(snapshot, animatingDiffereces: animated)

    // 원하는 섹션 순서를 지정한 후 섹션 스냅샷을 각 섹션에 직접 적용합니다.
    for section in sections {
        let sectionItems = items(for: section)
        var sectionSnapshot = NSDiffableDataSourceSectionSnapshot<Item>()
        sectionSnapshot.append(sectionItems)
        dataSource.apply(sectionSnapshot, to: section, animatingDifferences: animated)
    }

}

확장 상태

struct NSDiffableDataSourcSectionSnapshot<Item: Hashable> {
    func expand(_ items: [Item])
    func collapse(_ items: [Item])
    func isExpanded(_ item: Item) -> Bool
}

표시할 스냅샷을 만들 때 해당 항목의 상위 확장상태를 설정 하여 하위 콘텐츠가 처음에 표시 되는지 여부를 쉽게 결정할 수 있습니다.

그리고, 스냅샷을 쿼리하여 항목이 확장이나 축소되었는지도 확인이 가능합니다. 또 확장 상태를 변경하면 apply 할 때 까지 적용되지는 않습니다.

UICollectionViewDiffableDataSoruce

사용자와의 상호작용을 통해서 변경이 된다면 알림을 받는것이 유용할 때가 있습니다. 이를 지원하기 위해 DiffableDataSource에는 새로운 API를 추가했습니다.

extension UICollectionViewDiffableDataSoruce {
    struct SectionSnapshotHandlers<Item> {
        var shouldExpandItem: ((Item) -> Bool)?
        var willExpandItem: ((Item) -> Bool)?
        var shouldCollapseItem: ((Item) -> Bool)?
        var willCollapseItem: ((Item) -> Bool)?
        var snapshotForExpandingParent: ((Item, NSDiffalbeDataSourceSectionSnapshot<Item>) 
        -> NSDiffalbeDataSourceSectionSnapshot<Item>)?
    }
    var sectionSnapshotHandlers: SectionSnapshotHandlers<Item>
}

그리고, snapshotForExpandingParent를 통해 비용이 많이드는 컨텐츠의 지연 로드도 지원합니다. 이는, 콘텐츠를 가져오는 비용이 많이 들 때 초기 섹션 스냅샷에 로드되는 콘텐츠의 양을 최소화하는데 유용합니다.

따라서 현재 자식 스냅샷의 상태에 따라 해당 콘텐츠를 로드할 수 있습니다.

Reordering Support

Diffable Data Source가 제공하는 발전 중 하나는 컬렉션 뷰의 데이터를 고유한 식별자로 모델링하는 기능입니다. 이 고유한 식별자를 사용한다면, 재정렬 변경사항을 자동으로 커밋할 수 있습니다. 하지만 이것으로 충분하지 않습니다.

사용자가 발생시킨 재정렬 상호작용을 앱에 알려야 합니다. 그렇게 해야 앱의 최종 백업 저장소에 대해 새로운 시각적 순서를 유지할 수 있습니다. 이를 지원하기 위해 새로운 속성이 추가되었습니다.

extension UICollectionViewDiffableDataSource {
    struct ReorderingHandlers {
        var canReorderItem: ((Item) -> Bool)?
        var willReorder: ((NSDiffableDataSourceTransaction<Section, Item>) -> Void)?
        var didReorder: ((NSDiffableDataSourceTransaction<Section, Item>) -> Void)?
    }
    var reorderingHandlers: ReorderingHandlers
}

이 새 API를 통해 재정렬을 활성화하려면, 먼저 canReorderItem을 제공해야 합니다.

재정렬이 끝나면 didReorder 클로저가 호출됩니다. 이는 새로운 유형인 NSDiffableDataSourceTransaction를 반환합니다.

struct NSDiffableDataSourceTransaction<Section, Item> {
    var initialSnapshot: NSDiffableDataSourceSnapshot<Section, Item> { get }
    var finalSnapshot: NSDiffableDataSourceSnapshot<Section, Item> { get }
    var dfifference: CollectionDifference<Item> { get }

    var sectionTransactions: [NSDiffableDataSourceSectionTransaction<Section, Item>] { get }
}

struct NSDiffableDataSourceSectionTransaction<Section, Item> {
    var sectionIdentifier: Section { get }

    var initialSnapshot: NSDiffableDataSourceSectionSnapshot<Section, Item> { get }
    var finalSnapshot: NSDiffableDataSourceSectionSnapshot<Section, Item> { get }
    var dfifference: CollectionDifference<Item> { get }
}

트랜잭션은 DiffableDataSource에 대해 수행되는 업데이트에 관한 정보를 제공합니다.

  • initialSnapshot은 업데이트 전의 DiffableDataSource의 상태입니다.
  • finalSnapshot은 업데이트가 적용된 DiffableDataSource의 상태입니다.
  • difference는 앱의 진짜 데이터(source of truth)에 배열 같은 데이터 타입이 있는 경우에 해당 CollectionDifference를 직접 적용이 가능합니다.
  • sectionTransactions은 재정렬 업데이트와 관련된 섹션별 세부 정보를 제공하는 섹션 트랜잭션 목록이 표시됩니다.

예시코드

dataSource.reorderingHandlers.didReorder = { [weak self] transaction in
    guard let self = self else { return }
    
    if let updatedBackingStore = self.backingStore.applying(transaction.difference) {
        self.backingStore = updatedBackingStore
    }
}

트랜잭션과 함께 제공된 CollectionDifference를 사용하여 새로운 백업 저장소를 만들고 데이터(source of truth)를 직접 업데이트합니다.

profile
hi there 👋

2개의 댓글

comment-user-thumbnail
2022년 10월 10일

혹시 무테이팅에 대해서도 알려주실 수 있나요?

1개의 답글