DiffableDataSource

‍csk·2022년 10월 31일
0

Swift

목록 보기
2/6

Apple Documnetation
Sample code

CollectionView 기준으로 작성

기존의 UICollectionViewDataSource

  • 기존에는 UICollectionViewDataSource 프로토콜을 상속해야했다.
  • 이후
    1. 섹션수
    2. 섹션의 아이템 개수
    3. cell 설정
  • 세 가지를 필수로 수행해야한다.

Diffable Data Source : UICollectionViewDataSource

  • iOS13+ 버전에서 사용 가능
    Dffiable Data source = "CollectionView 또는 TableView Cell에 Data를 제공하고, 관리하는 객체"

사용 이유

It provides the behavior you need to manage updates to your collection view’s data and UI in a simple, efficient way.

  1. CollectionView 의 데이터와 UI에 간단하고 효율적으로 데이터를 준다.
  2. 충돌방지가 잘 되어있다.
    • 기존 invalid number of sections 에러를 안 볼 수 있다.
    • 언제 나는가 !
      collectionView.insertItems(at: [ IndexPath(row: 0, section: 0)])
      model.insert("newData.png", at: 0)
      `collectionview.reloadData()
    • 위와 같은 코드로 collectionView 0번째에 데이터를 넣고, 모델에도 0번째 코드를 넣고 reload 한다.
    • Section은 있는데 데이터가 없다 -> 에러
    • 사실 억지다.
  3. .reloadData() 를 통해서 UI를 업데이트 한다면, 뚝뚝 끊긴다.
  4. .reloadData().performBatchUpdates()를 안 해도 된다!
  5. 그래서 Animation을 보는 사용자에게도 좋다!

아무튼 좋다.

SnapShot

  • 사용법을 알기 위해선 일단 snapshot을 알아야 한다.
  • Snapshot : CollectionView의 현재 상태
  • 다음과 같은 형태를 지닌다.
NSDiffableDataSourceSnapshot<SectionIdentifierType, ItemIdentifierType> : 
	@unchecked Sendable where 
      SectionIdentifierType : Hashable,
      SectionIdentifierType : Sendable,
      ItemIdentifierType : Hashable,
      ItemIdentifierType : Sendable 

무슨의미 ?

  1. section -> SectionIdentifierType
  2. item -> ItemIdentifierType

Hashable

  • 데이터가 추가, 삭제되었을 때 CollectionView는 Hash로 처리한다.
  • 그래서 Hashable을 채택해야한다.

Sendable

  1. 값 타입 (struct, enum, tuple...)
  2. Mutable Property가 없는 Reference 타입 (class)
  3. 내부적으로 Statce 를 관리하는 Referece 타입 ...
    • 사실 Actor 들 사이의 데이터 전달 ( 값의 안전을 보장하는 ) 에서 좌주 나오는 개념이라..
      1. 내부적으로 (Lock등을 사용해서) 값의 안전을 보장
      2. final
    • 등의 규칙을 지키면 된다. ( 그냥 struct 또는 enum을 쓸 것 같다. )
  4. @Sendable 마킹을 한 Function, Closure
  • 안전 보장을 위해 지켜야한다.

var snapshot = NSDiffableDataSourceSnapshot<Int, UUID>()

// Populate the snapshot.
snapshot.appendSections([0])
snapshot.appendItems([UUID(), UUID(), UUID()])

// Apply the snapshot.
dataSource.apply(snapshot, animatingDifferences: true)

사용법

  1. create a snapshot (you can start from the current snapshot, or create a new one)
  2. configure it
  3. apply to our data source
  • 이제부터 우리는 Snapshot을 관리하면 된다.

NetFlix Main 화면 만들기

첫화면살짝아래화면

화면 구성

  • CompositionalLayout를 사용하는 CollectionView로 작업할 것이다.
  1. 신작을 표현하는 Section
  2. 왓고리즘, 추적추적 미스터리 등 장르에 따른 영화를 표현하는 Section
  • 두 개의 섹션이 필요하다. -> enum으로 관리
  • 내부 Item 데이터는 Movie라는 모델로 관리

1. create a snapshot (스냅샷 만들어)

    enum Section {
        case newWorkSection
        case genreSection
    }
    
    typealias Item = Movie
    
    private var diffableDatasource: UICollectionViewDiffableDataSource<Section, Item>?

Section이 Hashable한가?

  • enum -> value type -> Hashable

Movie가 Hashable한가?

  • 모른다.
  • 다음과 같은 Movie Model 이라면 가능하다.
struct Movie: Hashable {
  let id: String
  let name: String
  let imagePath: Int
  
  func hash(into hasher: inout Hasher) {
    hasher.combine(self.id.hashValue) // hash를 내부적으로 정의 가능
  }

  static func == (lhs: Self, rhs: Self) -> Bool {
    lhs.hashValue == rhs.hashValue
  }

hashable 프로토콜 상속 후 func hash(into:)를 통해서 직접 지정해도 된다.
-> 서버에서 주는 id 정보로 하면 좋을 것이다.

  • 이런 Moive로는 신작정보를 표시 못한다.
  • 섹션에 따른 item 을 달리 하려면 ? -> enum
enum Item: Hashable {
    case newWork(UUID)
    case movie(MoiveUUID)
}

2. configure it (설정해)

  • 사용할 스냅샷 선언 ( Section Item 을 typealias로 선언해둬서 보기가 좋다. )
    var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
  • 섹션 넣기
    snapshot.appendSections([.newWorkSection, genreSection])
  • 아이템 넣기
snapshot.appendItems(viewModel.newWorks, toSection: .newWorkSection)
snapshot.appendItems(self.viewModel.movies, toSection: genre)

3. apply to our data source ( 우리 데이터 소스에 넣기 )

self.diffableDatasource?.apply(snapshot, animatingDifferences: true)

정말 편하다.

4. Cell은 어떻게 꺼내서 사용?

끝이 아니었다.

diffableDatasource = UICollectionViewDiffableDataSource<Section, Item>(
            collectionView: collectionView,
            cellProvider: { collectionView, indexPath, itemIdentifier in
                if indexPath.section == 0 {
                    guard let cell = collectionView.dequeueReusableCell(
                        withReuseIdentifier: NewWorkCell.identifier,
                        for: indexPath
                    ) as? NewWorkCell else {
                        return UICollectionViewCell()
                    }
                    cell.setData(itemIdentifier)
                    return cell
                } else {
                    guard let cell = collectionView.dequeueReusableCell(
                        withReuseIdentifier: MovieCell.identifier,
                        for: indexPath
                    ) as? MovieCell else {
                        return UICollectionViewCell()
                    }
                    cell.setData(itemIdentifier)
                    return cell
                }
            })

진짜끝

  • 물론 네트워크를 통해 모델을 가져온다면, 비동기 처리가 필요하다.

출처

wwdc 정리 - wwdcnotes
UI Diffable Data Source란? - ellyheetov
Actor (4) - Sendable - Zedd0202

profile
Developer

0개의 댓글