Displaying history
This commit is contained in:
parent
49046d5619
commit
aa7efbe1b0
@ -18,6 +18,8 @@
|
||||
6841A8FF53F0AADF96B138C1 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6841AFE790F6FC06838B1E2C /* UIControl.swift */; };
|
||||
6841ABD5E4B126DEF3612BBD /* PNKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6841AB0052E9DB6914901EA3 /* PNKeyboard.swift */; };
|
||||
6841AF924E165F1B3A3B5FB5 /* AuthController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6841ABEA0314E3B4E438C311 /* AuthController.swift */; };
|
||||
7A1D80E027F1F275007BD64F /* DifferenceKit in Frameworks */ = {isa = PBXBuildFile; productRef = 7A1D80DF27F1F275007BD64F /* DifferenceKit */; };
|
||||
7A1D80E627F20FCB007BD64F /* DifferenceKit in Frameworks */ = {isa = PBXBuildFile; productRef = 7A1D80E527F20FCB007BD64F /* DifferenceKit */; };
|
||||
7A24C19727EE212E00049E7F /* RoadNumbers.otf in Resources */ = {isa = PBXBuildFile; fileRef = 7A24C19527EE212E00049E7F /* RoadNumbers.otf */; };
|
||||
7A24C19827EE212E00049E7F /* RoadNumbers2.0.otf in Resources */ = {isa = PBXBuildFile; fileRef = 7A24C19627EE212E00049E7F /* RoadNumbers2.0.otf */; };
|
||||
7A24C19C27EE25B400049E7F /* PlateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A24C19A27EE25B400049E7F /* PlateView.swift */; };
|
||||
@ -183,6 +185,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7A1D80E027F1F275007BD64F /* DifferenceKit in Frameworks */,
|
||||
7A49F4EC27D4064500AEAAE0 /* AutoCatCore.framework in Frameworks */,
|
||||
7A48B26727D9442A004D1A4B /* PKHUD in Frameworks */,
|
||||
7A28283627E74C110049BDBF /* SwiftEntryKit in Frameworks */,
|
||||
@ -207,6 +210,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7A1D80E627F20FCB007BD64F /* DifferenceKit in Frameworks */,
|
||||
7AE32D6E27F06D2D004EF6E0 /* SwiftDate in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@ -477,6 +481,7 @@
|
||||
packageProductDependencies = (
|
||||
7A48B26627D9442A004D1A4B /* PKHUD */,
|
||||
7A28283527E74C110049BDBF /* SwiftEntryKit */,
|
||||
7A1D80DF27F1F275007BD64F /* DifferenceKit */,
|
||||
);
|
||||
productName = AutoCat2;
|
||||
productReference = 7A49F49F27D4061900AEAAE0 /* AutoCat2.app */;
|
||||
@ -534,6 +539,7 @@
|
||||
name = AutoCatCore;
|
||||
packageProductDependencies = (
|
||||
7AE32D6D27F06D2D004EF6E0 /* SwiftDate */,
|
||||
7A1D80E527F20FCB007BD64F /* DifferenceKit */,
|
||||
);
|
||||
productName = AutoCatCore;
|
||||
productReference = 7A49F4D727D4064500AEAAE0 /* AutoCatCore.framework */;
|
||||
@ -601,6 +607,8 @@
|
||||
7A48B26527D9442A004D1A4B /* XCRemoteSwiftPackageReference "PKHUD" */,
|
||||
7A28283427E74C110049BDBF /* XCRemoteSwiftPackageReference "SwiftEntryKit" */,
|
||||
7AE32D6C27F06D2D004EF6E0 /* XCRemoteSwiftPackageReference "SwiftDate" */,
|
||||
7A1D80DE27F1F275007BD64F /* XCRemoteSwiftPackageReference "DifferenceKit" */,
|
||||
7A1D80E427F20FCB007BD64F /* XCRemoteSwiftPackageReference "DifferenceKit" */,
|
||||
);
|
||||
productRefGroup = 7A49F4A027D4061900AEAAE0 /* Products */;
|
||||
projectDirPath = "";
|
||||
@ -1199,6 +1207,22 @@
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
7A1D80DE27F1F275007BD64F /* XCRemoteSwiftPackageReference "DifferenceKit" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/ra1028/DifferenceKit";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 1.0.0;
|
||||
};
|
||||
};
|
||||
7A1D80E427F20FCB007BD64F /* XCRemoteSwiftPackageReference "DifferenceKit" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/ra1028/DifferenceKit";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 1.0.0;
|
||||
};
|
||||
};
|
||||
7A28283427E74C110049BDBF /* XCRemoteSwiftPackageReference "SwiftEntryKit" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/huri000/SwiftEntryKit";
|
||||
@ -1226,6 +1250,16 @@
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
7A1D80DF27F1F275007BD64F /* DifferenceKit */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 7A1D80DE27F1F275007BD64F /* XCRemoteSwiftPackageReference "DifferenceKit" */;
|
||||
productName = DifferenceKit;
|
||||
};
|
||||
7A1D80E527F20FCB007BD64F /* DifferenceKit */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 7A1D80DE27F1F275007BD64F /* XCRemoteSwiftPackageReference "DifferenceKit" */;
|
||||
productName = DifferenceKit;
|
||||
};
|
||||
7A28283527E74C110049BDBF /* SwiftEntryKit */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 7A28283427E74C110049BDBF /* XCRemoteSwiftPackageReference "SwiftEntryKit" */;
|
||||
|
||||
@ -1,5 +1,14 @@
|
||||
{
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "differencekit",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/ra1028/DifferenceKit",
|
||||
"state" : {
|
||||
"revision" : "62745d7780deef4a023a792a1f8f763ec7bf9705",
|
||||
"version" : "1.2.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "pkhud",
|
||||
"kind" : "remoteSourceControl",
|
||||
|
||||
@ -16,6 +16,27 @@
|
||||
</dict>
|
||||
<key>AutoCatCore.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
<key>DifferenceKit (Playground) 1.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>6</integer>
|
||||
</dict>
|
||||
<key>DifferenceKit (Playground) 2.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>7</integer>
|
||||
</dict>
|
||||
<key>DifferenceKit (Playground).xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
@ -24,21 +45,21 @@
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>3</integer>
|
||||
<integer>4</integer>
|
||||
</dict>
|
||||
<key>SwiftDate (Playground) 2.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>4</integer>
|
||||
<integer>5</integer>
|
||||
</dict>
|
||||
<key>SwiftDate (Playground).xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
<key>orderHint</key>
|
||||
<integer>2</integer>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
|
||||
@ -10,9 +10,60 @@ import AutoCatCore
|
||||
|
||||
class VehicleCell: UITableViewCell {
|
||||
|
||||
private let numberLabel = UILabel()
|
||||
private let nameLabel = UILabel()
|
||||
.disableTranslatesAutoresizingMaskIntoConstraints()
|
||||
|
||||
private lazy var nameStackView: UIStackView = {
|
||||
let stack = UIStackView(arrangedSubviews: [nameLabel])
|
||||
return stack
|
||||
}()
|
||||
|
||||
private let plateView: PlateView = {
|
||||
let view = PlateView(frame: .zero)
|
||||
view.fontSize = 48
|
||||
view.setContentHuggingPriority(.defaultHigh, for: .horizontal)
|
||||
return view
|
||||
}()
|
||||
|
||||
private let addedDateLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = UIFont.preferredFont(forTextStyle: .footnote)
|
||||
label.textColor = .tertiaryLabel
|
||||
return label
|
||||
}()
|
||||
|
||||
private let updatedDateLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = UIFont.preferredFont(forTextStyle: .footnote)
|
||||
label.textColor = .secondaryLabel
|
||||
return label
|
||||
}()
|
||||
|
||||
private lazy var dateStackView: UIStackView = {
|
||||
let stack = UIStackView(arrangedSubviews: [updatedDateLabel, addedDateLabel])
|
||||
stack.axis = .vertical
|
||||
stack.spacing = 8
|
||||
stack.alignment = .trailing
|
||||
return stack
|
||||
}()
|
||||
|
||||
private lazy var numberStackView: UIStackView = {
|
||||
let stack = UIStackView(arrangedSubviews: [plateView, dateStackView])
|
||||
stack.spacing = 8
|
||||
stack.alignment = .bottom
|
||||
return stack
|
||||
}()
|
||||
|
||||
private lazy var mainStackView: UIStackView = {
|
||||
let stack = UIStackView(arrangedSubviews: [nameStackView, numberStackView])
|
||||
stack.axis = .vertical
|
||||
stack.spacing = 8
|
||||
stack.translatesAutoresizingMaskIntoConstraints = false
|
||||
return stack
|
||||
}()
|
||||
|
||||
let formatter = DateFormatter()
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
setup()
|
||||
@ -23,15 +74,40 @@ class VehicleCell: UITableViewCell {
|
||||
}
|
||||
|
||||
func setup() {
|
||||
contentView.addSubview(numberLabel)
|
||||
numberLabel.pin(to: contentView, insets: .init(all: 8))
|
||||
contentView.addSubview(mainStackView)
|
||||
mainStackView.pin(to: contentView, insets: .init(vertical: 8, horizontal: 16))
|
||||
|
||||
formatter.dateStyle = .short
|
||||
formatter.timeStyle = .short
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension VehicleCell: ConfigurableCell {
|
||||
|
||||
func configure(with item: CDVehicle) {
|
||||
func configure(with vehicle: CDVehicle) {
|
||||
guard let number = vehicle.number else {
|
||||
return
|
||||
}
|
||||
|
||||
plateView.number = PlateNumber(number)
|
||||
nameLabel.text = vehicle.brand?.name?.original ?? "<Unknown>"
|
||||
|
||||
if vehicle.unrecognized {
|
||||
plateView.foreground = .systemRed
|
||||
} else if vehicle.outdated {
|
||||
plateView.foreground = .systemGray3
|
||||
} else {
|
||||
plateView.foreground = nil
|
||||
}
|
||||
|
||||
if vehicle.updatedDate - vehicle.addedDate > 60 {
|
||||
addedDateLabel.text = formatter.string(from: Date(timeIntervalSince1970: vehicle.addedDate))
|
||||
updatedDateLabel.text = formatter.string(from: Date(timeIntervalSince1970: vehicle.updatedDate))
|
||||
addedDateLabel.isHidden = false
|
||||
} else {
|
||||
addedDateLabel.isHidden = true
|
||||
updatedDateLabel.text = formatter.string(from: Date(timeIntervalSince1970: vehicle.updatedDate))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
import UIKit
|
||||
import CoreData
|
||||
import AutoCatCore
|
||||
import DifferenceKit
|
||||
|
||||
protocol ConfigurableCell {
|
||||
|
||||
@ -18,28 +19,29 @@ protocol ConfigurableCell {
|
||||
|
||||
typealias ConfigurableTableViewCell = UITableViewCell & ConfigurableCell
|
||||
|
||||
class CoreDataSource<Item: NSManagedObject, Cell: ConfigurableTableViewCell>: NSObject, NSFetchedResultsControllerDelegate where Cell.Item == Item {
|
||||
class CoreDataSource<Item: NSManagedObject & Dated & Differentiable, Cell: ConfigurableTableViewCell>
|
||||
: NSObject, NSFetchedResultsControllerDelegate, UITableViewDataSource where Cell.Item == Item {
|
||||
|
||||
private let cellIdentifier: String
|
||||
private let tableView: UITableView
|
||||
private let dataSource: UITableViewDiffableDataSource<DateSection<Item>, Item>
|
||||
//private let dataSource: UITableViewDiffableDataSource<DateSection<Item>, Item>
|
||||
private let fetchedResults: NSFetchedResultsController<Item>
|
||||
private var sections: [DateSection<Item>] = []
|
||||
|
||||
init(tableView: UITableView, context: NSManagedObjectContext) {
|
||||
let cellIdentifier = String(describing: Cell.self)
|
||||
init(tableView: UITableView, context: NSManagedObjectContext, cellIdentifier: String = String(describing: Cell.self)) {
|
||||
self.tableView = tableView
|
||||
self.cellIdentifier = cellIdentifier
|
||||
|
||||
self.dataSource = UITableViewDiffableDataSource(tableView: tableView) { tv, ip, item in
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier,
|
||||
for: ip) as? Cell
|
||||
cell?.configure(with: item)
|
||||
return cell
|
||||
}
|
||||
// self.dataSource = UITableViewDiffableDataSource(tableView: tableView) { tv, ip, item in
|
||||
// let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier,
|
||||
// for: ip) as? Cell
|
||||
// cell?.configure(with: item)
|
||||
// return cell
|
||||
// }
|
||||
|
||||
if let fetchRequest = Item.fetchRequest() as? NSFetchRequest<Item> {
|
||||
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "number", ascending: true)]
|
||||
let fr = NSFetchRequest<Item>(entityName: "Vehicle")
|
||||
fr.sortDescriptors = []
|
||||
self.fetchedResults = NSFetchedResultsController(fetchRequest: fr,
|
||||
fetchRequest.sortDescriptors = []
|
||||
self.fetchedResults = NSFetchedResultsController(fetchRequest: fetchRequest,
|
||||
managedObjectContext: context,
|
||||
sectionNameKeyPath: nil,
|
||||
cacheName: nil)
|
||||
@ -49,16 +51,74 @@ class CoreDataSource<Item: NSManagedObject, Cell: ConfigurableTableViewCell>: NS
|
||||
|
||||
super.init()
|
||||
|
||||
self.tableView.dataSource = self.dataSource
|
||||
self.tableView.dataSource = self //self.dataSource
|
||||
self.tableView.reloadData()
|
||||
|
||||
self.fetchedResults.delegate = self
|
||||
try? self.fetchedResults.performFetch()
|
||||
|
||||
reload()
|
||||
}
|
||||
|
||||
func reload() {
|
||||
let items: [Item] = fetchedResults.fetchedObjects ?? []
|
||||
|
||||
DispatchQueue.global().async {
|
||||
let newSections = items.groupedByDate()
|
||||
let changeset = StagedChangeset(source: self.sections, target: newSections)
|
||||
DispatchQueue.main.async {
|
||||
print("reloading tableView")
|
||||
self.tableView.reload(using: changeset, with: .fade) { newSects in
|
||||
print("updating sections")
|
||||
self.sections = newSects
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - NSFetchedResultsControllerDelegate
|
||||
|
||||
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) {
|
||||
// func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) {
|
||||
//
|
||||
// let snapshotBridged = snapshot as NSDiffableDataSourceSnapshot<DateSection<Item>, Item>
|
||||
// dataSource.apply(snapshotBridged)
|
||||
// }
|
||||
|
||||
let snapshotBridged = snapshot as NSDiffableDataSourceSnapshot<DateSection<Item>, Item>
|
||||
dataSource.apply(snapshotBridged)
|
||||
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
|
||||
}
|
||||
|
||||
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
|
||||
}
|
||||
|
||||
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) {
|
||||
}
|
||||
|
||||
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
|
||||
|
||||
reload()
|
||||
}
|
||||
|
||||
// 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: 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
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="20086" systemVersion="21A559" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="YES" userDefinedModelVersionIdentifier="">
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="20086" systemVersion="21E230" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="YES" userDefinedModelVersionIdentifier="">
|
||||
<entity name="VBrand" representedClassName=".CDVBrand" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="logo" optional="YES" attributeType="String"/>
|
||||
<relationship name="name" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="VName" inverseName="brand" inverseEntity="VName"/>
|
||||
<relationship name="vehicle" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Vehicle" inverseName="brand" inverseEntity="Vehicle"/>
|
||||
</entity>
|
||||
<entity name="Vehicle" representedClassName=".CDVehicle" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="addedDate" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<attribute name="currentNumber" optional="YES" attributeType="String"/>
|
||||
<attribute name="number" optional="YES" attributeType="String"/>
|
||||
<attribute name="updatedDate" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
|
||||
<relationship name="brand" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="VBrand" inverseName="vehicle" inverseEntity="VBrand"/>
|
||||
</entity>
|
||||
<entity name="VName" representedClassName=".CDVName" syncable="YES" codeGenerationType="class">
|
||||
@ -17,7 +19,7 @@
|
||||
</entity>
|
||||
<elements>
|
||||
<element name="VBrand" positionX="118.7528686523438" positionY="-209.1094360351562" width="128" height="74"/>
|
||||
<element name="Vehicle" positionX="-145.9427490234375" positionY="-363.4805297851562" width="128" height="74"/>
|
||||
<element name="Vehicle" positionX="-145.9427490234375" positionY="-363.4805297851562" width="128" height="104"/>
|
||||
<element name="VName" positionX="-54" positionY="9" width="128" height="74"/>
|
||||
</elements>
|
||||
</model>
|
||||
@ -7,12 +7,18 @@
|
||||
|
||||
import Foundation
|
||||
import SwiftDate
|
||||
import DifferenceKit
|
||||
|
||||
public class DateSection<T: Hashable> {
|
||||
public protocol Dated {
|
||||
var addedAt: Date { get }
|
||||
var updatedAt: Date { get }
|
||||
}
|
||||
|
||||
public class DateSection<T: Hashable & Differentiable>: DifferentiableSection {
|
||||
|
||||
private var timestamp: Double = 0
|
||||
private var header: String
|
||||
private var elements: [T]
|
||||
public private(set) var header: String
|
||||
public private(set) var elements: [T]
|
||||
|
||||
public init(timestamp: Double, items: [T]) {
|
||||
let formatter = DateFormatter()
|
||||
@ -45,6 +51,26 @@ public class DateSection<T: Hashable> {
|
||||
self.timestamp = timestamp
|
||||
self.elements = items
|
||||
}
|
||||
|
||||
public func append(_ element: T) {
|
||||
self.elements.append(element)
|
||||
}
|
||||
|
||||
// MARK: - DifferentiableSection
|
||||
|
||||
public required init<C>(source: DateSection<T>, elements: C) where C : Collection, C.Element == T {
|
||||
self.timestamp = source.timestamp
|
||||
self.header = source.header
|
||||
self.elements = Array(elements)
|
||||
}
|
||||
|
||||
public var differenceIdentifier: String {
|
||||
return header
|
||||
}
|
||||
|
||||
public func isContentEqual(to source: DateSection<T>) -> Bool {
|
||||
return differenceIdentifier == source.differenceIdentifier
|
||||
}
|
||||
}
|
||||
|
||||
extension DateSection: Hashable {
|
||||
@ -58,3 +84,31 @@ extension DateSection: Hashable {
|
||||
hasher.combine(self.elements)
|
||||
}
|
||||
}
|
||||
|
||||
extension RandomAccessCollection where Element: Dated & Hashable & Differentiable {
|
||||
|
||||
public func groupedByDate() -> [DateSection<Element>] {
|
||||
let now = Date()
|
||||
let monthStart = now.dateAtStartOf(.month)
|
||||
var sectionsIndices: [TimeInterval: Int] = [:]
|
||||
var sectionsArray: [DateSection<Element>] = []
|
||||
for item in self {
|
||||
let date = item.updatedAt
|
||||
let dateInRegion = DateInRegion(date, region: Region.current)
|
||||
|
||||
var key = dateInRegion.dateAtStartOf(.day).timeIntervalSince1970
|
||||
if date.isBeforeDate(monthStart, orEqual: false, granularity: .day) {
|
||||
key = dateInRegion.dateAtStartOf(.month).timeIntervalSince1970
|
||||
}
|
||||
|
||||
if let index = sectionsIndices[key] {
|
||||
sectionsArray[index].append(item)
|
||||
} else {
|
||||
sectionsArray.append(DateSection<Element>(timestamp: key, items: [item]))
|
||||
sectionsIndices[key] = sectionsArray.count - 1
|
||||
}
|
||||
}
|
||||
|
||||
return sectionsArray
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,23 +1,35 @@
|
||||
import Foundation
|
||||
import CoreData
|
||||
import DifferenceKit
|
||||
|
||||
public struct Vehicle: Decodable {
|
||||
|
||||
let number: String
|
||||
let currentNumber: String?
|
||||
let brand: VBrand?
|
||||
public let number: String
|
||||
public let currentNumber: String?
|
||||
public let brand: VBrand?
|
||||
public let addedDate: TimeInterval
|
||||
public let updatedDate: TimeInterval
|
||||
}
|
||||
|
||||
// TODO: Remove code duplication
|
||||
public var unrecognized: Bool {
|
||||
return self.brand == nil
|
||||
extension CDVehicle: Dated {
|
||||
|
||||
public var updatedAt: Date {
|
||||
Date(timeIntervalSince1970: updatedDate)
|
||||
}
|
||||
|
||||
public var outdated: Bool {
|
||||
if let current = self.currentNumber {
|
||||
return current != self.number
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
public var addedAt: Date {
|
||||
Date(timeIntervalSince1970: addedDate)
|
||||
}
|
||||
}
|
||||
|
||||
extension CDVehicle: Differentiable {
|
||||
|
||||
public var differenceIdentifier: String { number! }
|
||||
|
||||
public func isContentEqual(to source: CDVehicle) -> Bool {
|
||||
return number == source.number &&
|
||||
addedDate == source.addedDate &&
|
||||
updatedDate == source.updatedDate
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,6 +40,8 @@ extension CDVehicle {
|
||||
self.init(context: context)
|
||||
self.number = vehicle.number
|
||||
self.currentNumber = vehicle.currentNumber
|
||||
self.addedDate = vehicle.addedDate/1000
|
||||
self.updatedDate = vehicle.updatedDate/1000
|
||||
|
||||
if let vbrand = vehicle.brand {
|
||||
self.brand = CDVBrand(vbrand: vbrand, context: context)
|
||||
|
||||
@ -9,14 +9,21 @@ protocol StorageServiceProtocol {
|
||||
|
||||
public class StorageService: StorageServiceProtocol {
|
||||
|
||||
private static var instance: StorageService?
|
||||
|
||||
private let container: NSPersistentCloudKitContainer
|
||||
|
||||
public static var shared: StorageService {
|
||||
get async throws {
|
||||
print("!!!!!!!!!!!!!!!!!!!!!!!!! StorageService init")
|
||||
let service = StorageService()
|
||||
try await service.loadPersistentStores()
|
||||
return service
|
||||
if let instance = StorageService.instance {
|
||||
return instance
|
||||
} else {
|
||||
print("!!!!!!!!!!!!!!!!!!!!!!!!! StorageService init")
|
||||
let service = StorageService()
|
||||
try await service.loadPersistentStores()
|
||||
StorageService.instance = service
|
||||
return service
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user