Skip to content

Commit

Permalink
feat(TableViewDriver): allow lightweightDiffing (#219)
Browse files Browse the repository at this point in the history
  • Loading branch information
nickell-andrew authored Nov 16, 2023
1 parent 1c04800 commit c996bf7
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 31 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ The changelog for `ReactiveLists`. Also see the [releases](https://github.com/pl
------

NEXT
----
0.8.3
-----
- Added `lightweightDiffing` option to `TableViewDriver`

----
0.8.2
-----
Expand Down
2 changes: 1 addition & 1 deletion ReactiveLists.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "ReactiveLists"
s.version = "0.8.2"
s.version = "0.8.3"

s.summary = "React-like API for UITableView and UICollectionView"
s.homepage = "https://github.com/plangrid/ReactiveLists"
Expand Down
83 changes: 53 additions & 30 deletions Sources/TableViewDriver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ open class TableViewDriver: NSObject {

private let _automaticDiffingEnabled: Bool

private let _lightweightDiffing: Bool

/// Initializes a data source that drives a `UITableView` based on a `TableViewModel`.
///
/// - Parameters:
Expand All @@ -82,14 +84,19 @@ open class TableViewDriver: NSObject {
/// - automaticDiffingEnabled: defines whether or not this data source updates the table
/// view automatically when cells/sections are moved/inserted/deleted.
/// Defaults to `true`.
/// - lightweightDiffing: when enabled, simply diff the count of rows rather than generating full changesets.
/// Defaults to `false`.
public init(
tableView: UITableView,
tableViewModel: TableViewModel? = nil,
shouldDeselectUponSelection: Bool = true,
automaticDiffingEnabled: Bool = true) {
automaticDiffingEnabled: Bool = true,
lightweightDiffing: Bool = false
) {
self._tableViewModel = tableViewModel
self.tableView = tableView
self._automaticDiffingEnabled = automaticDiffingEnabled
self._lightweightDiffing = lightweightDiffing
self._shouldDeselectUponSelection = shouldDeselectUponSelection
super.init()
tableView.dataSource = self
Expand Down Expand Up @@ -182,40 +189,56 @@ open class TableViewDriver: NSObject {

guard let newModel = newModel else { return }

if self._automaticDiffingEnabled {
if self._automaticDiffingEnabled, self._lightweightDiffing {
let old = oldModel?.sectionModels.reduce(into: 0) { $0 += $1.cellViewModels.count }
let new = newModel.sectionModels.reduce(into: 0) { $0 += $1.cellViewModels.count }

let visibleIndexPaths = tableView.indexPathsForVisibleRows ?? []
let old: [DiffableTableSectionViewModel] = oldModel?.sectionModelsForDiffing(inVisibleIndexPaths: visibleIndexPaths) ?? []
let changeset = StagedChangeset(
source: old,
target: newModel.sectionModelsForDiffing(inVisibleIndexPaths: visibleIndexPaths)
)
if changeset.isEmpty {
self._tableViewModel = newModel
self._tableViewModel = newModel
if old == new {
self.refreshViews(refreshContext: .contentOnly)
} else {
self.tableView.reload(
using: changeset,
deleteSectionsAnimation: self.deletionAnimation,
insertSectionsAnimation: self.insertionAnimation,
reloadSectionsAnimation: self.insertionAnimation,
deleteRowsAnimation: self.deletionAnimation,
insertRowsAnimation: self.insertionAnimation,
reloadRowsAnimation: self.insertionAnimation
) {
self._tableViewModel = $0.makeTableViewModel(sectionIndexTitles: oldModel?.sectionIndexTitles)
// We need to call reloadData here to ensure UITableView is in-sync with the data source before we start
// making calls to access visible cells. In the automatic diffing case, this is handled by calls to
// beginUpdates() endUpdates()
self.tableView.reloadData()
self.refreshViews()
}
} else {
if self._automaticDiffingEnabled {

let visibleIndexPaths = tableView.indexPathsForVisibleRows ?? []
let old: [DiffableTableSectionViewModel] = oldModel?.sectionModelsForDiffing(inVisibleIndexPaths: visibleIndexPaths) ?? []
let changeset = StagedChangeset(
source: old,
target: newModel.sectionModelsForDiffing(inVisibleIndexPaths: visibleIndexPaths)
)
if changeset.isEmpty {
self._tableViewModel = newModel
} else {
self.tableView.reload(
using: changeset,
deleteSectionsAnimation: self.deletionAnimation,
insertSectionsAnimation: self.insertionAnimation,
reloadSectionsAnimation: self.insertionAnimation,
deleteRowsAnimation: self.deletionAnimation,
insertRowsAnimation: self.insertionAnimation,
reloadRowsAnimation: self.insertionAnimation
) {
self._tableViewModel = $0.makeTableViewModel(sectionIndexTitles: oldModel?.sectionIndexTitles)
}
self._tableViewModel = newModel
}
// always refresh visible cells, in case some
// state changed that isn't captured by the diff
self.refreshViews(refreshContext: .contentOnly)
} else {
self._tableViewModel = newModel
// We need to call reloadData here to ensure UITableView is in-sync with the data source before we start
// making calls to access visible cells. In the automatic diffing case, this is handled by calls to
// beginUpdates() endUpdates()
self.tableView.reloadData()
self.refreshViews()
}
// always refresh visible cells, in case some
// state changed that isn't captured by the diff
self.refreshViews(refreshContext: .contentOnly)
} else {
self._tableViewModel = newModel
// We need to call reloadData here to ensure UITableView is in-sync with the data source before we start
// making calls to access visible cells. In the automatic diffing case, this is handled by calls to
// beginUpdates() endUpdates()
self.tableView.reloadData()
self.refreshViews()
}
}

Expand Down

0 comments on commit c996bf7

Please sign in to comment.