Speeding up local search. Support for regular expressions.
This commit is contained in:
parent
ae99fb98a7
commit
4496149fb7
@ -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 = "<group>"; };
|
||||
7A0420B925693D2C00034941 /* dkbm.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = dkbm.js; sourceTree = "<group>"; };
|
||||
7A0516192414FF0900FC55AC /* DateSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateSection.swift; sourceTree = "<group>"; };
|
||||
7A0B663629984201006F5189 /* DateCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateCache.swift; sourceTree = "<group>"; };
|
||||
7A0B969F257D6D4B000B39AD /* MultilineLabelRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultilineLabelRow.swift; sourceTree = "<group>"; };
|
||||
7A1090E724A394F100B4F0B2 /* AudioRecordCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioRecordCell.swift; sourceTree = "<group>"; };
|
||||
7A1090E924A3A26300B4F0B2 /* AudioPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayer.swift; sourceTree = "<group>"; };
|
||||
@ -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 = "<group>";
|
||||
@ -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" */;
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
extension RandomAccessCollection where Element: Dated & Hashable & Differentiable {
|
||||
var updatedTimestamp: TimeInterval {
|
||||
getAddedDate()
|
||||
}
|
||||
}
|
||||
|
||||
extension RandomAccessCollection where Element: Dated & Identifiable {
|
||||
func groupedByDate(type: SortParameter = .updatedDate) -> [DateSection<Element>] {
|
||||
let now = Date()
|
||||
let monthStart = now.dateAtStartOf(.month)
|
||||
var sectionsIndices: [TimeInterval: Int] = [:]
|
||||
|
||||
var sectionsArray: [DateSection<Element>] = []
|
||||
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
|
||||
|
||||
var key = dateInRegion.dateAtStartOf(.day).timeIntervalSince1970
|
||||
if date.isBeforeDate(monthStart, orEqual: false, granularity: .day) {
|
||||
key = dateInRegion.dateAtStartOf(.month).timeIntervalSince1970
|
||||
while index >= startIndex {
|
||||
|
||||
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<Element>(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<Element>(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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1,13 +1,12 @@
|
||||
import UIKit
|
||||
import RealmSwift
|
||||
import DifferenceKit
|
||||
import ExceptionCatcher
|
||||
import AutoCatCore
|
||||
|
||||
typealias FilterPredicate<T> = (T) -> Bool
|
||||
|
||||
class RealmSectionedDataSource<Item,Cell>: 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<Item,Cell>: NSObject, UITableViewDataSource
|
||||
private var sections: [DateSection<Item>] = []
|
||||
private var cellIdentifier: String
|
||||
private var filterPredicate: FilterPredicate<Item>?
|
||||
private var searchPredicate: FilterPredicate<Item>?
|
||||
private let groupQueue = DispatchQueue(label: "group")
|
||||
|
||||
private var objects: [Item] = []
|
||||
|
||||
private let onSizeChanged: ((Int) -> Void)?
|
||||
|
||||
init(table: UITableView, data: Results<Item>, cellIdentifier: String = String(describing: Cell.self), onSizeChanged: ((Int) -> Void)? = nil) {
|
||||
@ -35,24 +37,30 @@ class RealmSectionedDataSource<Item,Cell>: 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<Item,Cell>: 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)
|
||||
}
|
||||
|
||||
self.groupQueue.async {
|
||||
let newSections = items.groupedByDate()
|
||||
// let changeset = StagedChangeset(source: self.sections, target: newSections)
|
||||
DispatchQueue.main.async {
|
||||
self.sections = newSections
|
||||
if let searchPredicate = self.searchPredicate {
|
||||
items = items.filter(searchPredicate)
|
||||
}
|
||||
|
||||
self.onSizeChanged?(items.count)
|
||||
|
||||
self.sections = items.groupedByDate()
|
||||
self.tv.reloadData()
|
||||
// self.tv.reload(using: changeset, with: animated ? .fade : .none) { newSects in
|
||||
// self.sections = newSects
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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<Item,Cell>: NSObject, UITableViewDataSource
|
||||
}
|
||||
|
||||
func setFilterPredicate(_ predicate: FilterPredicate<Item>?) {
|
||||
guard self.filterPredicate != nil || predicate != nil else {
|
||||
return
|
||||
}
|
||||
|
||||
self.filterPredicate = predicate
|
||||
self.reload()
|
||||
}
|
||||
|
||||
func setSearchPredicate(_ predicate: FilterPredicate<Item>?) {
|
||||
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<Item,Cell>: NSObject, UITableViewDataSource
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func setQuery(_ query: Results<Item>) {
|
||||
self.data = query
|
||||
startObservingChanges()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
import UIKit
|
||||
import DifferenceKit
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
import AutoCatCore
|
||||
|
||||
class RxSectionedDataSource<Item,Cell>: NSObject, UITableViewDataSource where Item: Dated & Hashable & Differentiable & Decodable, Cell: UITableViewCell & ConfigurableCell, Cell.Item == Item {
|
||||
class RxSectionedDataSource<Item,Cell>: 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<Item>] = []
|
||||
|
||||
@ -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?
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import Foundation
|
||||
import SwiftDate
|
||||
import DifferenceKit
|
||||
|
||||
public struct DateSection<T>: Differentiable, DifferentiableSection, Hashable where T: Differentiable & Hashable {
|
||||
public struct DateSection<T: Identifiable> {
|
||||
|
||||
var timestamp: Double = 0
|
||||
var dateString: String
|
||||
@ -12,10 +11,10 @@ public struct DateSection<T>: Differentiable, DifferentiableSection, Hashable wh
|
||||
"\(dateString) (\(elements.count))"
|
||||
}
|
||||
|
||||
public init<C>(source: DateSection<T>, elements: C) where C : Swift.Collection, C.Element == Self.Collection.Element {
|
||||
public init(source: DateSection<T>, 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<T>: 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<T>: 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<T>: 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<T>) -> Bool {
|
||||
return self.differenceIdentifier == source.differenceIdentifier
|
||||
}
|
||||
|
||||
// MARK: - Hashable
|
||||
|
||||
public static func == (lhs: DateSection<T>, rhs: DateSection<T>) -> 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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
36
AutoCatCore/Utils/DateCache.swift
Normal file
36
AutoCatCore/Utils/DateCache.swift
Normal file
@ -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)
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user