From 4496149fb722935fdab4d5bb2c39588a5f03b15b Mon Sep 17 00:00:00 2001 From: Selim Mustafaev Date: Sun, 12 Feb 2023 01:38:17 +0300 Subject: [PATCH] Speeding up local search. Support for regular expressions. --- AutoCat.xcodeproj/project.pbxproj | 25 ++---- .../xcshareddata/swiftpm/Package.resolved | 9 -- AutoCat/Controllers/CheckController.swift | 20 +++-- AutoCat/Controllers/MainTabController.swift | 7 ++ AutoCat/Extensions/Dated.swift | 66 +++++++++----- AutoCat/SceneDelegate.swift | 32 +++++++ AutoCat/Utils/RxRealmDataSource.swift | 85 +++++++++++-------- AutoCat/Utils/RxSectionedDataSource.swift | 3 +- AutoCatCore/Models/AudioRecord.swift | 3 +- AutoCatCore/Models/DateSection.swift | 35 ++------ AutoCatCore/Models/Vehicle.swift | 11 +-- AutoCatCore/Utils/DateCache.swift | 36 ++++++++ 12 files changed, 195 insertions(+), 137 deletions(-) create mode 100644 AutoCatCore/Utils/DateCache.swift diff --git a/AutoCat.xcodeproj/project.pbxproj b/AutoCat.xcodeproj/project.pbxproj index 7fccfca..44b6ee0 100644 --- a/AutoCat.xcodeproj/project.pbxproj +++ b/AutoCat.xcodeproj/project.pbxproj @@ -12,6 +12,7 @@ 7A0420B12561A0E100034941 /* OsagoAddController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0420B02561A0E100034941 /* OsagoAddController.swift */; }; 7A0420B62568650C00034941 /* DkbmController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0420B52568650C00034941 /* DkbmController.swift */; }; 7A0420BA25693D2C00034941 /* dkbm.js in Resources */ = {isa = PBXBuildFile; fileRef = 7A0420B925693D2C00034941 /* dkbm.js */; }; + 7A0B663729984201006F5189 /* DateCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0B663629984201006F5189 /* DateCache.swift */; }; 7A0B96A0257D6D4B000B39AD /* MultilineLabelRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0B969F257D6D4B000B39AD /* MultilineLabelRow.swift */; }; 7A1090E824A394F100B4F0B2 /* AudioRecordCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1090E724A394F100B4F0B2 /* AudioRecordCell.swift */; }; 7A1090EA24A3A26300B4F0B2 /* AudioPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1090E924A3A26300B4F0B2 /* AudioPlayer.swift */; }; @@ -80,7 +81,6 @@ 7AA7BC3325A5DFB80053A5D5 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 7AF58D332402A91C00CE01A0 /* Kingfisher */; }; 7AA7BC3525A5DFB80053A5D5 /* ExceptionCatcher in Frameworks */ = {isa = PBXBuildFile; productRef = 7A813DC02508C4D900CC93B9 /* ExceptionCatcher */; }; 7AA7BC3625A5DFB80053A5D5 /* PKHUD in Frameworks */ = {isa = PBXBuildFile; productRef = 7AABDE1C2532F3EB0041AFC6 /* PKHUD */; }; - 7AABB1F0267E9CAA00D7AB32 /* DifferenceKit in Frameworks */ = {isa = PBXBuildFile; productRef = 7AABB1EF267E9CAA00D7AB32 /* DifferenceKit */; settings = {ATTRIBUTES = (Required, ); }; }; 7AABB1F2267E9CC800D7AB32 /* SwiftDate in Frameworks */ = {isa = PBXBuildFile; productRef = 7AABB1F1267E9CC800D7AB32 /* SwiftDate */; }; 7AABDE26253350C30041AFC6 /* RxSectionedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AABDE25253350C30041AFC6 /* RxSectionedDataSource.swift */; }; 7AB67E8C2435C38700258F61 /* CustomTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB67E8B2435C38700258F61 /* CustomTextField.swift */; }; @@ -174,6 +174,7 @@ 7A0420B52568650C00034941 /* DkbmController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DkbmController.swift; sourceTree = ""; }; 7A0420B925693D2C00034941 /* dkbm.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = dkbm.js; sourceTree = ""; }; 7A0516192414FF0900FC55AC /* DateSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateSection.swift; sourceTree = ""; }; + 7A0B663629984201006F5189 /* DateCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateCache.swift; sourceTree = ""; }; 7A0B969F257D6D4B000B39AD /* MultilineLabelRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultilineLabelRow.swift; sourceTree = ""; }; 7A1090E724A394F100B4F0B2 /* AudioRecordCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioRecordCell.swift; sourceTree = ""; }; 7A1090E924A3A26300B4F0B2 /* AudioPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayer.swift; sourceTree = ""; }; @@ -321,7 +322,6 @@ buildActionMask = 2147483647; files = ( 7AA54C1E26CD977A00F2BF28 /* RxSwift in Frameworks */, - 7AABB1F0267E9CAA00D7AB32 /* DifferenceKit in Frameworks */, 7AF6D2252677C2B40086EA64 /* RealmSwift in Frameworks */, 7AA54C1C26CD977A00F2BF28 /* RxCocoa in Frameworks */, 7AF6D2232677C2B40086EA64 /* Realm in Frameworks */, @@ -646,6 +646,7 @@ 7A11474323FF06CA00B424AF /* Api.swift */, 7A96AE30246B2FE400297C33 /* Constants.swift */, 7A000AA124C2EEDE001F5B00 /* Location.swift */, + 7A0B663629984201006F5189 /* DateCache.swift */, ); path = Utils; sourceTree = ""; @@ -733,7 +734,6 @@ packageProductDependencies = ( 7AF6D2222677C2B40086EA64 /* Realm */, 7AF6D2242677C2B40086EA64 /* RealmSwift */, - 7AABB1EF267E9CAA00D7AB32 /* DifferenceKit */, 7AABB1F1267E9CC800D7AB32 /* SwiftDate */, 7AA54C1B26CD977A00F2BF28 /* RxCocoa */, 7AA54C1D26CD977A00F2BF28 /* RxSwift */, @@ -783,7 +783,6 @@ 7A05160F241412CA00FC55AC /* XCRemoteSwiftPackageReference "SwiftDate" */, 7A813DBF2508C4D900CC93B9 /* XCRemoteSwiftPackageReference "ExceptionCatcher" */, 7AABDE1B2532F3EB0041AFC6 /* XCRemoteSwiftPackageReference "PKHUD" */, - 7AABDE21253327F10041AFC6 /* XCRemoteSwiftPackageReference "DifferenceKit" */, 7A35177927E23F8800DC538C /* XCRemoteSwiftPackageReference "Eureka" */, 7AC355482969652F00889457 /* XCRemoteSwiftPackageReference "SwiftEntryKit" */, ); @@ -941,6 +940,7 @@ 7A761C07267E8E7F0005F28F /* AnyEncodable.swift in Sources */, 7AF6D2162677C1680086EA64 /* Cloneable.swift in Sources */, 7AF6D21A2677C1680086EA64 /* User.swift in Sources */, + 7A0B663729984201006F5189 /* DateCache.swift in Sources */, 7AF6D21D2677C1680086EA64 /* Osago.swift in Sources */, 7AF6D2152677C1680086EA64 /* Settings.swift in Sources */, 7A761C052677F1BC0005F28F /* CocoaError.swift in Sources */, @@ -1140,7 +1140,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 110; + CURRENT_PROJECT_VERSION = 111; DEVELOPMENT_TEAM = 46DTTB8X4S; INFOPLIST_FILE = AutoCat/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; @@ -1165,7 +1165,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 110; + CURRENT_PROJECT_VERSION = 111; DEVELOPMENT_TEAM = 46DTTB8X4S; INFOPLIST_FILE = AutoCat/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 13.0; @@ -1381,14 +1381,6 @@ minimumVersion = 5.4.0; }; }; - 7AABDE21253327F10041AFC6 /* XCRemoteSwiftPackageReference "DifferenceKit" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/ra1028/DifferenceKit.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 1.1.5; - }; - }; 7AC355482969652F00889457 /* XCRemoteSwiftPackageReference "SwiftEntryKit" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/huri000/SwiftEntryKit"; @@ -1433,11 +1425,6 @@ package = 7A530B89240181F500CBFE6E /* XCRemoteSwiftPackageReference "RxRealm" */; productName = RxRealm; }; - 7AABB1EF267E9CAA00D7AB32 /* DifferenceKit */ = { - isa = XCSwiftPackageProductDependency; - package = 7AABDE21253327F10041AFC6 /* XCRemoteSwiftPackageReference "DifferenceKit" */; - productName = DifferenceKit; - }; 7AABB1F1267E9CC800D7AB32 /* SwiftDate */ = { isa = XCSwiftPackageProductDependency; package = 7A05160F241412CA00FC55AC /* XCRemoteSwiftPackageReference "SwiftDate" */; diff --git a/AutoCat.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/AutoCat.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 8bd90e4..c1ba975 100644 --- a/AutoCat.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/AutoCat.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,14 +1,5 @@ { "pins" : [ - { - "identity" : "differencekit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/ra1028/DifferenceKit.git", - "state" : { - "revision" : "14c66681e12a38b81045f44c6c29724a0d4b0e72", - "version" : "1.1.5" - } - }, { "identity" : "eureka", "kind" : "remoteSourceControl", diff --git a/AutoCat/Controllers/CheckController.swift b/AutoCat/Controllers/CheckController.swift index a91767b..c48aae7 100644 --- a/AutoCat/Controllers/CheckController.swift +++ b/AutoCat/Controllers/CheckController.swift @@ -294,18 +294,24 @@ class CheckController: UIViewController, UITableViewDelegate, UISearchResultsUpd // MARK: - UISearchResultsUpdating func updateSearchResults(for searchController: UISearchController) { - guard let realm = try? Realm() else { + guard let text = searchController.searchBar.text?.uppercased(), + !text.isEmpty + else { + historyDataSource.setSearchPredicate(nil) return } - var query = realm.objects(Vehicle.self) - .sorted(byKeyPath: "updatedDate", ascending: false) + let regex = try? NSRegularExpression(pattern: text) - if let text = searchController.searchBar.text?.uppercased(), !text.isEmpty { - query = query.filter("number CONTAINS '\(text)'") + historyDataSource.setSearchPredicate { vehicle in + let number = vehicle.getNumber() + if let regex { + let range = NSRange(location: 0, length: number.utf16.count) + return regex.numberOfMatches(in: number, range: range) > 0 + } else { + return number.contains(text) + } } - - historyDataSource.setQuery(query) } // MARK: - Contextual actions diff --git a/AutoCat/Controllers/MainTabController.swift b/AutoCat/Controllers/MainTabController.swift index bc8787b..3f5e687 100644 --- a/AutoCat/Controllers/MainTabController.swift +++ b/AutoCat/Controllers/MainTabController.swift @@ -10,6 +10,13 @@ class MainTabController: UITabBarController, UITabBarControllerDelegate { override func viewDidLoad() { super.viewDidLoad() self.delegate = self + + #if targetEnvironment(macCatalyst) + + // Remove "+" tab for macOS version (it will be on the toolbar) + viewControllers?.remove(at: 2) + + #endif } func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool { diff --git a/AutoCat/Extensions/Dated.swift b/AutoCat/Extensions/Dated.swift index 41a2f52..d0c6313 100644 --- a/AutoCat/Extensions/Dated.swift +++ b/AutoCat/Extensions/Dated.swift @@ -1,14 +1,16 @@ import Foundation import SwiftDate -import DifferenceKit import AutoCatCore protocol Dated { var added: Date { get } + var addedTimestamp: TimeInterval { get } var updated: Date { get } + var updatedTimestamp: TimeInterval { get } } extension Vehicle: Dated { + var updated: Date { Date(timeIntervalSince1970: self.updatedDate) } @@ -16,6 +18,14 @@ extension Vehicle: Dated { var added: Date { Date(timeIntervalSince1970: self.addedDate) } + + var addedTimestamp: TimeInterval { + addedDate + } + + var updatedTimestamp: TimeInterval { + updatedDate + } } extension AudioRecord: Dated { @@ -26,38 +36,48 @@ extension AudioRecord: Dated { var added: Date { Date(timeIntervalSince1970: self.getAddedDate()) } + + var addedTimestamp: TimeInterval { + getAddedDate() + } + + var updatedTimestamp: TimeInterval { + getAddedDate() + } } -extension RandomAccessCollection where Element: Dated & Hashable & Differentiable { +extension RandomAccessCollection where Element: Dated & Identifiable { func groupedByDate(type: SortParameter = .updatedDate) -> [DateSection] { - let now = Date() - let monthStart = now.dateAtStartOf(.month) - var sectionsIndices: [TimeInterval: Int] = [:] + var sectionsArray: [DateSection] = [] - for vehicle in self { - let date = self.date(of: type, for: vehicle) - let dateInRegion = DateInRegion(date, region: Region.current) + var key: TimeInterval = 0 + var keyNext: TimeInterval = 0 + var index = self.index(before: endIndex) + let currentMonthStart = DateCache.shared.monthStart.timeIntervalSince1970 + + while index >= startIndex { - var key = dateInRegion.dateAtStartOf(.day).timeIntervalSince1970 - if date.isBeforeDate(monthStart, orEqual: false, granularity: .day) { - key = dateInRegion.dateAtStartOf(.month).timeIntervalSince1970 + let timestamp: TimeInterval + switch type { + case .addedDate: timestamp = self[index].addedTimestamp + case .updatedDate: timestamp = self[index].updatedTimestamp } - if let index = sectionsIndices[key] { - sectionsArray[index].append(vehicle) - } else { - sectionsArray.append(DateSection(timestamp: key, items: [vehicle])) - sectionsIndices[key] = sectionsArray.count - 1 + if keyNext == 0 || timestamp > keyNext { + + let component: Calendar.Component = timestamp >= currentMonthStart ? .day : .month + let dateInRegion = DateInRegion(seconds: timestamp, region: .current) + let startOfPeriod = dateInRegion.dateAtStartOf(component) + key = startOfPeriod.timeIntervalSince1970 + + sectionsArray.insert(DateSection(timestamp: key, items: []), at: 0) + keyNext = startOfPeriod.dateByAdding(1, component).timeIntervalSince1970 } + + sectionsArray[0].insert(self[index], at: 0) + index = self.index(before: index) } return sectionsArray } - - func date(of type: SortParameter, for object: Dated) -> Date { - switch type { - case .addedDate: return object.added - case .updatedDate: return object.updated - } - } } diff --git a/AutoCat/SceneDelegate.swift b/AutoCat/SceneDelegate.swift index 4c33320..77613bd 100644 --- a/AutoCat/SceneDelegate.swift +++ b/AutoCat/SceneDelegate.swift @@ -39,6 +39,21 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { self.window?.rootViewController = storyboard.instantiateViewController(identifier: "MainSplitController") } + #if targetEnvironment(macCatalyst) + + let toolbar = NSToolbar(identifier: "main") + toolbar.delegate = self + toolbar.displayMode = .iconOnly + + if let titlebar = windowScene.titlebar { + titlebar.toolbar = toolbar + if #available(macCatalyst 14.0, *) { + titlebar.toolbarStyle = .automatic + } + } + + #endif + self.window?.makeKeyAndVisible() } @@ -163,3 +178,20 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } } +#if targetEnvironment(macCatalyst) + +extension SceneDelegate: NSToolbarDelegate { + + func toolbarDefaultItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { + let identifiers: [NSToolbarItem.Identifier] = [ + .toggleSidebar + ] + return identifiers + } + + func toolbarAllowedItemIdentifiers(_ toolbar: NSToolbar) -> [NSToolbarItem.Identifier] { + return toolbarDefaultItemIdentifiers(toolbar) + } +} + +#endif diff --git a/AutoCat/Utils/RxRealmDataSource.swift b/AutoCat/Utils/RxRealmDataSource.swift index f5e9808..5fef017 100644 --- a/AutoCat/Utils/RxRealmDataSource.swift +++ b/AutoCat/Utils/RxRealmDataSource.swift @@ -1,13 +1,12 @@ import UIKit import RealmSwift -import DifferenceKit import ExceptionCatcher import AutoCatCore typealias FilterPredicate = (T) -> Bool class RealmSectionedDataSource: NSObject, UITableViewDataSource - where Item: Object & Identifiable & Dated & Differentiable & Cloneable, + where Item: Object & Identifiable & Dated & Cloneable, Cell: UITableViewCell & ConfigurableCell, Cell.Item == Item { @@ -17,8 +16,11 @@ class RealmSectionedDataSource: NSObject, UITableViewDataSource private var sections: [DateSection] = [] private var cellIdentifier: String private var filterPredicate: FilterPredicate? + private var searchPredicate: FilterPredicate? private let groupQueue = DispatchQueue(label: "group") + private var objects: [Item] = [] + private let onSizeChanged: ((Int) -> Void)? init(table: UITableView, data: Results, cellIdentifier: String = String(describing: Cell.self), onSizeChanged: ((Int) -> Void)? = nil) { @@ -35,24 +37,30 @@ class RealmSectionedDataSource: NSObject, UITableViewDataSource func startObservingChanges() { self.notificationToken = self.data.observe { changes in - self.onSizeChanged?(self.data.count) switch changes { case .initial: + self.objects = self.data.toArray().map { $0.shallowClone() } self.reload(animated: false) case .update(_, let deletions, let insertions, let modifications): - if deletions.isEmpty && modifications.isEmpty && insertions.count == 1 && insertions.first == 0 { - // Most common use case - adding new element to the top of the list - // Example - checking new number - self.insertFirst() - } else if deletions.isEmpty && insertions.isEmpty && modifications.count == 1 && modifications.first == 0 { - self.reloadFirst() - } else if modifications.isEmpty && deletions.count == 1 && insertions.count == 1 && insertions.first == 0 { - // Probably moving an item to the first position - // For example - updating number info - self.moveToFirst(deleteIndex: deletions.first!) - } else { - self.reload(animated: false) - } + + deletions.forEach { self.objects.remove(at: $0) } + insertions.forEach { self.objects.insert(self.data[$0].shallowClone(), at: $0) } + modifications.forEach { self.objects[$0] = self.data[$0].shallowClone() } + self.reload() + +// if deletions.isEmpty && modifications.isEmpty && insertions.count == 1 && insertions.first == 0 { +// // Most common use case - adding new element to the top of the list +// // Example - checking new number +// self.insertFirst() +// } else if deletions.isEmpty && insertions.isEmpty && modifications.count == 1 && modifications.first == 0 { +// self.reloadFirst() +// } else if modifications.isEmpty && deletions.count == 1 && insertions.count == 1 && insertions.first == 0 { +// // Probably moving an item to the first position +// // For example - updating number info +// self.moveToFirst(deleteIndex: deletions.first!) +// } else { +// self.reload(animated: false) +// } case .error(let err): print("Realm observer error: \(err)") } @@ -90,26 +98,23 @@ class RealmSectionedDataSource: NSObject, UITableViewDataSource } func reload(animated: Bool = true) { - var items = self.data.toArray().map { $0.shallowClone() } - if let predicate = self.filterPredicate { - items = items.filter(predicate) + var items = objects + if let filterPredicate = self.filterPredicate { + items = items.filter(filterPredicate) } + + if let searchPredicate = self.searchPredicate { + items = items.filter(searchPredicate) + } + + self.onSizeChanged?(items.count) - self.groupQueue.async { - let newSections = items.groupedByDate() -// let changeset = StagedChangeset(source: self.sections, target: newSections) - DispatchQueue.main.async { - self.sections = newSections - self.tv.reloadData() -// self.tv.reload(using: changeset, with: animated ? .fade : .none) { newSects in -// self.sections = newSects -// } - } - } + self.sections = items.groupedByDate() + self.tv.reloadData() } func insertFirst() { - guard let item = data.first?.clone() else { + guard let item = data.first?.shallowClone() else { reload(animated: false) return } @@ -157,10 +162,23 @@ class RealmSectionedDataSource: NSObject, UITableViewDataSource } func setFilterPredicate(_ predicate: FilterPredicate?) { + guard self.filterPredicate != nil || predicate != nil else { + return + } + self.filterPredicate = predicate self.reload() } + func setSearchPredicate(_ predicate: FilterPredicate?) { + guard self.searchPredicate != nil || predicate != nil else { + return + } + + self.searchPredicate = predicate + self.reload() + } + func makeCsv() throws -> String { guard let type = Item.self as? Exportable.Type else { throw CocoaError.error("Type \(Item.self) is not exportable") @@ -185,9 +203,4 @@ class RealmSectionedDataSource: NSObject, UITableViewDataSource return result } - - func setQuery(_ query: Results) { - self.data = query - startObservingChanges() - } } diff --git a/AutoCat/Utils/RxSectionedDataSource.swift b/AutoCat/Utils/RxSectionedDataSource.swift index 377bf31..834354c 100644 --- a/AutoCat/Utils/RxSectionedDataSource.swift +++ b/AutoCat/Utils/RxSectionedDataSource.swift @@ -1,10 +1,9 @@ import UIKit -import DifferenceKit import RxSwift import RxCocoa import AutoCatCore -class RxSectionedDataSource: NSObject, UITableViewDataSource where Item: Dated & Hashable & Differentiable & Decodable, Cell: UITableViewCell & ConfigurableCell, Cell.Item == Item { +class RxSectionedDataSource: NSObject, UITableViewDataSource where Item: Dated & Decodable & Identifiable, Cell: UITableViewCell & ConfigurableCell, Cell.Item == Item { private var tv: UITableView private var cellIdentifier: String private var sections: [DateSection] = [] diff --git a/AutoCatCore/Models/AudioRecord.swift b/AutoCatCore/Models/AudioRecord.swift index d52a6bc..67d74f1 100644 --- a/AutoCatCore/Models/AudioRecord.swift +++ b/AutoCatCore/Models/AudioRecord.swift @@ -1,8 +1,7 @@ import Foundation import RealmSwift -import DifferenceKit -public class AudioRecord: Object, Identifiable, Differentiable, Cloneable { +public class AudioRecord: Object, Identifiable, Cloneable { @objc public dynamic var path: String = "" @objc public dynamic var number: String? diff --git a/AutoCatCore/Models/DateSection.swift b/AutoCatCore/Models/DateSection.swift index e9b98b5..b824ba5 100644 --- a/AutoCatCore/Models/DateSection.swift +++ b/AutoCatCore/Models/DateSection.swift @@ -1,8 +1,7 @@ import Foundation import SwiftDate -import DifferenceKit -public struct DateSection: Differentiable, DifferentiableSection, Hashable where T: Differentiable & Hashable { +public struct DateSection { var timestamp: Double = 0 var dateString: String @@ -12,10 +11,10 @@ public struct DateSection: Differentiable, DifferentiableSection, Hashable wh "\(dateString) (\(elements.count))" } - public init(source: DateSection, elements: C) where C : Swift.Collection, C.Element == Self.Collection.Element { + public init(source: DateSection, elements: [T]) { self.timestamp = source.timestamp self.dateString = source.dateString - self.elements = Array(elements) + self.elements = elements } public init(original: DateSection, items: [T]) { @@ -28,9 +27,8 @@ public struct DateSection: Differentiable, DifferentiableSection, Hashable wh formatter.dateStyle = .medium formatter.timeStyle = .medium - let now = DateInRegion(Date(), region: Region.current) - let monthStart = now.dateAtStartOf(.month) - let weekStart = now.dateAtStartOf(.weekOfMonth) + let monthStart = DateCache.shared.monthStart + let weekStart = DateCache.shared.weekStart let date = Date(timeIntervalSince1970: timestamp) let dateInRegion = DateInRegion(date, region: Region.current) @@ -64,7 +62,7 @@ public struct DateSection: Differentiable, DifferentiableSection, Hashable wh } public func index(of element: T) -> Int? { - elements.firstIndex { $0.differenceIdentifier == element.differenceIdentifier } + elements.firstIndex { $0.id == element.id } } public mutating func replace(_ element: T, at index: Int) { @@ -74,25 +72,4 @@ public struct DateSection: Differentiable, DifferentiableSection, Hashable wh public mutating func remove(at index: Int) { elements.remove(at: index) } - - // MARK: - Differentiable - - public var differenceIdentifier: String { - return dateString - } - - public func isContentEqual(to source: DateSection) -> Bool { - return self.differenceIdentifier == source.differenceIdentifier - } - - // MARK: - Hashable - - public static func == (lhs: DateSection, rhs: DateSection) -> Bool { - return lhs.timestamp == rhs.timestamp && lhs.elements == rhs.elements - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(self.timestamp) - hasher.combine(self.elements) - } } diff --git a/AutoCatCore/Models/Vehicle.swift b/AutoCatCore/Models/Vehicle.swift index 062ba9e..4863b1b 100644 --- a/AutoCatCore/Models/Vehicle.swift +++ b/AutoCatCore/Models/Vehicle.swift @@ -1,6 +1,5 @@ import Foundation import RealmSwift -import DifferenceKit public class VehicleName: Object, Decodable, Cloneable { @objc public dynamic var original: String? @@ -160,7 +159,7 @@ public class VehicleOwnershipPeriod: Object, Decodable, Cloneable { } } -public final class Vehicle: Object, Decodable, Identifiable, Differentiable, Cloneable, Exportable { +public final class Vehicle: Object, Decodable, Identifiable, Cloneable, Exportable { @objc public dynamic var brand: VehicleBrand? @objc public dynamic var model: VehicleModel? @objc public dynamic var color: String? @@ -194,14 +193,6 @@ public final class Vehicle: Object, Decodable, Identifiable, Differentiable, Clo return f }() - public var differenceIdentifier: String { self.number } - - public func isContentEqual(to source: Vehicle) -> Bool { - return self.number == source.number && - self.addedDate == source.addedDate && - self.updatedDate == source.updatedDate - } - enum CodingKeys: String, CodingKey { case brand case model diff --git a/AutoCatCore/Utils/DateCache.swift b/AutoCatCore/Utils/DateCache.swift new file mode 100644 index 0000000..52233fb --- /dev/null +++ b/AutoCatCore/Utils/DateCache.swift @@ -0,0 +1,36 @@ +// +// DateCache.swift +// AutoCatCore +// +// Created by Selim Mustafaev on 12.02.2023. +// Copyright © 2023 Selim Mustafaev. All rights reserved. +// + +import Foundation +import SwiftDate + +public class DateCache { + + public static let shared = DateCache() + + public var monthStart: DateInRegion + public var weekStart: DateInRegion + + public init() { + + let now = DateInRegion(Date(), region: Region.current) + self.monthStart = now.dateAtStartOf(.month) + self.weekStart = now.dateAtStartOf(.weekOfMonth) + + NotificationCenter.default.addObserver(self, + selector: #selector(dayChanged), + name: .NSCalendarDayChanged, + object: nil) + } + + @objc func dayChanged(_ notification: Notification) { + let now = DateInRegion(Date(), region: Region.current) + self.monthStart = now.dateAtStartOf(.month) + self.weekStart = now.dateAtStartOf(.weekOfMonth) + } +}