Diffable datasource in check history list
This commit is contained in:
parent
9959fbc854
commit
6d0fb32fa7
@ -25,7 +25,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||||
|
|
||||||
let config = Realm.Configuration(
|
let config = Realm.Configuration(
|
||||||
schemaVersion: 27,
|
schemaVersion: 28,
|
||||||
migrationBlock: { migration, oldSchemaVersion in
|
migrationBlock: { migration, oldSchemaVersion in
|
||||||
if oldSchemaVersion <= 3 {
|
if oldSchemaVersion <= 3 {
|
||||||
var numbers: [String] = []
|
var numbers: [String] = []
|
||||||
|
|||||||
@ -18,7 +18,7 @@ extension AudioRecord: Dated {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension RandomAccessCollection where Element: Dated & Equatable & Differentiable {
|
extension RandomAccessCollection where Element: Dated & Hashable & Differentiable {
|
||||||
func groupedByDate() -> [DateSection<Element>] {
|
func groupedByDate() -> [DateSection<Element>] {
|
||||||
let now = Date()
|
let now = Date()
|
||||||
let monthStart = now.dateAtStartOf(.month)
|
let monthStart = now.dateAtStartOf(.month)
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import Foundation
|
|||||||
import SwiftDate
|
import SwiftDate
|
||||||
import DifferenceKit
|
import DifferenceKit
|
||||||
|
|
||||||
struct DateSection<T>: Differentiable, DifferentiableSection where T: Differentiable {
|
struct DateSection<T>: Differentiable, DifferentiableSection, Hashable where T: Differentiable & Hashable {
|
||||||
|
|
||||||
var timestamp: Double = 0
|
var timestamp: Double = 0
|
||||||
var header: String
|
var header: String
|
||||||
@ -59,4 +59,15 @@ struct DateSection<T>: Differentiable, DifferentiableSection where T: Differenti
|
|||||||
func isContentEqual(to source: DateSection<T>) -> Bool {
|
func isContentEqual(to source: DateSection<T>) -> Bool {
|
||||||
return self.differenceIdentifier == source.differenceIdentifier
|
return self.differenceIdentifier == source.differenceIdentifier
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Hasable
|
||||||
|
|
||||||
|
static func == (lhs: DateSection<T>, rhs: DateSection<T>) -> Bool {
|
||||||
|
return lhs.timestamp == rhs.timestamp && lhs.elements == rhs.elements
|
||||||
|
}
|
||||||
|
|
||||||
|
func hash(into hasher: inout Hasher) {
|
||||||
|
hasher.combine(self.timestamp)
|
||||||
|
hasher.combine(self.elements)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import RealmSwift
|
|||||||
|
|
||||||
class VehicleAd: Object, Decodable {
|
class VehicleAd: Object, Decodable {
|
||||||
@objc dynamic var id: Int = 0
|
@objc dynamic var id: Int = 0
|
||||||
@objc dynamic var url: String = ""
|
@objc dynamic var url: String?
|
||||||
@objc dynamic var price: String?
|
@objc dynamic var price: String?
|
||||||
@objc dynamic var date: TimeInterval = Date().timeIntervalSince1970
|
@objc dynamic var date: TimeInterval = Date().timeIntervalSince1970
|
||||||
@objc dynamic var mileage: String?
|
@objc dynamic var mileage: String?
|
||||||
|
|||||||
@ -3,7 +3,13 @@ import RealmSwift
|
|||||||
import DifferenceKit
|
import DifferenceKit
|
||||||
import ExceptionCatcher
|
import ExceptionCatcher
|
||||||
|
|
||||||
class RealmSectionedDataSource<Item,Cell>: NSObject, UITableViewDataSource where Item: Object & Identifiable & Dated & Differentiable, Cell: UITableViewCell & ConfigurableCell, Cell.Item == Item {
|
class RealmDiffableDataSourse<Item>: UITableViewDiffableDataSource<DateSection<Item>, Item> where Item: Hashable & ContentEquatable & ContentIdentifiable {
|
||||||
|
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||||
|
return snapshot().sectionIdentifiers[section].header
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RealmSectionedDataSource<Item,Cell> where Item: Object & Identifiable & Dated & Differentiable, Cell: UITableViewCell & ConfigurableCell, Cell.Item == Item {
|
||||||
|
|
||||||
private var tv: UITableView
|
private var tv: UITableView
|
||||||
private var data: Results<Item>
|
private var data: Results<Item>
|
||||||
@ -11,106 +17,45 @@ class RealmSectionedDataSource<Item,Cell>: NSObject, UITableViewDataSource where
|
|||||||
private var sections: [DateSection<Item>] = []
|
private var sections: [DateSection<Item>] = []
|
||||||
private var cellIdentifier: String
|
private var cellIdentifier: String
|
||||||
private var lastUpdateTime = Date()
|
private var lastUpdateTime = Date()
|
||||||
|
private var dataSource: RealmDiffableDataSourse<Item>!
|
||||||
|
|
||||||
init(table: UITableView, data: Results<Item>, cellIdentifier: String = String(describing: Cell.self)) {
|
init(table: UITableView, data: Results<Item>, cellIdentifier: String = String(describing: Cell.self)) {
|
||||||
self.tv = table
|
self.tv = table
|
||||||
self.data = data
|
self.data = data
|
||||||
self.cellIdentifier = cellIdentifier
|
self.cellIdentifier = cellIdentifier
|
||||||
super.init()
|
|
||||||
self.tv.dataSource = self
|
self.dataSource = RealmDiffableDataSourse<Item>(tableView: table) { tv, ip, model -> UITableViewCell? in
|
||||||
|
let cell = tv.dequeueReusableCell(withIdentifier: self.cellIdentifier, for: ip) as? Cell
|
||||||
|
cell?.configure(with: model)
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
self.tv.dataSource = self.dataSource
|
||||||
|
|
||||||
self.notificationToken = self.data.observe { changes in
|
self.notificationToken = self.data.observe { changes in
|
||||||
switch changes {
|
switch changes {
|
||||||
case .initial:
|
case .initial:
|
||||||
self.sections = self.data.groupedByDate()
|
self.reload(animated: false)
|
||||||
self.tv.reloadData()
|
case .update(_, _, _, _):
|
||||||
case .update(_, let deletions, let insertions, let modifications):
|
self.reload(animated: true)
|
||||||
if !Calendar.current.isDateInToday(self.lastUpdateTime) {
|
|
||||||
self.sections = self.data.groupedByDate()
|
|
||||||
self.tv.reloadData()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var actions = "Deletions: \(deletions), Insertions: \(insertions), Modifications: \(modifications)"
|
|
||||||
let newSections = self.data.groupedByDate()
|
|
||||||
let changeset = StagedChangeset(source: self.sections, target: newSections, section: 0)
|
|
||||||
|
|
||||||
do {
|
|
||||||
try ExceptionCatcher.catch {
|
|
||||||
self.tv.beginUpdates()
|
|
||||||
self.tv.deleteRows(at: deletions.map(self.indexPath), with: .automatic)
|
|
||||||
self.sections = self.data.groupedByDate()
|
|
||||||
self.tv.insertRows(at: insertions.map(self.indexPath), with: .automatic)
|
|
||||||
self.tv.reloadRows(at: modifications.map(self.indexPath), with: .automatic)
|
|
||||||
|
|
||||||
actions += "\n" + "Changesets count: \(changeset.count)"
|
|
||||||
if let firstChangeset = changeset.first {
|
|
||||||
actions += "\n" + "Deletions: \(firstChangeset.elementDeleted.map { $0.element })"
|
|
||||||
actions += "\n" + "Insertions: \(firstChangeset.elementInserted.map { $0.element })"
|
|
||||||
self.tv.deleteSections(IndexSet(firstChangeset.elementDeleted.map { $0.element }), with: .automatic)
|
|
||||||
self.tv.insertSections(IndexSet(firstChangeset.elementInserted.map { $0.element }), with: .automatic)
|
|
||||||
}
|
|
||||||
self.tv.endUpdates()
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
let msg = error.localizedDescription + "\n\n" + actions
|
|
||||||
let alert = UIAlertController(title: "Error", message: msg, preferredStyle: .alert)
|
|
||||||
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
|
|
||||||
self.tv.window?.rootViewController?.present(alert, animated: true)
|
|
||||||
}
|
|
||||||
case .error(let err):
|
case .error(let err):
|
||||||
print("Realm observer error: \(err)")
|
print("Realm observer error: \(err)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Here I assume that sections keep order of elements the same as in initial flat collection
|
|
||||||
// Otherwise it will not work properly
|
|
||||||
func indexPath(by index: Int) -> IndexPath {
|
|
||||||
var sectionStartIndex = 0
|
|
||||||
for (i, section) in self.sections.enumerated() {
|
|
||||||
if index < sectionStartIndex + section.elements.count {
|
|
||||||
return IndexPath(row: index - sectionStartIndex, section: i)
|
|
||||||
}
|
|
||||||
|
|
||||||
sectionStartIndex += section.elements.count
|
|
||||||
}
|
|
||||||
|
|
||||||
return IndexPath(row: 0, section: 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - UITableViewDataSource
|
|
||||||
|
|
||||||
func numberOfSections(in tableView: UITableView) -> Int {
|
|
||||||
return self.sections.count
|
|
||||||
}
|
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
||||||
return self.sections[section].elements.count
|
|
||||||
}
|
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
|
||||||
guard let cell = tableView.dequeueReusableCell(withIdentifier: self.cellIdentifier, for: indexPath) as? Cell else {
|
|
||||||
return UITableViewCell()
|
|
||||||
}
|
|
||||||
|
|
||||||
let item = self.sections[indexPath.section].elements[indexPath.row]
|
|
||||||
cell.configure(with: item)
|
|
||||||
return cell
|
|
||||||
}
|
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
|
||||||
return self.sections[section].header
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Public
|
// MARK: - Public
|
||||||
|
|
||||||
func item(at indexPath: IndexPath) -> Item {
|
func item(at indexPath: IndexPath) -> Item {
|
||||||
return self.sections[indexPath.section].elements[indexPath.row]
|
return self.sections[indexPath.section].elements[indexPath.row]
|
||||||
}
|
}
|
||||||
|
|
||||||
func reload() {
|
func reload(animated: Bool = true) {
|
||||||
self.sections = self.data.groupedByDate()
|
self.sections = self.data.groupedByDate()
|
||||||
self.tv.reloadData()
|
var snapshot = NSDiffableDataSourceSnapshot<DateSection<Item>, Item>()
|
||||||
|
snapshot.appendSections(self.sections)
|
||||||
|
for section in self.sections {
|
||||||
|
snapshot.appendItems(section.elements, toSection: section)
|
||||||
|
}
|
||||||
|
self.dataSource.apply(snapshot, animatingDifferences: animated, completion: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import DifferenceKit
|
|||||||
import RxSwift
|
import RxSwift
|
||||||
import RxCocoa
|
import RxCocoa
|
||||||
|
|
||||||
class RxSectionedDataSource<Item,Cell>: NSObject, UITableViewDataSource where Item: Dated & Equatable & Differentiable, Cell: UITableViewCell & ConfigurableCell, Cell.Item == Item {
|
class RxSectionedDataSource<Item,Cell>: NSObject, UITableViewDataSource where Item: Dated & Hashable & Differentiable, Cell: UITableViewCell & ConfigurableCell, Cell.Item == Item {
|
||||||
private var tv: UITableView
|
private var tv: UITableView
|
||||||
private var cellIdentifier: String
|
private var cellIdentifier: String
|
||||||
private var sections: [DateSection<Item>] = []
|
private var sections: [DateSection<Item>] = []
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user