I have some code which loads in some empty sections and items. Then if there is a network connection some calls are made for the visible cells. I have a button in each section which navigates to another view based on the section type, which uses the indexPath. My problem is that when I remove the fist section the cellProvider doesn't update the indexPath and button navigates to the wrong view. If I scroll however the problem goes away. How do I make sure the indexPath updates? EDIT: It appears that my headerViews are retaining their old indexPath, but the cellProvider correctly updates. The header views are set to the correct indexPath once you scroll away and come back.
private enum RowItemType: Hashable {
case itemTypeOne(collection: MyCollection)
case itemTypeTwo(collection: MyCollection)
case itemTypeThree(collection: OtherCollection)
}
private enum SectionType: Hashable {
case sectionOne
case sectionTwo
case sectionThree
}
class MyCollection: Codable, Hashable, Equatable {
var identifier = UUID()
var foos: [Foo]
init(foos: [Foo] = [Foo]()) {
self.foos = foos
}
// MARK: Equatable
var hash: Int {
var hasher = Hasher()
hasher.combine(identifier)
return hasher.finalize()
}
static func == (lhs: MyCollection, rhs: MyCollection) -> Bool {
lhs.identifier == rhs.identifier
}
func hash(into hasher: inout Hasher) {
hasher.combine(identifier)
}
}
class OtherCollection: Codable, Hashable, Equatable {
var identifier = UUID()
var bars: [Bar]
init(bars: [Bar] = [Bar]()) {
self.bars = bars
}
// MARK: Equatable
var hash: Int {
var hasher = Hasher()
hasher.combine(identifier)
return hasher.finalize()
}
static func == (lhs: OtherCollection, rhs: OtherCollection) -> Bool {
lhs.identifier == rhs.identifier
}
func hash(into hasher: inout Hasher) {
hasher.combine(identifier)
}
}
final class SomeTableViewController: UITableVIewController {
...code....
RELEVENT CODE
private func makeDataSource() -> UITableViewDiffableDataSource<SectionType, RowItemType> {
UITableViewDiffableDataSource(tableView: tableView, cellProvider: { [weak self] tableView, indexPath, item in
guard let self = self else {
return UITableViewCell()
}
guard let marginTableView = tableView as? AutoMarginTableView else {
return UITableViewCell()
}
switch item {
case .itemTypeOne(collection: let collection):
let carousel = self.itemOneCarouCell(from: marginTableView, withSection: .sectionOne, atIndexPath: indexPath)
if !collection.foos.isEmpty {
carousel.configureCell(with: .sectionOne, fooCollection: collection)
self.showViewAllButtonForHeader(collection.foos.count > 3, atIndexPath: indexPath)
} else {
carousel.configureCellForLoading()
self.getFoosTypeOne(collection: collection)
}
return carousel
case .itemTypeTwo(let collection):
let carousel = self.itemTwoCarouCell(from: marginTableView, withSection: .sectionTwo, atIndexPath: indexPath)
if !collection.foos.isEmpty {
carousel.configureCell(with: .sectionTwo, fooCollection: collection)
self.showViewAllButtonForHeader(collection.foos.count > 3, atIndexPath: indexPath)
} else {
carousel.configureCellForLoading()
self.getFoosTypeTwo(collection: collection)
}
return carousel
case .ttemTypeThree(let collection):
let carousel = self.itemThreeCarouCell(from: marginTableView, withSection: .sectionThree, atIndexPath: indexPath)
if !collection.bars.isEmpty {
carousel.configureCell(with: collection)
self.showViewAllButtonForHeader(true, atIndexPath: indexPath)
} else {
carousel.configureCellForLoading()
self.getBars(collection: collection)
}
return carousel
})
}
private func showViewAllButtonForHeader(_ show: Bool, atIndexPath indexPath: IndexPath) {
if let headerView = self.tableView.headerView(forSection: indexPath.section) as? MyTableHeaderView {
UIView.animate(withDuration: 0.5, delay: 0.0, options: .curveEaseInOut, animations: {
headerView.showViewAll(show)
}, completion: nil)
}
}
private func getFoosTypeOne(collection: MyCollection) {
var snapshot = dataSource.snapshot()
if FeatureFlags.isFeatureEnabled && snapshot.sectionIdentifiers.contains(.sectionOne) {
someRepositry.getfoos { result in
if case .success(let foos) = result {
if !foos.isEmpty {
collection.foos = foos
snapshot.reloadItems([.itemTypeOne(collection: collection)])
} else {
if snapshot.sectionIdentifiers.contains(.sectionOne) {
snapshot.deleteSections([.sectionOne])
}
}
self.dataSource.apply(snapshot, animatingDifferences: true)
}
}
}
}
private func getFoosTypeTwo(collection: MyCollection) {
var snapshot = dataSource.snapshot()
if FeatureFlags.isFeatureEnabled && snapshot.sectionIdentifiers.contains(.sectionTwo) {
someRepositry.getDifferentFoos { result in
if case .success(let foos) = result {
if !foos.isEmpty {
collection.foos = foos
snapshot.reloadItems([.itemTypeTwo(collection: collection)])
} else {
if snapshot.sectionIdentifiers.contains(.sectionTwo) {
snapshot.deleteSections([.sectionTwo])
}
}
self.dataSource.apply(snapshot, animatingDifferences: true)
}
}
}
}
private func getBars(collection: OtherCollection) {
var snapshot = dataSource.snapshot()
someService.getSomeCollection { someColl in
DispatchQueue.main.async {
if let someColl = someColl {
if !someColl.bars.isEmpty {
collection.bars = someColl.bars
snapshot.reloadItems([.itemTypeThree(collection: collection)])
} else {
if snapshot.sectionIdentifiers.contains(.sectionThree) {
snapshot.deleteSections([.sectionThree])
}
}
}
self.dataSource.apply(snapshot, animatingDifferences: true)
}
}
}
}
extension SomeTabelViewController: SomeTableHeaderViewDelegate {
func someTableHeaderViewTappedViewAll(_ headerView: SomeTableHeaderView) {
guard headerView.section < dataSource.numberOfSections(in: tableView) else {
return
}
let snapshot = dataSource.snapshot()
let section = snapshot.sectionIdentifiers[headerView.section]
switch section {
case .sectionOne:
if case .itemTypeOne(let collection) = snapshot.itemIdentifiers(inSection: .sectionOne).first, !collection.foos.isEmpty {
let vc = SomeListViewController()
navigationController?.pushViewController(vc, animated: true)
}
case .sectionTwo:
if case .itemTypeTwo(let collection) = snapshot.itemIdentifiers(inSection: .sectionTwo).first, !collection.foos.isEmpty {
let vc = SomeListViewController()
navigationController?.pushViewController(vc, animated: true)
}
case .sectionThree:
if case .itemTypeThree(let collection) = snapshot.itemIdentifiers(inSection: .sectionThree).first, !collection.bars.isEmpty {
let vc = OtherViewController(collecton: collection)
navigationController?.pushViewController(vc, animated: true)
}
}
}