From 6d0fb32fa7d9cc4d80e70f8929fbcb417a9d9997 Mon Sep 17 00:00:00 2001 From: Selim Mustafaev Date: Fri, 18 Dec 2020 08:51:57 +0300 Subject: [PATCH] Diffable datasource in check history list --- AutoCat/AppDelegate.swift | 2 +- AutoCat/Extensions/Dated.swift | 2 +- AutoCat/Models/DateSection.swift | 13 ++- AutoCat/Models/VehicleAd.swift | 2 +- AutoCat/Utils/RxRealmDataSource.swift | 105 ++++++---------------- AutoCat/Utils/RxSectionedDataSource.swift | 2 +- 6 files changed, 41 insertions(+), 85 deletions(-) diff --git a/AutoCat/AppDelegate.swift b/AutoCat/AppDelegate.swift index beb21d8..7c99f7e 100644 --- a/AutoCat/AppDelegate.swift +++ b/AutoCat/AppDelegate.swift @@ -25,7 +25,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { let config = Realm.Configuration( - schemaVersion: 27, + schemaVersion: 28, migrationBlock: { migration, oldSchemaVersion in if oldSchemaVersion <= 3 { var numbers: [String] = [] diff --git a/AutoCat/Extensions/Dated.swift b/AutoCat/Extensions/Dated.swift index e43567e..0658d0c 100644 --- a/AutoCat/Extensions/Dated.swift +++ b/AutoCat/Extensions/Dated.swift @@ -18,7 +18,7 @@ extension AudioRecord: Dated { } } -extension RandomAccessCollection where Element: Dated & Equatable & Differentiable { +extension RandomAccessCollection where Element: Dated & Hashable & Differentiable { func groupedByDate() -> [DateSection] { let now = Date() let monthStart = now.dateAtStartOf(.month) diff --git a/AutoCat/Models/DateSection.swift b/AutoCat/Models/DateSection.swift index 6b8c243..792f60b 100644 --- a/AutoCat/Models/DateSection.swift +++ b/AutoCat/Models/DateSection.swift @@ -2,7 +2,7 @@ import Foundation import SwiftDate import DifferenceKit -struct DateSection: Differentiable, DifferentiableSection where T: Differentiable { +struct DateSection: Differentiable, DifferentiableSection, Hashable where T: Differentiable & Hashable { var timestamp: Double = 0 var header: String @@ -59,4 +59,15 @@ struct DateSection: Differentiable, DifferentiableSection where T: Differenti func isContentEqual(to source: DateSection) -> Bool { return self.differenceIdentifier == source.differenceIdentifier } + + // MARK: - Hasable + + static func == (lhs: DateSection, rhs: DateSection) -> Bool { + return lhs.timestamp == rhs.timestamp && lhs.elements == rhs.elements + } + + func hash(into hasher: inout Hasher) { + hasher.combine(self.timestamp) + hasher.combine(self.elements) + } } diff --git a/AutoCat/Models/VehicleAd.swift b/AutoCat/Models/VehicleAd.swift index ee859e5..683ef2e 100644 --- a/AutoCat/Models/VehicleAd.swift +++ b/AutoCat/Models/VehicleAd.swift @@ -3,7 +3,7 @@ import RealmSwift class VehicleAd: Object, Decodable { @objc dynamic var id: Int = 0 - @objc dynamic var url: String = "" + @objc dynamic var url: String? @objc dynamic var price: String? @objc dynamic var date: TimeInterval = Date().timeIntervalSince1970 @objc dynamic var mileage: String? diff --git a/AutoCat/Utils/RxRealmDataSource.swift b/AutoCat/Utils/RxRealmDataSource.swift index c0ba6ef..5d70f49 100644 --- a/AutoCat/Utils/RxRealmDataSource.swift +++ b/AutoCat/Utils/RxRealmDataSource.swift @@ -3,7 +3,13 @@ import RealmSwift import DifferenceKit import ExceptionCatcher -class RealmSectionedDataSource: NSObject, UITableViewDataSource where Item: Object & Identifiable & Dated & Differentiable, Cell: UITableViewCell & ConfigurableCell, Cell.Item == Item { +class RealmDiffableDataSourse: UITableViewDiffableDataSource, Item> where Item: Hashable & ContentEquatable & ContentIdentifiable { + override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + return snapshot().sectionIdentifiers[section].header + } +} + +class RealmSectionedDataSource where Item: Object & Identifiable & Dated & Differentiable, Cell: UITableViewCell & ConfigurableCell, Cell.Item == Item { private var tv: UITableView private var data: Results @@ -11,97 +17,31 @@ class RealmSectionedDataSource: NSObject, UITableViewDataSource where private var sections: [DateSection] = [] private var cellIdentifier: String private var lastUpdateTime = Date() + private var dataSource: RealmDiffableDataSourse! init(table: UITableView, data: Results, cellIdentifier: String = String(describing: Cell.self)) { self.tv = table self.data = data self.cellIdentifier = cellIdentifier - super.init() - self.tv.dataSource = self + + self.dataSource = RealmDiffableDataSourse(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 switch changes { case .initial: - self.sections = self.data.groupedByDate() - self.tv.reloadData() - case .update(_, let deletions, let insertions, let modifications): - 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) - } + self.reload(animated: false) + case .update(_, _, _, _): + self.reload(animated: true) case .error(let 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 @@ -109,8 +49,13 @@ class RealmSectionedDataSource: NSObject, UITableViewDataSource where return self.sections[indexPath.section].elements[indexPath.row] } - func reload() { + func reload(animated: Bool = true) { self.sections = self.data.groupedByDate() - self.tv.reloadData() + var snapshot = NSDiffableDataSourceSnapshot, Item>() + snapshot.appendSections(self.sections) + for section in self.sections { + snapshot.appendItems(section.elements, toSection: section) + } + self.dataSource.apply(snapshot, animatingDifferences: animated, completion: nil) } } diff --git a/AutoCat/Utils/RxSectionedDataSource.swift b/AutoCat/Utils/RxSectionedDataSource.swift index 722e0ac..851a274 100644 --- a/AutoCat/Utils/RxSectionedDataSource.swift +++ b/AutoCat/Utils/RxSectionedDataSource.swift @@ -3,7 +3,7 @@ import DifferenceKit import RxSwift import RxCocoa -class RxSectionedDataSource: NSObject, UITableViewDataSource where Item: Dated & Equatable & Differentiable, Cell: UITableViewCell & ConfigurableCell, Cell.Item == Item { +class RxSectionedDataSource: NSObject, UITableViewDataSource where Item: Dated & Hashable & Differentiable, Cell: UITableViewCell & ConfigurableCell, Cell.Item == Item { private var tv: UITableView private var cellIdentifier: String private var sections: [DateSection] = []