iOS) Diffable Data Source ์•Œ์•„๋ณด๊ธฐ

7 minute read

๐Ÿ‘ทDiffable Data Source ๋ž€?

  • ๋จผ์ € Diffable Data Source ๊ฐ€ ๋ฌด์—‡์ธ์ง€์— ๋Œ€ํ•ด์„œ ๊ฐ„๋‹จํ•˜๊ฒŒ ์•Œ๊ณ  ๋„˜์–ด๊ฐ€๋ณด์ž!

TableView(๋˜๋Š” CollectionView)๋ฅผ ๊ทธ๋ฆฌ๊ธฐ ์œ„ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  UI๋ฅผ ์—…๋ฐ์ดํŠธ ํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค. Data Source ์™€ ๋‹ฌ๋ฆฌ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‹ฌ๋ผ์ง„ ๋ถ€๋ถ„์„ ์ถ”์ ํ•˜์—ฌย ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ UI๋ฅผ ์—…๋ฐ์ดํŠธํ•œ๋‹ค.

  • ๊ธฐ๋ณธ์ ์œผ๋กœ, Diffable Data Source ์™€ Data Source ์˜ ์—ญํ• ์€ ๊ฐ™๋‹ค. ๊ทธ๋Ÿฌ๋‚˜, Diffable Data Source ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด table view ๋‚˜ collection view ๋ฅผ ๊ฐ„์†Œํ™”ํ•˜๊ฒŒ ์—…๋ฐ์ดํŠธ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.
  • Data Source ๋Š” Protocol ์ด๋‹ค. ๋ฐ˜๋ฉด์— Diffable Data Source ๋Š” Generic Class์ด๋ฉฐ, ํ•ด๋‹น ํด๋ž˜์Šค๊ฐ€ Data Source ๋ฅผ ์ฑ„ํƒํ•˜๊ณ  ์žˆ๋‹ค.

WWDC

WWDC 2019 > Advances in UI Data Source ์—์„œ ์†Œ๊ฐœ๋˜์—ˆ๊ณ , iOS 13 ๋ถ€ํ„ฐ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  WWDC 2021 > Make blazing fast lists and collection views ์—์„œ diffable data source ์— ๋Œ€ํ•œ ๊ฐœ์„ ์‚ฌํ•ญ์ด ์†Œ๊ฐœ๋˜์—ˆ๋‹ค.

์†Œ๊ฐœ๊ธ€์„ ์‚ดํŽด๋ณด๊ณ  ์ •๋ฆฌํ•ด๋ณด์•˜๋‹ค.

๐Ÿง‘๐Ÿปโ€๐Ÿ’ปย WWDC 2019

  • UI Data Sources ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ž๋™ ๋น„๊ต๋ฅผ ํ†ตํ•ด์„œ table view ์™€ collection view items ์˜ ์—…๋ฐ์ดํŠธ๋ฅผ ๊ฐ„์†Œํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ณ ํ’ˆ์งˆ์˜ ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ์ž๋™์œผ๋กœ ์ด๋ฃจ์–ด์ง€๊ณ , ์ถ”๊ฐ€์ ์€ ์ฝ”๋“œ๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค!
  • ๊ฐœ์„ ๋œ data source ๋ฉ”์ปค๋‹ˆ์ฆ˜์€ ๋™๊ธฐํ™” ๋ฒ„๊ทธ, ์˜ˆ์™ธ ๋ฐ ์ถฉ๋Œ์„ ์™„์ „ํžˆ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค!
  • UI ๋ฐ์ดํ„ฐ ๋™๊ธฐํ™”์˜ ์‚ฌ์†Œํ•œ ๋ถ€๋ถ„ ๋Œ€์‹  ์•ฑ์˜ ๋™์  ๋ฐ์ดํ„ฐ์™€ ์ฝ˜ํ…์ธ ์— ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๋„๋ก identifiers ์™€ snapshots ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฐ„์†Œํ™”๋œ data model ์— ๋Œ€ํ•ด ์•Œ์•„๋ณด์„ธ์š”.

๐Ÿง‘๐Ÿปโ€๐Ÿ’ปย WWDC 2021

  • ์ผ๊ด€๋˜๊ฒŒ ๋ถ€๋“œ๋Ÿฌ์šด scrolling list ๊ทธ๋ฆฌ๊ณ  collection views: ์…€์˜ lifecycle ์„ ํƒ์ƒ‰ํ•˜๊ณ  ํ•ด๋‹น ์ง€์‹์„ ์ ์šฉํ•ด์„œ ๊ฑฐ์นœ ์Šคํฌ๋กค ๋ฐ ๋ˆ„๋ฝ๋œ ํ”„๋ ˆ์ž„์„ ์ œ๊ฑฐํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์šฐ์„ธ์š”.
  • ๋˜ํ•œ ์ตœ์ ํ™”๋œ ์ด๋ฏธ์ง€ ๋กœ๋”ฉ ๋ฐ automatic cell prefetching ์„ ํ†ตํ•ด์„œ ์ „๋ฐ˜์ ์ธ ์Šคํฌ๋กค ๊ฒฝํ—˜์„ ๊ฐœ์„ ํ•˜๊ณ  ๋น„์šฉ์ด ๋งŽ์ด๋“œ๋Š” ์žฅ์• ๋ฅผ ํ”ผํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹คโ€ฆโ€ฆ(์ƒ๋žต)

โ“์ด์ „์— ์‚ฌ์šฉ๋œ UITableView/CollectionViewDataSource ์˜ ์—ญํ• ์€?

  • section ๊ณผ row / item ์˜ ์ˆ˜๋ฅผ ์ œ๊ณต
  • ๊ฐ ํ–‰์— ๋Œ€ํ•œ ์…€์„ ์ œ๊ณต
  • section ์˜ header ์™€ footer ์ œ๊ณต
  • ๋ฐ์ดํ„ฐ ๋ณ€ํ™”์— ๋ฐ˜์‘

๋“ฑ๋“ฑ ์—ญํ• ์„ ํ•˜๊ณ  ์žˆ์—ˆ์–ด์š”! ํ•„์ˆ˜๋กœ ๊ตฌํ˜„ํ•ด์•ผํ•  ๋ฉ”์„œ๋“œ๋„ ์กด์žฌํ–ˆ์—ˆ์ฃ ! collection view ๋กœ ์•Œ์•„๋ณด์ž๊ตฌ์—ฌ

  • collection view ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ”„๋กœํ† ์ฝœ์˜ ๋‘๊ฐ€์ง€ ๋ฉ”์†Œ๋“œ๋ฅผ ๋ฐ˜๋“œ์‹œ ๊ตฌํ˜„ํ•ด์ฃผ์–ด์•ผ ํ–ˆ์–ด์š”!
// MARK: UICollectionViewDataSource

// โœ… ๊ฐ section ์˜ ์•„์ดํ…œ ๊ฐฏ์ˆ˜ ์ œ๊ณต.
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return 1
}
// โœ… ๊ฐ ํ–‰์— ๋Œ€ํ•œ ์…€ ์ œ๊ณต.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? HomeCollectionViewCell else { return UICollectionViewCell() }

    return cell
}

โ“Why?

๊ทธ๋Ÿผ ์™œ? Diffable Data Source ๊ฐ€ ๋“ฑ์žฅํ•œ๊ฑธ๊นŒ์š”?

WWDC 2019 ์—์„œ ์ž˜ ์„ค๋ช…ํ•ด์ฃผ๊ณ  ์žˆ๋Š”๋ฐ์š”. ๊ธฐ์กด Controller ์™€ UI ๊ด€๊ณ„๋ฅผ ์‚ดํŽด๋ณด์ž๊ตฌ์š”.

Controller ๊ฐ€ ์›น์„œ๋น„์Šค ์‘๋‹ต์„ ๋ฐ›๊ณ , ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•ด์š”. ๊ทธ๋ฆฌ๊ณ  UI ์—๊ฒŒ ๋ฐ”๋€Œ์—ˆ๋‹ค๊ณ  ์ „๋‹ฌํ•ด์š”. ๊ทธ๋ฆฌ๊ณ  ์šฐ๋ฆฌ๋Š” ์—๋Ÿฌ๋ฅผ ๋งˆ์ฃผํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  Diffalbe Data Source ๋Š” ๋ฐ”๋กœ ๋‹ค์Œ ์ƒํ™ฉ์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

  • ์„น์…˜ ์ˆ˜๊ฐ€ ์ž˜๋ชป๋˜์–ด ์•ฑ์ด ์ข…๋ฃŒ๋˜๋Š” ๊ฒฝ์šฐ์—์š”! ์œ„์˜ ์—๋Ÿฌ๋Š” ๋ฐ์ดํ„ฐ์˜ ๋ณ€๊ฒฝ ์ƒํ™ฉ์„ ์ˆ˜๋™์œผ๋กœ ๋™๊ธฐํ™”ํ•ด์•ผํ•จ์„ ์˜๋ฏธํ•ด์š”. ๋”ฐ๋ผ์„œ reloadData ๋ฅผ ํ†ตํ•ด์„œ ๋™๊ธฐํ™”ํ•ด์ฃผ์–ด์•ผ ํ•ด์š”.

โ“๋ญ๊ฐ€ ๋ฌธ์ œ์ฃ ?

๊ฐ€์žฅ ํฐ ๋ฌธ์ œ๋Š” UI ์™€ DataSource ์—ญํ• ์„ ํ•˜๋Š” Controller ๊ฐ€ ์‹œ๊ฐ„์ด ์ง€๋‚จ์— ๋”ฐ๋ผ ๋ณ€ํ•˜๋Š” ์ž๊ธฐ๋“ค๋งŒ์˜ ๋ฒ„์ „์ธ truth ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.(own version of the truth)

์ด truth ๊ฐ€ ์„œ๋กœ ๋งž์ง€ ์•Š๊ฒŒ๋˜๋ฉด ์œ„์™€ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋‚˜๋Š”๊ฒƒ์ด์ฃ ! ์ด๋Ÿฌํ•œ ์ ‘๊ทผ๋ฐฉ์‹์€ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ์‰ฝ์Šต๋‹ˆ๋‹ค. centralize ๋œ truth ๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.


์ž ๋‹ค์‹œ ๋Œ์•„๊ฐ€๋ณผ๊ฒŒ์š”!

WWDC ์—์„œ๋„ reloadData ๋ฅผ ํ†ตํ•œ ๋™๊ธฐํ™”๋ฅผ ๊ดœ์ฐฎ๋‹ค๊ณ  ํ•ด์š”. ๊ทธ๋Ÿฌ๋‚˜ ๋ชจ๋‘๊ฐ€ ์•Œ๋‹ค์‹œํ”ผ reloadData ๋ฅผ ํ•˜๊ฒŒ ๋˜๋ฉด ์• ๋‹ˆ๋ฉ”์ด์…˜ ์—†์ด ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ด๋Š” ์‚ฌ์šฉ์ž ๊ฒฝํ—˜(UX) ๋ฅผ ์ €ํ•˜์‹œํ‚จ๋‹ค๊ณ  ํ•ด์š”. ๋Œ€์‹  Diffable Data Source ๋Š” ๋ณ€๊ฒฝ๋œ ๋ฐ์ดํ„ฐ ๋ถ€๋ถ„์— ๋Œ€ํ•ด์„œ ์ž์—ฐ์Šค๋Ÿฌ์šด ์—…๋ฐ์ดํŠธ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์œ„์—์„œ ๊ทธ๋ ‡๊ฒŒ ๊ณ ํ’ˆ์งˆ์˜ ์• ๋‹ˆ๋ฉ”์ด์…˜, ์Šคํฌ๋กค ๊ฒฝํ—˜ ๊ฐœ์„ ์— ๋Œ€ํ•ด์„œ ๊ฐ•์กฐํ•œ ๊ฒƒ์ด์ฃ .

  • data source

์ถœ์ฒ˜: https://velog.io/@ellyheetov/UI-Diffable-Data-Source

  • diffable data source

์ถœ์ฒ˜: https://velog.io/@ellyheetov/UI-Diffable-Data-Source

โ“๊ทธ๋ ‡๋‹ค๋ฉด ๊ธฐ์กด์—๋Š” ์ž์—ฐ์Šค๋Ÿฌ์šด ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ์—†์—ˆ๋‚˜์š”?

  • ๊ธฐ์กด์€ ํŠน์ • cell๋งŒ ๋ฐ”๋€๊ฒฝ์šฐ์˜ ์ฒ˜๋ฆฌ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ์ฒ˜๋ฆฌํ–ˆ์–ด์š”.
    • ๋ฐฉ๋ฒ• 1) performBatchUpdates()์˜ ํด๋กœ์ € ๋ธ”๋ก์— ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์ž‘์—… ์ถ”๊ฐ€.(insert, delete, reload, move ์—ฐ์‚ฐ์„ ๊ทธ๋ฃน์œผ๋กœ ๋ฌถ์–ด์„œ animate ํ•œ๋‹ค.)
    • ๋ฐฉ๋ฒ• 2) beginUpdates()์™€ endupdates() ์‚ฌ์ด์— ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์ž‘์—… ์ž‘์„ฑ.(์ผ๋ถ€๋ถ„๋งŒ์„ ๋ณ€ํ™”์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•. ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋ธ”๋ก์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.)

โ“Diffable DataSource ์—์„œ๋Š”?

  • iOS 13+ ์—์„œ๋Š” apply()) ๋ฅผ ํ†ตํ•ด์„œ ์œ„ ์ž‘์—…๋“ค์„ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅํ•ด์š”!

โœจย **apply(_:animatingDifferences:completion:)**

  • snapshot ์˜ ๋ฐ์ดํ„ฐ ์ƒํƒœ๋ฅผ ๋ฐ˜์˜ํ•˜๋„๋ก UI ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๊ณ , ์„ ํƒ์ ์œผ๋กœ UI ๋ณ€๊ฒฝ์‚ฌํ•ญ์— ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ ์šฉํ•˜๊ณ  completion handler ๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.

Parameter

  • snapshot: table / collection view ์—์„œ ๋ฐ์ดํ„ฐ์˜ ์ƒˆ ์ƒํƒœ๋ฅผ ๋ฐ˜์˜ํ•˜๋Š” snapshot.
  • animatingDifferences: true ์ธ ๊ฒฝ์šฐ, ์‹œ์Šคํ…œ์€ table / collection view ์— ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ๋ฅผ ์• ๋‹ˆ๋ฉ”์ด์…˜์œผ๋กœ ๋งŒ๋“ ๋‹ค. false ์ธ ๊ฒฝ์šฐ, ์‹œ์Šคํ…œ์€ ์—…๋ฐ์ดํŠธ๋ฅผ ์• ๋‹ˆ๋ฉ”์ด์…˜ํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • completion: ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ์™„๋ฃŒ๋˜๋ฉด ์‹คํ–‰ํ•  ํด๋กœ์ €. ์‹œ์Šคํ…œ์€ main queue ์—์„œ ์ด ํด๋กœ์ €๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

โœจย Snapshot

  • Snapshot ์€ ๊ฐ„๋‹จํžˆ ๋งํ•ด์„œ ํ˜„์žฌ UI state ์˜ truth ์ž…๋‹ˆ๋‹ค.
  • section ๊ณผ item ์— ๋Œ€ํ•ด unique identifiers ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
  • IndexPath ๊ฐ€ ์•„๋‹ˆ๋ผ unique identifiers ๋กœ ์—…๋ฐ์ดํŠธํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

โ—๏ธ์˜ˆ์ œ๋ฅผ ํ†ตํ•ด์„œ ์•Œ์•„๋ณด์ž๊ตฌ์š”!

  • FOO,ย BAR,ย BIF ๋กœ ๊ตฌ์„ฑ๋œ Cureent Snapshot ์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

  • ๊ทธ ํ›„, Controller๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ด…์‹œ๋‹ค. ์ฆ‰, apply() ํ•  ์ˆ˜ ์žˆ๋Š” ์ƒˆ๋กœ์šด Snapshot ์ด ์ƒ๊ธด๊ฑฐ์ฃ .


๐Ÿ‘ท์‚ฌ์šฉํ•ด๋ณด์ž!

1๏ธโƒฃย UICollectionViewDiffalbeDataSource ์•Œ์•„๋ณด๊ธฐ

  • Table View ๋„ ๊ฑฐ์˜ ๋™์ผํ•˜๊ธฐ๋•Œ๋ฌธ์— UICollectionView ๋กœ ์ง„ํ–‰ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค!
  • ๊ฐœ๋ฐœ์ž๋ฌธ์„œ๋ฅผ ์‚ดํŽด๋ด…์‹œ๋‹ค!

โœจย UICollectionViewDiffableDataSource

๋ฐ์ดํ„ฐ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  collection view ์— ๋Œ€ํ•œ cells ์„ ์ œ๊ณตํ•˜๋Š”๋ฐ ์‚ฌ์šฉํ•˜๋Š” ๊ฐœ์ฒด์ž…๋‹ˆ๋‹ค.

Declaration

@MainActorย classย UICollectionViewDiffableDataSource<SectionIdentifierType,ย ItemIdentifierType> :ย [NSObject](https://developer.apple.com/documentation/objectivec/nsobject)ย whereย SectionIdentifierTypeย :ย [Hashable](https://developer.apple.com/documentation/swift/hashable),ย ItemIdentifierTypeย :ย [Hashable](https://developer.apple.com/documentation/swift/hashable)

Overview

diffable data source ๊ฐœ์ฒด๋Š” collection view ๊ฐœ์ฒด์™€ ํ•จ๊ป˜ ์ž‘๋™ํ•˜๋Š” ํŠน์ˆ˜ํ•œ ์œ ํ˜•์˜ data source ์ž…๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ์™€ UI ์— ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ๋ฅผ ๊ฐ„๋‹จํ•˜๊ณ , ํšจ์œจ์ ์ธ ๋ฐฉ์‹์œผ๋กœ ๊ด€๋ฆฌํ•˜๋Š๋ฐ ํ•„์š”ํ•œ ๋™์ž‘์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ UICollectionViewDataSource ํ”„๋กœํ† ์ฝœ์„ ์ค€์ˆ˜ํ•˜๊ณ  ํ”„๋กœํ† ์ฝœ์˜ ๋ชจ๋“  ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•œ ๊ตฌํ˜„์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

๊ตฌํ˜„ ์ˆœ์„œ

  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๏ธโƒฃ

diffable data source ๋ฅผ collection view ์™€ ์—ฐ๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” init(collectionView:cellProvider:) ์ด๋‹ˆ์…œ๋ผ์ด์ €๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋งŒ๋“ค์–ด์ฃผ๊ณ , data source ์™€ ์—ฐ๊ฒฐํ•˜๋ ค๋Š” collection view ๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

2๏ธโƒฃ

๋˜ํ•œ ๊ฐ ์…€์„ ๊ตฌ์„ฑํ•˜์—ฌ UI ์— ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œ์‹œํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๊ฒฐ์ •ํ•˜๋Š” cell provider ๋ฅผ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

dataSource = UICollectionViewDiffableDataSource<Int, UUID>(collectionView: collectionView) {
    (collectionView: UICollectionView, indexPath: IndexPath, itemIdentifier: UUID) -> UICollectionViewCell? in
    // Configure and return cell.
}

3๏ธโƒฃ

๊ทธ๋Ÿฐ ๋‹ค์Œ, Snapshot ์„ ๊ตฌ์„ฑํ•˜๊ณ  ์ ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ์˜ current state ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  UI ์— ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œ์‹œํ•œ๋‹ค. ๋” ๋งŽ์€ ๋‚ด์šฉ์€ NSDiffableDataSourceSnapshot ๋ฅผ ์ฐธ๊ณ ํ•˜์‹ญ์‹œ์˜ค.

๐Ÿšง Important - diffable data source ๋กœ ๊ตฌ์„ฑํ•œ ํ›„ collection view ์—์„œ dataSource ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ๋งˆ์„ธ์š”.

  • ๋งŒ์•ฝ collection view ๊ฐ€ ์ดˆ๊ธฐ ๊ตฌ์„ฑํ•œ ํ›„ ์ƒˆ๋กœ์šด data source ๋ฅผ ํ•„์š”ํ•œ๋‹ค๋ฉด, ์ƒˆ collection view ์™€ diffable data source ๋ฅผ ๋งŒ๋“ค๊ณ  ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.

โ“๋‹ฌ๋ผ์ง„ ๋ถ€๋ถ„์„ ์–ด๋–ป๊ฒŒ ์•Œ์•„์ฐจ๋ฆฌ๋Š” ๊ฑธ๊นŒ์š”?

๊ฒฐ๋ก ๋ถ€ํ„ฐ ๋งํ•˜์ž๋ฉด Hash Value ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

Diffable Data Source ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋‹ค์Œ ๋‘๊ฐ€์ง€ generic type ์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค.

  • section identifier
  • item identifier
// Diffalbe Data Source
@MainActorย classย UICollectionViewDiffableDataSource<SectionIdentifierType,ย ItemIdentifierType> :ย [NSObject](https://developer.apple.com/documentation/objectivec/nsobject)ย whereย SectionIdentifierTypeย :ย [Hashable](https://developer.apple.com/documentation/swift/hashable),ย ItemIdentifierTypeย :ย [Hashable](https://developer.apple.com/documentation/swift/hashable)
// Snapshot
struct NSDiffableDataSourceSnapshot<SectionIdentifierType, ItemIdentifierType> where SectionIdentifierType : Hashable, ItemIdentifierType : Hashable

SectionIdentifierType,ย ItemIdentifierTypeย ๋‘๊ฐœ์˜ generic parameter๋กœ ๊ฒฐ์ •๋ฉ๋‹ˆ๋‹ค.

์ž๋ฃŒํ˜•์„ ๋ณด๋‹ค์‹œํ”ผ ๋‘ ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ๋ฐ˜๋“œ์‹œย Hashableย ํ•ด์•ผ ํ•ด์š”. Hashalbe ํ•˜๊ธฐ ๋•Œ๋ฌธ์— apply ํ• ๋•Œ ๋น„๊ตํ•ด์„œ ์ถ”๊ฐ€ ๋˜๋Š” ์‚ญ์ œ๋œ ๋ถ€๋ถ„์„ ์•Œ์•„์ฐจ๋ฆฌ๋„๋ก ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

2๏ธโƒฃย ์ฝ”๋“œ๋ฅผ ์งœ๋ณด์ž!

1. Connect a diffable data source to your collection view.

DiffableDataSource ๋Š” ํ”„๋กœํ† ์ฝœ์ด ์•„๋‹ˆ๋ผ Generic Class ๋ผ๊ณ  ํ–ˆ์Šต๋‹ˆ๋‹ค.

@MainActorย classย UICollectionViewDiffableDataSource<SectionIdentifierType,ย ItemIdentifierType> :ย [NSObject](https://developer.apple.com/documentation/objectivec/nsobject)ย whereย SectionIdentifierTypeย :ย [Hashable](https://developer.apple.com/documentation/swift/hashable),ย ItemIdentifierTypeย :ย [Hashable](https://developer.apple.com/documentation/swift/hashable)
  • ์ด๋‹ˆ์…œ๋ผ์ด์ €๋ฅผ ํ†ตํ•ด์„œ ๋งŒ๋“ค์–ด์ฃผ๊ณ  ์ „๋‹ฌํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
@MainActor init(collectionView: UICollectionView, cellProvider: @escaping UICollectionViewDiffableDataSource<SectionIdentifierType, ItemIdentifierType>.CellProvider)

7

Generic type ๋ถ€ํ„ฐ ์ง€์ •ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. SectionIdentifierType ๊ณผ ItemIdentifierType ์„ ์„ค์ •ํ•˜๋ฉด๋ฉ๋‹ˆ๋‹ค.

๐Ÿ’ซย SectionIdentifierType

๋งŽ์€ ์˜ˆ์ œ๋ฅผ ๋ณด๋‹ˆ Int ํ˜น์€ CaseIterable ์„ ์ฑ„ํƒํ•˜๋Š” enum ์„ ์‚ฌ์šฉํ•˜๋”๋ผ๊ตฌ์š”. ๋‹น์—ฐํ•˜๊ฒŒ๋„ Hashable ๋ฅผ ์ฑ„ํƒํ•ด์„œ ์‚ฌ์šฉํ•ด๋„ ์ข‹์Šต๋‹ˆ๋‹ค!

enum Section: CaseIterable {
    case main
}

8

  • cellProvider๋Š” 3๊ฐœ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. collectionView,ย IndexPath, ItemIdentifierType ์ž…๋‹ˆ๋‹ค.
  • data source ์ฝ”๋“œ๋ฅผ ๋‹ค๋ฅธ๊ณณ์—์„œ๋„ ์‚ฌ์šฉํ•ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ „์—ญ๋ณ€์ˆ˜๋กœ ๋งŒ๋“ค์–ด์„œ ๊ด€๋ฆฌํ•ด์ค๋‹ˆ๋‹ค.
var dataSource: UICollectionViewDiffableDataSource<Section, String>!

//...

self.dataSource = UICollectionViewDiffableDataSource<Section, String>(collectionView: self.collectionView) { collectionView, indexPath, itemIdentifier -> UICollectionViewCell? in
    // code
}

โ“ย  Hashable ์ด๋ผ๊ณ  ํ–ˆ๋Š”๋ฐ CaseIterable ์€ ์™œ ๊ฐ€๋Šฅํ•œ๊ฐ€์š”?

CaseIterable ์— ๋Œ€ํ•ด์„œ ์ž ๊น ์•Œ์•„๋ด์š”!

์™œ ์ฑ„ํƒํ• ๊นŒ์š”? allCases ๋ผ๋Š” ํƒ€์ž…์†์„ฑ์„ ์–ป๊ธฐ ์œ„ํ•จ์ธ๋ฐ์š”. ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ํ”„๋กœํ† ์ฝœ ๊ตฌํ˜„์„ ์ž๋™์œผ๋กœ ์ œ๊ณตํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ฑ„ํƒ ์„ ์–ธ๋งŒ ํ•ด์ฃผ๋ฉด ๋ฐ”๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”!

allCases์˜ ํƒ€์ž…์€ ํ”„๋กœํ† ์ฝœ์˜ ์„ ์–ธ์„ ๋”ฐ๋ผ enum ์ž์‹ ์„ ์›์†Œ๋กœ ๊ฐ€์ง€๋Š” Collection ํƒ€์ž…์œผ๋กœ ์ œํ•œ๋ฉ๋‹ˆ๋‹ค.

allCases ๋Š” assoicated value ๊ฐ€ ํ•˜๋‚˜๋ผ๋„ ์žˆ๋‹ค๋ฉด ์ž๋™์œผ๋กœ allCases ํ”„๋กœํผํ‹ฐ๋ฅผ ๋งŒ๋“ค์–ด์ค„ ์ˆ˜ ์—†์–ด์š”.

Associated Value์˜ ๊ฐ’์ด ๋‹ค๋ฅธ ๊ฒฝ์šฐ๋ฅผ ๊ฐ™์€ case๋กœ ์ทจ๊ธ‰ํ•ด์•ผ ํ•˜๋Š”์ง€, ๋‹ค๋ฅธ case๋กœ ์ทจ๊ธ‰ํ•ด์•ผํ•˜๋Š”์ง€ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ํŒ๋‹จํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ด ๊ฒฝ์šฐ๋Š” ์ง์ ‘ allCases ํ”„๋กœํผํ‹ฐ๋ฅผ ์ œ๊ณตํ•ด์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Hashable ์„ ์ž ๊น ์‚ดํŽด๋ณผ๊นŒ์š”!

Hashable ์€ associated value ์—†์ดย enum ์„ ์ •์˜ํ•˜๋ฉด ์ž๋™์œผ๋กœย Hashable ์„ ์ค€์ˆ˜ํ•ฉ๋‹ˆ๋‹ค.

์ฆ‰, associated value ์—†๋Š” enum ์€ ์ž๋™์œผ๋กœ Hashable ์„ ์ค€์ˆ˜ํ•˜๊ณ , CaseIterable ์„ ์ฑ„ํƒํ•ด์„œ ์ž๋™์œผ๋กœ allCasese ๋ผ๋Š” ํƒ€์ž…์†์„ฑ์„ ์–ป์—ˆ๋‹ค๋Š”๊ฒƒ์€ associated value ๊ฐ€ ์—†๋‹ค๋ผ๋Š” ๊ฒƒ์ด๋‹ˆ๊นŒ Hashable ์„ ์ค€์ˆ˜ํ•˜๋Š” ๊ฒƒ์ด์ง€์š”!

๊ทธ๋ž˜์„œ CaseIterable ์„ ์ฑ„ํƒํ•˜๋Š” ์˜ˆ์ œ๋„ ์„ฑ๊ณต์ ์œผ๋กœ ๊ตฌํ˜„์ด ๊ฐ€๋Šฅํ•œ ๊ฒƒ์ด๋ž๋‹ˆ๋‹ค!

  • ์ฐธ๊ณ :

[Swift] CaseIterable ์ด๋ž€?

Hashable


๐Ÿ’ซย ItemIdentifierType

๋ณด์—ฌ์ค„ item ์ด String ์ด๋ผ๊ณ  ๊ฐ€์ •ํ•˜๊ณ  ๋„ฃ์–ด์ฃผ๊ฒ ์Šต๋‹ˆ๋‹ค.

  • ์ด๋•Œ ๊ฐ™์€ ๊ฐ’์ด ์ค‘๋ณต์œผ๋กœ ๋“ค์–ด๊ฐ€๊ฒŒ ๋˜๋ฉด ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค! ๊ทธ๋ž˜์„œ ์ค‘๋ณต๋œ ๊ฐ’์„ ์ฒ˜๋ฆฌํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

โ“์ค‘๋ณต๋œ ๊ฐ’ ์ฒ˜๋ฆฌ

1๏ธโƒฃย 

UUID ๋ฅผ ํ™œ์šฉํ•œ ํ”„๋กœํผํ‹ฐ ์ •์˜

struct ItemName: Hashable {
    let id = UUID()
    var name: String
}

2๏ธโƒฃย 

ItemIdentifierType ๋ณ€๊ฒฝ

// var dataSource: UICollectionViewDiffableDataSource<Section, String>!
// ๋ณ€๊ฒฝ.
var dataSource: UICollectionViewDiffableDataSource<Section, ItemName>!

2. Implement a cell provider to configure your collection viewโ€™s cells.

  • cell ์„ ๋งŒ๋“ค๊ณ , cell ์— ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ์–ด์ค€๋‹ค.
// cell ์ด ํ•ด๋‹น collection view ์— register ๋œ ์ƒํƒœ์—ฌ์•ผ ํ•œ๋‹ค.
collectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: "cell")

self.dataSource = UICollectionViewDiffableDataSource<Section, String>(collectionView: self.collectionView) { collectionView, indexPath, itemIdentifier -> UICollectionViewCell? in
    guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? CollectionViewCell else { return UICollectionViewCell() }
    // โœ… cell ์ดˆ๊ธฐํ™”.
    cell.initCell(itemIdentifier)
    return cell
}

collectionView.dataSource = dataSource

3. Generate the current state of the data.

  • Snapshot ์„ ๊ตฌ์„ฑํ•ด๋ณด์ž.
// โœ… ํ…์ŠคํŠธ์— ๋”ฐ๋ผ์„œ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๊ฐ€ ๋“ฑ์žฅํ•˜๋Š” ๋กœ์ง.
func performQuery(with filter: String?) {
// โœ… ํ…์ŠคํŠธ๊ฐ€ ๋“ค์–ด์žˆ๋Š” arr ์—์„œ filter ๋กœ ์‹œ์ž‘ํ•˜๋Š” ํ…์ŠคํŠธ๋“ค์„ ์ €์žฅ.
    let filtered = self.arr.filter { $0.hasPrefix(filter ?? "") }
// โœ… Snpapshot ์ƒ์„ฑ.
    var snapshot = NSDiffableDataSourceSnapshot<Section, String>()
// โœ… section ๋ฐ item ์ถ”๊ฐ€.
    snapshot.appendSections([.main])
    snapshot.appendItems(filtered)
// โœ… apply ๋ฅผ ํ†ตํ•ด์„œ snapshot ๊ณผ animatingDifferences ํŒŒ๋ผ๋ฏธํ„ฐ ์ „๋‹ฌ.
// โœ… ์—ฌ๊ธฐ์„œ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ dataSource ๋ฅผ ์ „์—ญ์„ ์–ธํ•จ.
    self.dataSource.apply(snapshot, animatingDifferences: true)
}

โ—๏ธapply snapshot without animation

์œ„์—์„œ ์‚ฌ์šฉํ•œ apply(_:animatingDifferences:completion:) ๋ฉ”์„œ๋“œ์˜ ๋™์ž‘์ด iOS 15 ๋ถ€ํ„ฐ ๋ณ€๊ฒฝ๋˜์—ˆ๋‹ค.

  • animatingDifferences ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ true ์ด๋ฉด ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ์ ์šฉ๋˜๊ณ , false ๋ฉด ๋‚ด๋ถ€์ ์œผ๋กœ reloadData ๋กœ ๋ฐ”๋€Œ์–ด์„œ ๋™์ž‘ํ–ˆ์—ˆ๋‹ค.

iOS 15 ๋ถ€ํ„ฐ

  • animatingDifferences ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ false ๋ฅผ ๋„˜๊ธฐ๋Š” ๊ฒฝ์šฐ๋„ ๋ฐ”๋€ ๋ถ€๋ถ„๋งŒ ์ ์šฉํ•˜๊ณ  reloadData ๋กœ ๋ฐ”๋€Œ์–ด ๋™์ž‘ํ•˜์ง€ ์•Š๋Š”๋‹ค๊ณ  ํ•œ๋‹ค.
  • ๊ทธ๋ฆฌ๊ณ  applySnapshotUsingReloadData(_:completion:) ๋ฉ”์„œ๋“œ๊ฐ€ ์ถ”๊ฐ€๋˜์–ด์„œ ๋ฐ”๋€ ๋ถ€๋ถ„๋งŒ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ reload ํ•˜๊ณ  ์‹ถ์œผ๋ฉด ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

4. Display the data in the UI

์œ„์—์„œ ์ž‘์„ฑํ•œ performQuery(with) ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด์„œ UI ์ด ๊ฐฑ์‹ ์ด ํ•„์š”ํ•œ ์ž‘์—…๋งˆ๋‹ค snapshot ๊ณผ ํ•จ๊ป˜ apply() ๋ฅผ ํ˜ธ์ถœํ•ด์„œ UI ๋ฅผ ๊ฐฑ์‹ ํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค!


Categories:

Updated: