From efafeb6cb082cb67abd30bccdfc920dff7c52543 Mon Sep 17 00:00:00 2001 From: Selim Mustafaev Date: Wed, 18 Mar 2020 01:29:39 +0300 Subject: [PATCH] Grouping history by dates --- AutoCat.xcodeproj/project.pbxproj | 46 +++++++++++++ .../xcshareddata/swiftpm/Package.resolved | 18 ++++++ .../xcdebugger/Breakpoints_v2.xcbkptlist | 4 +- AutoCat/Controllers/CheckController.swift | 64 +++++++++++++++++-- AutoCat/Models/DateSection.swift | 22 +++++++ AutoCat/Models/Vehicle.swift | 5 +- 6 files changed, 149 insertions(+), 10 deletions(-) create mode 100644 AutoCat/Models/DateSection.swift diff --git a/AutoCat.xcodeproj/project.pbxproj b/AutoCat.xcodeproj/project.pbxproj index 882fd07..377f064 100644 --- a/AutoCat.xcodeproj/project.pbxproj +++ b/AutoCat.xcodeproj/project.pbxproj @@ -7,6 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + 7A051611241412CA00FC55AC /* SwiftDate in Frameworks */ = {isa = PBXBuildFile; productRef = 7A051610241412CA00FC55AC /* SwiftDate */; }; + 7A0516162414EC1200FC55AC /* Differentiator in Frameworks */ = {isa = PBXBuildFile; productRef = 7A0516152414EC1200FC55AC /* Differentiator */; }; + 7A0516182414EC1200FC55AC /* RxDataSources in Frameworks */ = {isa = PBXBuildFile; productRef = 7A0516172414EC1200FC55AC /* RxDataSources */; }; + 7A05161A2414FF0900FC55AC /* DateSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A0516192414FF0900FC55AC /* DateSection.swift */; }; 7A11470123FDE7E500B424AF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A11470023FDE7E500B424AF /* AppDelegate.swift */; }; 7A11470323FDE7E500B424AF /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A11470223FDE7E500B424AF /* SceneDelegate.swift */; }; 7A11470823FDE7E500B424AF /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7A11470623FDE7E500B424AF /* Main.storyboard */; }; @@ -48,6 +52,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 7A0516192414FF0900FC55AC /* DateSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateSection.swift; sourceTree = ""; }; 7A1146FD23FDE7E500B424AF /* AutoCat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AutoCat.app; sourceTree = BUILT_PRODUCTS_DIR; }; 7A11470023FDE7E500B424AF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7A11470223FDE7E500B424AF /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -85,9 +90,12 @@ buildActionMask = 2147483647; files = ( 7AF58D342402A91C00CE01A0 /* Kingfisher in Frameworks */, + 7A0516162414EC1200FC55AC /* Differentiator in Frameworks */, 7A11472823FEA1F400B424AF /* RealmSwift in Frameworks */, 7A11472123FEA18700B424AF /* RxCocoa in Frameworks */, 7A11474E23FFEE8800B424AF /* SVProgressHUD.framework in Frameworks */, + 7A0516182414EC1200FC55AC /* RxDataSources in Frameworks */, + 7A051611241412CA00FC55AC /* SwiftDate in Frameworks */, 7A11472323FEA18700B424AF /* RxBlocking in Frameworks */, 7A530B8B240181F500CBFE6E /* RxRealm in Frameworks */, 7A11472B23FEA24D00B424AF /* Action in Frameworks */, @@ -173,6 +181,7 @@ 7A11474823FF2B2D00B424AF /* Response.swift */, 7A11474A23FF368B00B424AF /* Settings.swift */, 7A530B7F2401803A00CBFE6E /* Vehicle.swift */, + 7A0516192414FF0900FC55AC /* DateSection.swift */, ); path = Models; sourceTree = ""; @@ -239,6 +248,9 @@ 7A530B8A240181F500CBFE6E /* RxRealm */, 7AF58D2E24029C5200CE01A0 /* MagazineLayout */, 7AF58D332402A91C00CE01A0 /* Kingfisher */, + 7A051610241412CA00FC55AC /* SwiftDate */, + 7A0516152414EC1200FC55AC /* Differentiator */, + 7A0516172414EC1200FC55AC /* RxDataSources */, ); productName = AutoCat; productReference = 7A1146FD23FDE7E500B424AF /* AutoCat.app */; @@ -276,6 +288,8 @@ 7A530B89240181F500CBFE6E /* XCRemoteSwiftPackageReference "RxRealm" */, 7AF58D2D24029C5200CE01A0 /* XCRemoteSwiftPackageReference "MagazineLayout" */, 7AF58D322402A91C00CE01A0 /* XCRemoteSwiftPackageReference "Kingfisher" */, + 7A05160F241412CA00FC55AC /* XCRemoteSwiftPackageReference "SwiftDate" */, + 7A0516142414EC1200FC55AC /* XCRemoteSwiftPackageReference "RxDataSources" */, ); productRefGroup = 7A1146FE23FDE7E500B424AF /* Products */; projectDirPath = ""; @@ -346,6 +360,7 @@ 7A530B7E24017FEE00CBFE6E /* VehicleCell.swift in Sources */, 7A530B87240180CC00CBFE6E /* Reactive+RxRealmDataSources.swift in Sources */, 7A11474423FF06CA00B424AF /* Api.swift in Sources */, + 7A05161A2414FF0900FC55AC /* DateSection.swift in Sources */, 7A11474B23FF368B00B424AF /* Settings.swift in Sources */, 7A7547E024032CB6004E8406 /* VehiclePhotoCell.swift in Sources */, ); @@ -559,6 +574,22 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ + 7A05160F241412CA00FC55AC /* XCRemoteSwiftPackageReference "SwiftDate" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/malcommac/SwiftDate.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 6.1.0; + }; + }; + 7A0516142414EC1200FC55AC /* XCRemoteSwiftPackageReference "RxDataSources" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/RxSwiftCommunity/RxDataSources"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 4.0.1; + }; + }; 7A11471B23FEA18700B424AF /* XCRemoteSwiftPackageReference "RxSwift" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/ReactiveX/RxSwift.git"; @@ -618,6 +649,21 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 7A051610241412CA00FC55AC /* SwiftDate */ = { + isa = XCSwiftPackageProductDependency; + package = 7A05160F241412CA00FC55AC /* XCRemoteSwiftPackageReference "SwiftDate" */; + productName = SwiftDate; + }; + 7A0516152414EC1200FC55AC /* Differentiator */ = { + isa = XCSwiftPackageProductDependency; + package = 7A0516142414EC1200FC55AC /* XCRemoteSwiftPackageReference "RxDataSources" */; + productName = Differentiator; + }; + 7A0516172414EC1200FC55AC /* RxDataSources */ = { + isa = XCSwiftPackageProductDependency; + package = 7A0516142414EC1200FC55AC /* XCRemoteSwiftPackageReference "RxDataSources" */; + productName = RxDataSources; + }; 7A11471C23FEA18700B424AF /* RxSwift */ = { isa = XCSwiftPackageProductDependency; package = 7A11471B23FEA18700B424AF /* XCRemoteSwiftPackageReference "RxSwift" */; diff --git a/AutoCat.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/AutoCat.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 7b73d97..551a351 100644 --- a/AutoCat.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/AutoCat.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -55,6 +55,15 @@ "version": "5.23.8" } }, + { + "package": "RxDataSources", + "repositoryURL": "https://github.com/RxSwiftCommunity/RxDataSources", + "state": { + "branch": null, + "revision": "a18cee01c9ee55f04d0c7eb15c77a96c3648c88e", + "version": "4.0.1" + } + }, { "package": "RxRealm", "repositoryURL": "https://github.com/RxSwiftCommunity/RxRealm", @@ -72,6 +81,15 @@ "revision": "b3e888b4972d9bc76495dd74d30a8c7fad4b9395", "version": "5.0.1" } + }, + { + "package": "SwiftDate", + "repositoryURL": "https://github.com/malcommac/SwiftDate.git", + "state": { + "branch": null, + "revision": "700dde1b3417fd4a7fd2bec9f5bc8ab2de1b96b4", + "version": "6.1.0" + } } ] }, diff --git a/AutoCat.xcodeproj/xcuserdata/selim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/AutoCat.xcodeproj/xcuserdata/selim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 07e3a91..4d0ec99 100644 --- a/AutoCat.xcodeproj/xcuserdata/selim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/AutoCat.xcodeproj/xcuserdata/selim.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -8,7 +8,7 @@ BreakpointExtensionID = "Xcode.Breakpoint.SwiftErrorBreakpoint"> @@ -17,7 +17,7 @@ BreakpointExtensionID = "Xcode.Breakpoint.ExceptionBreakpoint"> (cellIdentifier: "VehicleCell", cellType: VehicleCell.self) { cell, ip, vehicle in - cell.configure(with: vehicle) +// let ds = RxTableViewRealmDataSource(cellIdentifier: "VehicleCell", cellType: VehicleCell.self) { cell, ip, vehicle in +// cell.configure(with: vehicle) +// } +// ds.headerTitle = "History" + + let ds = RxTableViewSectionedAnimatedDataSource>(configureCell: { dataSource, tableView, indexPath, item in + if let cell = tableView.dequeueReusableCell(withIdentifier: "VehicleCell", for: indexPath) as? VehicleCell { + cell.configure(with: item) + return cell + } else { + return UITableViewCell() + } + }) + + ds.titleForHeaderInSection = { dataSourse, index in + return dataSourse.sectionModels[index].header } - ds.headerTitle = "History" let realm = try! Realm() - Observable.changeset(from: realm.objects(Vehicle.self).sorted(byKeyPath: "addedDate", ascending: false)) - .bind(to: self.history.rx.realmChanges(ds)) - .disposed(by: self.bag) +// Observable.changeset(from: realm.objects(Vehicle.self).sorted(byKeyPath: "addedDate", ascending: false)) +// .bind(to: self.history.rx.realmChanges(ds)) +// .disposed(by: self.bag) self.history.rx.realmModelSelected(Vehicle.self) .subscribe(onNext: self.updateDetailController(with:)) .disposed(by: self.bag) + + Observable.collection(from: realm.objects(Vehicle.self).sorted(byKeyPath: "addedDate", ascending: false)).map { (vehicles: Results) -> [DateSection] in + var sections: [TimeInterval: [Vehicle]] = [:] + for vehicle in vehicles { + let date = Date(timeIntervalSince1970: vehicle.addedDate) + let key = date.dateAtStartOf(.weekday).timeIntervalSince1970 + print("==== Key: \(key)") + if sections[key] == nil { + sections[key] = [vehicle] + } else { + sections[key]?.append(vehicle) + } + } + + let formatter = DateFormatter() + formatter.dateStyle = .medium + formatter.timeStyle = .medium + var sectionsArray: [DateSection] = [] + for (timestamp, vehicles) in sections { + let dateStr = formatter.string(from: Date(timeIntervalSince1970: timestamp/1000)) + sectionsArray.append(DateSection(header: dateStr, items: vehicles)) + } + + print("==========================") + for s in sectionsArray { + print(s.header) + } + + return sectionsArray + } + .bind(to: self.history.rx.items(dataSource: ds)) + .disposed(by: self.bag) } override func viewWillAppear(_ animated: Bool) { @@ -44,6 +92,7 @@ class CheckController: UIViewController, MaskedTextFieldDelegateListener { @IBAction func checkTapped(_ sender: UIButton) { guard let number = self.number.text else { return } + self.number.resignFirstResponder() self.number.text = nil SVProgressHUD.show() Api.checkVehicle(by: number) @@ -60,7 +109,6 @@ class CheckController: UIViewController, MaskedTextFieldDelegateListener { } func textFieldShouldReturn(_ textField: UITextField) -> Bool { - textField.resignFirstResponder() if self.check.isEnabled { self.checkTapped(self.check) } @@ -96,4 +144,6 @@ class CheckController: UIViewController, MaskedTextFieldDelegateListener { } } } + + } diff --git a/AutoCat/Models/DateSection.swift b/AutoCat/Models/DateSection.swift new file mode 100644 index 0000000..f63451a --- /dev/null +++ b/AutoCat/Models/DateSection.swift @@ -0,0 +1,22 @@ +import Foundation +import RxDataSources + +struct DateSection: AnimatableSectionModelType where T: IdentifiableType, T: Equatable { + + var header: String + var items: [T] + + var identity: String { + return header + } + + init(original: DateSection, items: [T]) { + self = original + self.items = items + } + + init(header: String, items: [T]) { + self.header = header + self.items = items + } +} diff --git a/AutoCat/Models/Vehicle.swift b/AutoCat/Models/Vehicle.swift index 3549383..477c226 100644 --- a/AutoCat/Models/Vehicle.swift +++ b/AutoCat/Models/Vehicle.swift @@ -1,5 +1,6 @@ import Foundation import RealmSwift +import RxDataSources class VehicleName: Object, Decodable { @objc dynamic var original: String? @@ -39,7 +40,7 @@ class VehiclePhoto: Object, Decodable { } } -class Vehicle: Object, Decodable { +class Vehicle: Object, Decodable, IdentifiableType { @objc dynamic var _id: String = "" @objc dynamic var brand: VehicleBrand? @objc dynamic var model: VehicleModel? @@ -58,6 +59,8 @@ class Vehicle: Object, Decodable { @objc dynamic var addedBy: String = "" let photos = List() + var identity: String { _id } + enum CodingKeys: String, CodingKey { case _id case brand