[UIKit] Diffable Datasource

찬솔·2024년 1월 22일
0
post-thumbnail
post-custom-banner

애플의 WWDC19에서 iOS 13부터 도입된 새로운 데이터 소스 UICollectionViewDiffableDataSource를 발표하였다.

Before

이전에는 UICollectionViewDataSource 프로토콜을 채택하여 UICollectionView의 데이터 소스를 관리했다.
이 방법은 간단하고 유연하다.보통 Controller 가 데이터 받아와서, UI 에게 변경을 알려줌.

Why?

- 😵‍💫 복잡한 구현

최근의 앱들은 점점 복잡해지고, 기존에 사용하던 UIViewFlowLayout으로는 구현하기 어려운 부분이 생겨서 CustomLayout으로 관리를 해줘야 했다.

점점 복잡한 구현이 생기면서, 기존방식 사용시 사진과 같은 이슈가 생길때가 있는데,
Controller, UI 가 들고 있는 데이터 사이에서 일치 하지 않을때, 앱에서는 어느것이 더 맞는 데이터인지 판단하기 어렵다.

Single Source Of Truth Data 의 필요성

기존의 구현 방식에서 어떤 데이터(Controller, UI가 각각 데이터를 들고 있음)가 참인지 알기 어려웠다.
따라서, 근본적인 문제 해결 방식은 참인 데이터를 한개만 두도록 함 → Single Source of Truth
그렇게 제안된 방법이 Diffable Datasource 이다.
위 사진 처럼 performBatchUpdates() 라는 메소드를 기존 CollectionView에서 사용했는데, Diffable Datasource에서는 apply() 메소드만 사용하면 된다.

  • 기존방식보다 에러가 덜 나오고 쉽게 적용 할 수 있다.

Snapshot

Snapshot이라는 개념도 같이 도입 되었다.
한가지 참인 데이터를 관리하는 객체인데,
기존에 쓰던 Indexpath 를 쓰지 않고, section과 item에 대해 Unique identifiers로 업데이트 한다.
-> apply()하면 새로운 snapshot이 적용된다.

Code

//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])

Diffable Datasource

UICollectionViewDiffableDataSource의 정의를 확인해보면,
이를 대략적으로 해석해보면,

SectionIdentifierType 및 ItemIdentifierType이 Hashable 및 Sendable 프로토콜을 채택해야 함
CellProvider: CollectionView, indexPath 및 itemIdentifier를 받아 해당 위치에 대한 셀을 반환.
SupplementaryViewProvider: 콜렉션 뷰, elementKind, indexPath를 받아 해당 위치에 대한 보충 뷰를 반환.
생성자: UICollectionView 및 CellProvider 클로저를 인수로 받아 데이터 소스를 설정.
apply: NSDiffableDataSourceSnapshot을 사용하여 데이터 소스에 변경 사항을 적용.
snapshot: 변경 사항이 포함된 스냅샷
animatingDifferences: 변경 사항을 애니메이션으로 보여줄지 여부 (기본값은 true)
completion: 적용이 완료된 후 실행할 클로저

로 해석 가능하다.

Diffable Datasource를 구현 하는 순서는 다음과 같다.

  1. Connect a diffable data source to your collection view.
  2. Implement a cell provider to configure your collection view's cells.
  3. Generate the current state of the data.
  4. Display the data in the UI.

구현

  1. UICollectionView(UITableView)와 연결할 수 있는 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을 준수하는 타입만 넣어줄 수 있다.

  1. 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
    }
  1. 데이터의 현재 상태를 Snapshot을 이용해 생성
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
snapshot.appendSections([.main])
snapshot.appendItems(list, toSection: .main)
datasource.apply(snapshot)
  1. 최신화된 Snapshot을 기준으로 UI 업데이트.
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
    }

Outro

지금까지 알아본 Diffable Datasource는 여러 장점이 있다.

  • 간단하고 효율적인 데이터 관리
  • 타입 세이프한 인터페이스
  • 애니메이션 지원
  • 다양한 데이터 소스 지원
  • 이미지, 텍스트, 서브뷰 등 다양한 셀 컨텐츠 지원
  • UIKit의 최신 기능과 통합
  • 코드 베이스의 유지보수 용이성
    요즘은 대부분 iOS 13이상을 사용하기 때문에 CollectionView(TableView)를 사용 할 때 꼭 사용하자.
post-custom-banner

0개의 댓글