Skip to content

Instantly share code, notes, and snippets.

@mrpcalcantara
Last active April 5, 2023 20:24
Show Gist options
  • Save mrpcalcantara/310217cce3f3f8b36575f24187d3a36c to your computer and use it in GitHub Desktop.
Save mrpcalcantara/310217cce3f3f8b36575f24187d3a36c to your computer and use it in GitHub Desktop.
Example on how to use the UITableViewDiffableDataSource
//
// SnapshotDiffableDataSource.swift
// testDiffableDataSource
//
// Created by Miguel Alcântara on 22/01/2020.
// Copyright © 2020 Miguel Alcântara. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView! {
didSet {
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellIdentifier)
}
}
@IBOutlet weak var btnRandomize: UIButton!
var diffableDataSource: UITableViewDiffableDataSource<Int, String>!
var currentDataSourceSnapshot: NSDiffableDataSourceSnapshot<Int, String>!
let cellIdentifier = "tableViewCell"
let doNewSnapshot = true
var toggleButton: Bool = true {
didSet { btnRandomize.isEnabled = toggleButton }
}
lazy var dataSourceData = randomizeData()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
setup()
}
private func setup() {
// Initialize Diffable data source
diffableDataSource = UITableViewDiffableDataSource<Int, String>(tableView: tableView, cellProvider: { (tv, indexPath, value) -> UITableViewCell? in
let cell = tv.dequeueReusableCell(withIdentifier: self.cellIdentifier, for: indexPath)
cell.textLabel?.text = value
cell.detailTextLabel?.text = "Cell \(value)"
return cell
})
// Instantiate the new snapshot and append the data to the datasource
addValuesToSnapshot(for: randomizeData())
}
@IBAction func randomize(_ sender: UIButton) {
defer { applySnapshot() }
// Randomize new data
let dataValues = randomizeData()
// Check if snapshot diff should be calculated manually or just reinstantiate the snapshot
guard !doNewSnapshot else {
// Instantiate the new snapshot and append the data to the datasource
return addValuesToSnapshot(for: randomizeData())
}
// Filter the sections to delete
let sectionsToDelete = currentDataSourceSnapshot
.sectionIdentifiers
.filter { !dataValues.keys.contains($0) }
// Filter the items to delete
let itemsToDelete = currentDataSourceSnapshot
.itemIdentifiers
.filter { !dataValues.values.flatMap { $0 }.contains($0) }
// Delete the calculated items and sections from the snapshot
currentDataSourceSnapshot.deleteSections(sectionsToDelete)
currentDataSourceSnapshot.deleteItems(itemsToDelete)
// Filter the sections to append
let sectionsToAppend = dataValues.keys
.filter { !currentDataSourceSnapshot.sectionIdentifiers.contains($0) }
// Append the calculated sections to the snapshot
currentDataSourceSnapshot.appendSections(sectionsToAppend)
// For each entry, check which items are to be appended to the datasource
dataValues.forEach { entry in
let snapshotItems = currentDataSourceSnapshot.itemIdentifiers(inSection: entry.key)
let dataItems = dataValues[entry.key] ?? []
// Filter the new items, that might have been "fetched" from the network or CoreData ( for example ), that are not in the current data source
let itemsToAppend = Array(Set(snapshotItems).symmetricDifference(Set(dataItems)))
// Append the calculated items to the section
currentDataSourceSnapshot.appendItems(itemsToAppend, toSection: entry.key)
}
// Append the calculated items to the section
dataSourceData = dataValues
}
private func addValuesToSnapshot(for values: [Int: [String]]) {
// Initialize Snapshot to apply to the data source
var snapshot = NSDiffableDataSourceSnapshot<Int, String>()
// Append values to the snapshot
snapshot.appendSections( values.keys.map { $0 } )
values.forEach { snapshot.appendItems($0.value, toSection: $0.key) }
// Save the current snapshot state
currentDataSourceSnapshot = snapshot
dataSourceData = values
}
private func applySnapshot() {
// Apply the snapshot to the data source
diffableDataSource.apply(currentDataSourceSnapshot, animatingDifferences: true)
}
// Just randomize some data for the example
private func randomizeData() -> [Int: [String]]{
let keys = (0...Int.random(in: 1...3))
.map { $0 }
let values = keys.map { key -> [String] in
let offset = key + 1
return (offset...offset+Int.random(in: 1...9)).map { "key-\($0)" }
}
var dict = Dictionary<Int, [String]>()
keys.enumerated()
.flatMap { [$0.element: values[$0.offset]] }
.forEach { dict.updateValue($0.value, forKey: $0.key) }
return dict
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment