Refactoring of displaying realm results in a sectioned list
This commit is contained in:
parent
cbfe729440
commit
1ab4de19e0
@ -88,6 +88,7 @@
|
|||||||
7A96AE2F246B2BCD00297C33 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A96AE2E246B2BCD00297C33 /* WebKit.framework */; };
|
7A96AE2F246B2BCD00297C33 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A96AE2E246B2BCD00297C33 /* WebKit.framework */; };
|
||||||
7A96AE31246B2FE400297C33 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A96AE30246B2FE400297C33 /* Constants.swift */; };
|
7A96AE31246B2FE400297C33 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A96AE30246B2FE400297C33 /* Constants.swift */; };
|
||||||
7A96AE33246C095700297C33 /* Base64FS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A96AE32246C095700297C33 /* Base64FS.swift */; };
|
7A96AE33246C095700297C33 /* Base64FS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A96AE32246C095700297C33 /* Base64FS.swift */; };
|
||||||
|
7A9FEEC82529AB23001CA50E /* RxRealmDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9FEEC72529AB23001CA50E /* RxRealmDataSource.swift */; };
|
||||||
7AAE6AD324CDDF950023860B /* VehicleEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AAE6AD224CDDF950023860B /* VehicleEvent.swift */; };
|
7AAE6AD324CDDF950023860B /* VehicleEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AAE6AD224CDDF950023860B /* VehicleEvent.swift */; };
|
||||||
7AB562BA249C9E9B00473D53 /* Region.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB562B9249C9E9B00473D53 /* Region.swift */; };
|
7AB562BA249C9E9B00473D53 /* Region.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB562B9249C9E9B00473D53 /* Region.swift */; };
|
||||||
7AB67E8C2435C38700258F61 /* CustomTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB67E8B2435C38700258F61 /* CustomTextField.swift */; };
|
7AB67E8C2435C38700258F61 /* CustomTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB67E8B2435C38700258F61 /* CustomTextField.swift */; };
|
||||||
@ -102,6 +103,7 @@
|
|||||||
7AE24C5F251F1B4E00758E39 /* Buttons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE24C5E251F1B4E00758E39 /* Buttons.swift */; };
|
7AE24C5F251F1B4E00758E39 /* Buttons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE24C5E251F1B4E00758E39 /* Buttons.swift */; };
|
||||||
7AE26A3324EEF9EC00625033 /* UIViewControllerExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE26A3224EEF9EC00625033 /* UIViewControllerExt.swift */; };
|
7AE26A3324EEF9EC00625033 /* UIViewControllerExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE26A3224EEF9EC00625033 /* UIViewControllerExt.swift */; };
|
||||||
7AE26A3524F31B0700625033 /* EventsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE26A3424F31B0700625033 /* EventsController.swift */; };
|
7AE26A3524F31B0700625033 /* EventsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE26A3424F31B0700625033 /* EventsController.swift */; };
|
||||||
|
7AEFC3BE2529D3CC00BADFB2 /* ConfigurableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEFC3BD2529D3CC00BADFB2 /* ConfigurableCell.swift */; };
|
||||||
7AEFE728240455E200910EB7 /* SettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEFE727240455E200910EB7 /* SettingsController.swift */; };
|
7AEFE728240455E200910EB7 /* SettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEFE727240455E200910EB7 /* SettingsController.swift */; };
|
||||||
7AF58D2F24029C5200CE01A0 /* MagazineLayout in Frameworks */ = {isa = PBXBuildFile; productRef = 7AF58D2E24029C5200CE01A0 /* MagazineLayout */; };
|
7AF58D2F24029C5200CE01A0 /* MagazineLayout in Frameworks */ = {isa = PBXBuildFile; productRef = 7AF58D2E24029C5200CE01A0 /* MagazineLayout */; };
|
||||||
7AF58D3124029E1000CE01A0 /* VehicleHeaderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF58D3024029E1000CE01A0 /* VehicleHeaderCell.swift */; };
|
7AF58D3124029E1000CE01A0 /* VehicleHeaderCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF58D3024029E1000CE01A0 /* VehicleHeaderCell.swift */; };
|
||||||
@ -184,6 +186,7 @@
|
|||||||
7A96AE2E246B2BCD00297C33 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/WebKit.framework; sourceTree = DEVELOPER_DIR; };
|
7A96AE2E246B2BCD00297C33 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/WebKit.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
7A96AE30246B2FE400297C33 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
|
7A96AE30246B2FE400297C33 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
|
||||||
7A96AE32246C095700297C33 /* Base64FS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Base64FS.swift; sourceTree = "<group>"; };
|
7A96AE32246C095700297C33 /* Base64FS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Base64FS.swift; sourceTree = "<group>"; };
|
||||||
|
7A9FEEC72529AB23001CA50E /* RxRealmDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RxRealmDataSource.swift; sourceTree = "<group>"; };
|
||||||
7AAE6AD224CDDF950023860B /* VehicleEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleEvent.swift; sourceTree = "<group>"; };
|
7AAE6AD224CDDF950023860B /* VehicleEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleEvent.swift; sourceTree = "<group>"; };
|
||||||
7AB562B9249C9E9B00473D53 /* Region.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Region.swift; sourceTree = "<group>"; };
|
7AB562B9249C9E9B00473D53 /* Region.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Region.swift; sourceTree = "<group>"; };
|
||||||
7AB67E8B2435C38700258F61 /* CustomTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTextField.swift; sourceTree = "<group>"; };
|
7AB67E8B2435C38700258F61 /* CustomTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTextField.swift; sourceTree = "<group>"; };
|
||||||
@ -198,6 +201,7 @@
|
|||||||
7AE24C5E251F1B4E00758E39 /* Buttons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Buttons.swift; sourceTree = "<group>"; };
|
7AE24C5E251F1B4E00758E39 /* Buttons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Buttons.swift; sourceTree = "<group>"; };
|
||||||
7AE26A3224EEF9EC00625033 /* UIViewControllerExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewControllerExt.swift; sourceTree = "<group>"; };
|
7AE26A3224EEF9EC00625033 /* UIViewControllerExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewControllerExt.swift; sourceTree = "<group>"; };
|
||||||
7AE26A3424F31B0700625033 /* EventsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventsController.swift; sourceTree = "<group>"; };
|
7AE26A3424F31B0700625033 /* EventsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventsController.swift; sourceTree = "<group>"; };
|
||||||
|
7AEFC3BD2529D3CC00BADFB2 /* ConfigurableCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurableCell.swift; sourceTree = "<group>"; };
|
||||||
7AEFE727240455E200910EB7 /* SettingsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsController.swift; sourceTree = "<group>"; };
|
7AEFE727240455E200910EB7 /* SettingsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsController.swift; sourceTree = "<group>"; };
|
||||||
7AF58D3024029E1000CE01A0 /* VehicleHeaderCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleHeaderCell.swift; sourceTree = "<group>"; };
|
7AF58D3024029E1000CE01A0 /* VehicleHeaderCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleHeaderCell.swift; sourceTree = "<group>"; };
|
||||||
7AF58D57240309CA00CE01A0 /* VehicleTextParamCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleTextParamCell.swift; sourceTree = "<group>"; };
|
7AF58D57240309CA00CE01A0 /* VehicleTextParamCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleTextParamCell.swift; sourceTree = "<group>"; };
|
||||||
@ -311,6 +315,7 @@
|
|||||||
7A27ADF6249FEF690035F39E /* Recorder.swift */,
|
7A27ADF6249FEF690035F39E /* Recorder.swift */,
|
||||||
7A1090E924A3A26300B4F0B2 /* AudioPlayer.swift */,
|
7A1090E924A3A26300B4F0B2 /* AudioPlayer.swift */,
|
||||||
7A000AA124C2EEDE001F5B00 /* Location.swift */,
|
7A000AA124C2EEDE001F5B00 /* Location.swift */,
|
||||||
|
7A9FEEC72529AB23001CA50E /* RxRealmDataSource.swift */,
|
||||||
);
|
);
|
||||||
path = Utils;
|
path = Utils;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -383,6 +388,7 @@
|
|||||||
7A7547DF24032CB6004E8406 /* VehiclePhotoCell.swift */,
|
7A7547DF24032CB6004E8406 /* VehiclePhotoCell.swift */,
|
||||||
7A1090E724A394F100B4F0B2 /* AudioRecordCell.swift */,
|
7A1090E724A394F100B4F0B2 /* AudioRecordCell.swift */,
|
||||||
7A813DC22508EE4F00CC93B9 /* EventCell.swift */,
|
7A813DC22508EE4F00CC93B9 /* EventCell.swift */,
|
||||||
|
7AEFC3BD2529D3CC00BADFB2 /* ConfigurableCell.swift */,
|
||||||
);
|
);
|
||||||
path = Cells;
|
path = Cells;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -550,6 +556,7 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
7AEFC3BE2529D3CC00BADFB2 /* ConfigurableCell.swift in Sources */,
|
||||||
7A96AE33246C095700297C33 /* Base64FS.swift in Sources */,
|
7A96AE33246C095700297C33 /* Base64FS.swift in Sources */,
|
||||||
7A530B802401803A00CBFE6E /* Vehicle.swift in Sources */,
|
7A530B802401803A00CBFE6E /* Vehicle.swift in Sources */,
|
||||||
7A96AE31246B2FE400297C33 /* Constants.swift in Sources */,
|
7A96AE31246B2FE400297C33 /* Constants.swift in Sources */,
|
||||||
@ -610,6 +617,7 @@
|
|||||||
7A813DCB250B5DC900CC93B9 /* LocationPickerController.swift in Sources */,
|
7A813DCB250B5DC900CC93B9 /* LocationPickerController.swift in Sources */,
|
||||||
7A11474423FF06CA00B424AF /* Api.swift in Sources */,
|
7A11474423FF06CA00B424AF /* Api.swift in Sources */,
|
||||||
7A488C3D24A74B990054D0B2 /* RxCollectionViewRealmDataSource.swift in Sources */,
|
7A488C3D24A74B990054D0B2 /* RxCollectionViewRealmDataSource.swift in Sources */,
|
||||||
|
7A9FEEC82529AB23001CA50E /* RxRealmDataSource.swift in Sources */,
|
||||||
7AB67E8E2435D1A000258F61 /* CustomButton.swift in Sources */,
|
7AB67E8E2435D1A000258F61 /* CustomButton.swift in Sources */,
|
||||||
7A21112A24FC3D7E003BBF6F /* AudioEngine.swift in Sources */,
|
7A21112A24FC3D7E003BBF6F /* AudioEngine.swift in Sources */,
|
||||||
7A8A220B248D67B60073DFD9 /* VehicleReportImage.swift in Sources */,
|
7A8A220B248D67B60073DFD9 /* VehicleReportImage.swift in Sources */,
|
||||||
|
|||||||
@ -6,8 +6,8 @@
|
|||||||
"repositoryURL": "https://github.com/xmartlabs/Eureka",
|
"repositoryURL": "https://github.com/xmartlabs/Eureka",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "9fe153d2be663621d4f53196bf1b7effb83d7017",
|
"revision": "ae025915335f02e7277ed5446b4de002bab05866",
|
||||||
"version": "5.3.0"
|
"version": "5.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -24,8 +24,8 @@
|
|||||||
"repositoryURL": "https://github.com/onevcat/Kingfisher",
|
"repositoryURL": "https://github.com/onevcat/Kingfisher",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "22b795dd57766fa0e86773d8733efb31dc0f31fb",
|
"revision": "2a6d1135af3915547c4b08c3b154a05e6f1075a3",
|
||||||
"version": "5.15.1"
|
"version": "5.15.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -33,8 +33,8 @@
|
|||||||
"repositoryURL": "https://github.com/airbnb/MagazineLayout",
|
"repositoryURL": "https://github.com/airbnb/MagazineLayout",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "4a5eff2203ad8d8c7e14ea1b283b64f9320752a9",
|
"revision": "6f88742c282de208e48cb738a7a14b7dc2651701",
|
||||||
"version": "1.6.2"
|
"version": "1.6.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -42,8 +42,8 @@
|
|||||||
"repositoryURL": "https://github.com/realm/realm-cocoa",
|
"repositoryURL": "https://github.com/realm/realm-cocoa",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "f64ac045d8cb171d8e317d9b854df7215aed7466",
|
"revision": "2dc2d259095051b997b76a07e859822661105303",
|
||||||
"version": "5.4.2"
|
"version": "5.4.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -51,8 +51,8 @@
|
|||||||
"repositoryURL": "https://github.com/realm/realm-core",
|
"repositoryURL": "https://github.com/realm/realm-core",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "e051fc73c56830bf3ab0b8a82f7a613968cec6c6",
|
"revision": "2df510904ad04287926b287b4e89b786de2808c8",
|
||||||
"version": "6.0.26"
|
"version": "6.1.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -7,28 +7,28 @@
|
|||||||
<key>AutoCat.xcscheme_^#shared#^_</key>
|
<key>AutoCat.xcscheme_^#shared#^_</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>1</integer>
|
<integer>0</integer>
|
||||||
</dict>
|
</dict>
|
||||||
<key>Eureka (Playground) 1.xcscheme</key>
|
<key>Eureka (Playground) 1.xcscheme</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>isShown</key>
|
<key>isShown</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>5</integer>
|
<integer>3</integer>
|
||||||
</dict>
|
</dict>
|
||||||
<key>Eureka (Playground) 2.xcscheme</key>
|
<key>Eureka (Playground) 2.xcscheme</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>isShown</key>
|
<key>isShown</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>6</integer>
|
<integer>4</integer>
|
||||||
</dict>
|
</dict>
|
||||||
<key>Eureka (Playground).xcscheme</key>
|
<key>Eureka (Playground).xcscheme</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>isShown</key>
|
<key>isShown</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>4</integer>
|
<integer>2</integer>
|
||||||
</dict>
|
</dict>
|
||||||
<key>GettingStarted (Playground) 1.xcscheme</key>
|
<key>GettingStarted (Playground) 1.xcscheme</key>
|
||||||
<dict>
|
<dict>
|
||||||
@ -77,21 +77,21 @@
|
|||||||
<key>isShown</key>
|
<key>isShown</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>2</integer>
|
<integer>6</integer>
|
||||||
</dict>
|
</dict>
|
||||||
<key>Rx (Playground) 2.xcscheme</key>
|
<key>Rx (Playground) 2.xcscheme</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>isShown</key>
|
<key>isShown</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>3</integer>
|
<integer>7</integer>
|
||||||
</dict>
|
</dict>
|
||||||
<key>Rx (Playground).xcscheme</key>
|
<key>Rx (Playground).xcscheme</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>isShown</key>
|
<key>isShown</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>0</integer>
|
<integer>5</integer>
|
||||||
</dict>
|
</dict>
|
||||||
<key>SwiftDate (Playground) 1.xcscheme</key>
|
<key>SwiftDate (Playground) 1.xcscheme</key>
|
||||||
<dict>
|
<dict>
|
||||||
@ -133,7 +133,15 @@
|
|||||||
<key>isShown</key>
|
<key>isShown</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>orderHint</key>
|
<key>orderHint</key>
|
||||||
<integer>7</integer>
|
<integer>1</integer>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
<key>SuppressBuildableAutocreation</key>
|
||||||
|
<dict>
|
||||||
|
<key>7A1146FC23FDE7E500B424AF</key>
|
||||||
|
<dict>
|
||||||
|
<key>primary</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
|
|||||||
@ -24,7 +24,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||||
|
|
||||||
let config = Realm.Configuration(
|
let config = Realm.Configuration(
|
||||||
schemaVersion: 18,
|
schemaVersion: 19,
|
||||||
migrationBlock: { migration, oldSchemaVersion in
|
migrationBlock: { migration, oldSchemaVersion in
|
||||||
if oldSchemaVersion <= 3 {
|
if oldSchemaVersion <= 3 {
|
||||||
var numbers: [String] = []
|
var numbers: [String] = []
|
||||||
@ -44,6 +44,26 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||||||
new!["isRightWheel"] = RealmOptional<Bool>(old!["isRightWheel"] as? Bool)
|
new!["isRightWheel"] = RealmOptional<Bool>(old!["isRightWheel"] as? Bool)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if oldSchemaVersion <= 18 {
|
||||||
|
migration.enumerateObjects(ofType: "Vehicle") { old, new in
|
||||||
|
let addedDate = old!["addedDate"] as! TimeInterval
|
||||||
|
let events = old!.dynamicList("events")
|
||||||
|
new!["addedDate"] = addedDate/1000
|
||||||
|
|
||||||
|
let lastEvent = events.max { first, second in
|
||||||
|
let firstDate = first["date"] as! TimeInterval
|
||||||
|
let secondDate = second["date"] as! TimeInterval
|
||||||
|
return firstDate < secondDate
|
||||||
|
}
|
||||||
|
|
||||||
|
if let lastEvent = lastEvent {
|
||||||
|
new!["updatedDate"] = max(addedDate/1000, lastEvent["date"] as! TimeInterval)
|
||||||
|
} else {
|
||||||
|
new!["updatedDate"] = addedDate/1000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
Realm.Configuration.defaultConfiguration = config
|
Realm.Configuration.defaultConfiguration = config
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
import RxSwift
|
import RxSwift
|
||||||
|
|
||||||
class AudioRecordCell: UITableViewCell {
|
class AudioRecordCell: UITableViewCell, ConfigurableCell {
|
||||||
|
|
||||||
@IBOutlet weak var playButton: UIButton!
|
@IBOutlet weak var playButton: UIButton!
|
||||||
@IBOutlet weak var duration: UILabel!
|
@IBOutlet weak var duration: UILabel!
|
||||||
|
|||||||
6
AutoCat/Cells/ConfigurableCell.swift
Normal file
6
AutoCat/Cells/ConfigurableCell.swift
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import UIKit
|
||||||
|
|
||||||
|
protocol ConfigurableCell {
|
||||||
|
associatedtype Item
|
||||||
|
func configure(with item: Item)
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class VehicleCell: UITableViewCell {
|
class VehicleCell: UITableViewCell, ConfigurableCell {
|
||||||
|
|
||||||
@IBOutlet weak var name: UILabel!
|
@IBOutlet weak var name: UILabel!
|
||||||
@IBOutlet weak var plate: PlateView!
|
@IBOutlet weak var plate: PlateView!
|
||||||
@ -25,7 +25,7 @@ class VehicleCell: UITableViewCell {
|
|||||||
} else {
|
} else {
|
||||||
self.plate.foreground = nil
|
self.plate.foreground = nil
|
||||||
}
|
}
|
||||||
self.date.text = formatter.string(from: Date(timeIntervalSince1970: vehicle.addedDate/1000))
|
self.date.text = formatter.string(from: Date(timeIntervalSince1970: vehicle.addedDate))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import RealmSwift
|
|||||||
import RxSwift
|
import RxSwift
|
||||||
import SwiftDate
|
import SwiftDate
|
||||||
import RxRealm
|
import RxRealm
|
||||||
import RxDataSources
|
|
||||||
|
|
||||||
enum EventAction {
|
enum EventAction {
|
||||||
case doNotSend
|
case doNotSend
|
||||||
@ -18,6 +17,7 @@ class CheckController: UIViewController, UITableViewDelegate, UITextFieldDelegat
|
|||||||
@IBOutlet weak var history: UITableView!
|
@IBOutlet weak var history: UITableView!
|
||||||
|
|
||||||
let bag = DisposeBag()
|
let bag = DisposeBag()
|
||||||
|
var historyDataSource: RealmSectionedDataSource<Vehicle, VehicleCell>!
|
||||||
|
|
||||||
// MARK: - Lifecycle
|
// MARK: - Lifecycle
|
||||||
|
|
||||||
@ -33,32 +33,11 @@ class CheckController: UIViewController, UITableViewDelegate, UITextFieldDelegat
|
|||||||
self.number.inputView = keyboard
|
self.number.inputView = keyboard
|
||||||
self.check.isEnabled = false
|
self.check.isEnabled = false
|
||||||
|
|
||||||
let ds = RxTableViewSectionedAnimatedDataSource<DateSection<Vehicle>>(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()
|
|
||||||
}
|
|
||||||
}, canEditRowAtIndexPath: { _, _ in true })
|
|
||||||
|
|
||||||
ds.titleForHeaderInSection = { dataSourse, index in
|
|
||||||
return dataSourse.sectionModels[index].header
|
|
||||||
}
|
|
||||||
|
|
||||||
self.history.rx.modelSelected(Vehicle.self)
|
|
||||||
.subscribe(onNext: self.updateDetailController(with:))
|
|
||||||
.disposed(by: self.bag)
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
Observable.collection(from: realm.objects(Vehicle.self)
|
self.historyDataSource = RealmSectionedDataSource(table: self.history, data: realm.objects(Vehicle.self).sorted(byKeyPath: "updatedDate", ascending: false))
|
||||||
.sorted(byKeyPath: "addedDate", ascending: false))
|
|
||||||
.map { $0.groupedByDate() }
|
|
||||||
.bind(to: self.history.rx.items(dataSource: ds))
|
|
||||||
.disposed(by: self.bag)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.history.rx.setDelegate(self).disposed(by: self.bag)
|
self.history.delegate = self
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
@ -94,7 +73,7 @@ class CheckController: UIViewController, UITableViewDelegate, UITextFieldDelegat
|
|||||||
if let event = event {
|
if let event = event {
|
||||||
action = .sendSpecific(event)
|
action = .sendSpecific(event)
|
||||||
}
|
}
|
||||||
self.check(number: number, action: action)
|
self.check(number: number, action: action).subscribe().disposed(by: self.bag)
|
||||||
break
|
break
|
||||||
case .addVoiceRecord:
|
case .addVoiceRecord:
|
||||||
self.tabBarController?.selectedIndex = 1
|
self.tabBarController?.selectedIndex = 1
|
||||||
@ -116,83 +95,19 @@ class CheckController: UIViewController, UITableViewDelegate, UITextFieldDelegat
|
|||||||
guard let number = self.number.text else { return }
|
guard let number = self.number.text else { return }
|
||||||
|
|
||||||
let numberNormalized = number.filter { !$0.isWhitespace }.uppercased()
|
let numberNormalized = number.filter { !$0.isWhitespace }.uppercased()
|
||||||
self.check(number: numberNormalized, action: .receiveAndSend)
|
|
||||||
}
|
|
||||||
|
|
||||||
func check(number: String, action: EventAction) {
|
|
||||||
self.number.resignFirstResponder()
|
self.number.resignFirstResponder()
|
||||||
self.number.text = nil
|
self.number.text = nil
|
||||||
self.check.isEnabled = false
|
self.check.isEnabled = false
|
||||||
IHProgressHUD.show()
|
IHProgressHUD.show()
|
||||||
Api.checkVehicle(by: number)
|
|
||||||
.observeOn(MainScheduler.instance)
|
|
||||||
.subscribe(onSuccess: { vehicle in
|
|
||||||
self.onReceivedVehicle(vehicle, action: action)
|
|
||||||
}, onError: { err in
|
|
||||||
if let realm = try? Realm() {
|
|
||||||
let vehicle = Vehicle(number)
|
|
||||||
try? realm.write {
|
|
||||||
realm.add(vehicle, update: .all)
|
|
||||||
}
|
|
||||||
|
|
||||||
var eventSingle = self.getEvent()
|
|
||||||
if case .sendSpecific(let event) = action {
|
|
||||||
eventSingle = Single.just(event)
|
|
||||||
}
|
|
||||||
eventSingle
|
|
||||||
.flatMap { event in event.findAddress().map{ [event] }.catchErrorJustReturn([event]) }
|
|
||||||
.catchErrorJustReturn([])
|
|
||||||
.subscribe(onSuccess: { events in
|
|
||||||
try? realm.write {
|
|
||||||
vehicle.events.append(objectsIn: events)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.disposed(by: self.bag)
|
|
||||||
}
|
|
||||||
IHProgressHUD.showError(withStatus: err.localizedDescription)
|
|
||||||
print(err.localizedDescription)
|
|
||||||
}).disposed(by: self.bag)
|
|
||||||
}
|
|
||||||
|
|
||||||
func save(vehicle: Vehicle) {
|
|
||||||
if let realm = try? Realm() {
|
|
||||||
try? realm.write {
|
|
||||||
realm.add(vehicle, update: .all)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getEvent() -> Single<VehicleEvent> {
|
|
||||||
if let event = LocationManager.lastEvent, (Date().timeIntervalSince1970 - event.date) < 30 {
|
|
||||||
print("Using last event")
|
|
||||||
return Single<VehicleEvent>.just(event)
|
|
||||||
} else {
|
|
||||||
print("requesting new event")
|
|
||||||
return LocationManager.requestCurrentLocation()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func onReceivedVehicle(_ vehicle: Vehicle, action: EventAction = .receiveAndSend) {
|
|
||||||
self.save(vehicle: vehicle)
|
|
||||||
|
|
||||||
if case .doNotSend = action {
|
|
||||||
// Just do nothing
|
|
||||||
} else {
|
|
||||||
var eventSingle = self.getEvent()
|
|
||||||
if case .sendSpecific(let event) = action {
|
|
||||||
eventSingle = Single.just(event)
|
|
||||||
}
|
|
||||||
eventSingle
|
|
||||||
.flatMap { event in event.findAddress().map{ event }.catchErrorJustReturn(event) }
|
|
||||||
.flatMap {
|
|
||||||
Api.add(event: $0, to: vehicle.getNumber())
|
|
||||||
}
|
|
||||||
.subscribe(onSuccess: self.save(vehicle:), onError: { print("Error adding event: \($0)") })
|
|
||||||
.disposed(by: self.bag)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
self.check(number: numberNormalized, action: .receiveAndSend).subscribe { vehicle in
|
||||||
self.updateDetailController(with: vehicle)
|
self.updateDetailController(with: vehicle)
|
||||||
IHProgressHUD.dismiss()
|
IHProgressHUD.dismiss()
|
||||||
|
} onError: { error in
|
||||||
|
IHProgressHUD.show(error: error)
|
||||||
|
}
|
||||||
|
.disposed(by: self.bag)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateDetailController(with vehicle: Vehicle) {
|
func updateDetailController(with vehicle: Vehicle) {
|
||||||
@ -239,27 +154,27 @@ class CheckController: UIViewController, UITableViewDelegate, UITextFieldDelegat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: -
|
// MARK: - UITableViewDelegate
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
|
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
|
||||||
guard let vehicle: Vehicle = try? self.history.rx.model(at: indexPath) else { return nil }
|
let vehicle = self.historyDataSource.item(at: indexPath)
|
||||||
|
|
||||||
let updateAction = UIContextualAction(style: .normal, title: "Update") { action, view, completion in
|
let updateAction = UIContextualAction(style: .normal, title: "Update") { action, view, completion in
|
||||||
IHProgressHUD.show()
|
IHProgressHUD.show()
|
||||||
|
|
||||||
var eventAction: EventAction = .doNotSend
|
var eventAction: EventAction = .doNotSend
|
||||||
if vehicle.unrecognized, let savedEvent = vehicle.events.first {
|
if vehicle.unrecognized, let savedEvent = vehicle.events.first {
|
||||||
eventAction = .sendSpecific(savedEvent.freeze())
|
eventAction = .sendSpecific(savedEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
Api.checkVehicle(by: vehicle.getNumber(), force: true)
|
self.check(number: vehicle.getNumber(), action: eventAction, force: true).subscribe { vehicle in
|
||||||
.observeOn(MainScheduler.instance)
|
self.updateDetailController(with: vehicle)
|
||||||
.subscribe(onSuccess: { vehicle in
|
IHProgressHUD.dismiss()
|
||||||
self.onReceivedVehicle(vehicle, action: eventAction)
|
} onError: { error in
|
||||||
}, onError: { err in
|
IHProgressHUD.show(error: error)
|
||||||
IHProgressHUD.showError(withStatus: err.localizedDescription)
|
}
|
||||||
print(err.localizedDescription)
|
.disposed(by: self.bag)
|
||||||
}).disposed(by: self.bag)
|
|
||||||
completion(true)
|
completion(true)
|
||||||
}
|
}
|
||||||
updateAction.image = UIImage(systemName: "arrow.2.circlepath")
|
updateAction.image = UIImage(systemName: "arrow.2.circlepath")
|
||||||
@ -282,4 +197,71 @@ class CheckController: UIViewController, UITableViewDelegate, UITextFieldDelegat
|
|||||||
configuration.performsFirstActionWithFullSwipe = false
|
configuration.performsFirstActionWithFullSwipe = false
|
||||||
return configuration
|
return configuration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||||
|
let vehicle = self.historyDataSource.item(at: indexPath)
|
||||||
|
self.updateDetailController(with: vehicle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Checking number
|
||||||
|
|
||||||
|
func save(vehicle: Vehicle) throws {
|
||||||
|
let realm = try Realm()
|
||||||
|
try realm.write {
|
||||||
|
print("===== Save vehicle: \(vehicle.getNumber())")
|
||||||
|
realm.add(vehicle, update: .all)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getEvent(for action: EventAction) -> Single<VehicleEvent> {
|
||||||
|
if case .sendSpecific(let event) = action {
|
||||||
|
return Single.just(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let event = LocationManager.lastEvent, (Date().timeIntervalSince1970 - event.date) < 30 {
|
||||||
|
return Single<VehicleEvent>.just(event)
|
||||||
|
} else {
|
||||||
|
return LocationManager.requestCurrentLocation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func check(number: String, action: EventAction, force: Bool = false) -> Single<Vehicle> {
|
||||||
|
let checkSingle = Api.checkVehicle(by: number, force: force)
|
||||||
|
.observeOn(MainScheduler.instance)
|
||||||
|
.map { (vehicle: Vehicle) -> Vehicle in
|
||||||
|
try self.save(vehicle: vehicle)
|
||||||
|
return vehicle
|
||||||
|
}
|
||||||
|
.do (onError: { err in
|
||||||
|
let realm = try Realm()
|
||||||
|
if realm.object(ofType: Vehicle.self, forPrimaryKey: number) == nil {
|
||||||
|
let vehicle = Vehicle(number)
|
||||||
|
try realm.write { realm.add(vehicle, update: .all) }
|
||||||
|
|
||||||
|
self.getEvent(for: action)
|
||||||
|
.flatMap { event in event.findAddress().map{ [event] }.catchErrorJustReturn([event]) }
|
||||||
|
.catchErrorJustReturn([])
|
||||||
|
.map { events in
|
||||||
|
try realm.write { vehicle.events.append(objectsIn: events) }
|
||||||
|
}
|
||||||
|
.subscribe()
|
||||||
|
.disposed(by: self.bag)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
if case .doNotSend = action {
|
||||||
|
return checkSingle
|
||||||
|
} else {
|
||||||
|
return checkSingle
|
||||||
|
.flatMap { _ in self.getEvent(for: action) }
|
||||||
|
.flatMap { event in event.findAddress().map{ event }.catchErrorJustReturn(event) }
|
||||||
|
.flatMap { Api.add(event: $0, to: number) }
|
||||||
|
.observeOn(MainScheduler.instance)
|
||||||
|
.map {
|
||||||
|
try self.save(vehicle: $0)
|
||||||
|
return $0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import AVFoundation
|
|||||||
import RealmSwift
|
import RealmSwift
|
||||||
import RxSwift
|
import RxSwift
|
||||||
import RxRealm
|
import RxRealm
|
||||||
import RxDataSources
|
|
||||||
import Intents
|
import Intents
|
||||||
import CoreSpotlight
|
import CoreSpotlight
|
||||||
import MobileCoreServices
|
import MobileCoreServices
|
||||||
@ -18,6 +17,7 @@ class RecordsController: UIViewController, UITableViewDelegate {
|
|||||||
let bag = DisposeBag()
|
let bag = DisposeBag()
|
||||||
var recordDisposable: Disposable?
|
var recordDisposable: Disposable?
|
||||||
var audioSessionObserver: NSObjectProtocol?
|
var audioSessionObserver: NSObjectProtocol?
|
||||||
|
var recordsDataSource: RealmSectionedDataSource<AudioRecord, AudioRecordCell>!
|
||||||
|
|
||||||
let validLetters = Constants.pnLettersMap.keys.map(String.init)
|
let validLetters = Constants.pnLettersMap.keys.map(String.init)
|
||||||
|
|
||||||
@ -31,28 +31,11 @@ class RecordsController: UIViewController, UITableViewDelegate {
|
|||||||
|
|
||||||
self.recorder = Recorder()
|
self.recorder = Recorder()
|
||||||
|
|
||||||
let ds = RxTableViewSectionedAnimatedDataSource<DateSection<AudioRecord>>(configureCell: { dataSource, tableView, indexPath, item in
|
|
||||||
if let cell = tableView.dequeueReusableCell(withIdentifier: "AudioRecordCell", for: indexPath) as? AudioRecordCell {
|
|
||||||
cell.configure(with: item.freeze())
|
|
||||||
return cell
|
|
||||||
} else {
|
|
||||||
return UITableViewCell()
|
|
||||||
}
|
|
||||||
}, canEditRowAtIndexPath: { _, _ in true })
|
|
||||||
|
|
||||||
ds.titleForHeaderInSection = { dataSourse, index in
|
|
||||||
return dataSourse.sectionModels[index].header
|
|
||||||
}
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
Observable.collection(from: realm.objects(AudioRecord.self)
|
self.recordsDataSource = RealmSectionedDataSource(table: self.tableView, data: realm.objects(AudioRecord.self)
|
||||||
.sorted(byKeyPath: "addedDate", ascending: false))
|
.sorted(byKeyPath: "addedDate", ascending: false))
|
||||||
.map { $0.groupedByDate() }
|
|
||||||
.bind(to: self.tableView.rx.items(dataSource: ds))
|
|
||||||
.disposed(by: self.bag)
|
|
||||||
}
|
}
|
||||||
|
self.tableView.delegate = self
|
||||||
self.tableView.rx.setDelegate(self).disposed(by: self.bag)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewDidAppear(_ animated: Bool) {
|
override func viewDidAppear(_ animated: Bool) {
|
||||||
@ -241,9 +224,10 @@ class RecordsController: UIViewController, UITableViewDelegate {
|
|||||||
// MARK: - UITableViewDelegate
|
// MARK: - UITableViewDelegate
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
|
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
|
||||||
guard let record: AudioRecord = try? self.tableView.rx.model(at: indexPath) else { return nil }
|
|
||||||
guard let cell = tableView.cellForRow(at: indexPath) else { return nil }
|
guard let cell = tableView.cellForRow(at: indexPath) else { return nil }
|
||||||
|
|
||||||
|
let record = self.recordsDataSource.item(at: indexPath)
|
||||||
|
|
||||||
let check = UIContextualAction(style: .normal, title: "Check") { action, view, completion in
|
let check = UIContextualAction(style: .normal, title: "Check") { action, view, completion in
|
||||||
if let number = record.number {
|
if let number = record.number {
|
||||||
self.check(number: number, event: record.event)
|
self.check(number: number, event: record.event)
|
||||||
@ -339,7 +323,7 @@ class RecordsController: UIViewController, UITableViewDelegate {
|
|||||||
alert.dismiss(animated: true)
|
alert.dismiss(animated: true)
|
||||||
}))
|
}))
|
||||||
alert.addTextField { tf in
|
alert.addTextField { tf in
|
||||||
tf.text = record.number ?? record.rawText
|
tf.text = record.number ?? record.rawText.replacingOccurrences(of: " ", with: "")
|
||||||
NotificationCenter.default.addObserver(forName: UITextField.textDidChangeNotification, object: tf, queue: OperationQueue.main) { _ in
|
NotificationCenter.default.addObserver(forName: UITextField.textDidChangeNotification, object: tf, queue: OperationQueue.main) { _ in
|
||||||
done.isEnabled = self.valid(number: tf.text?.uppercased() ?? "")
|
done.isEnabled = self.valid(number: tf.text?.uppercased() ?? "")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,12 +8,7 @@ protocol Dated {
|
|||||||
|
|
||||||
extension Vehicle: Dated {
|
extension Vehicle: Dated {
|
||||||
var date: Date {
|
var date: Date {
|
||||||
if let lastEventDate = self.events.max(by: { $0.date < $1.date })?.date {
|
return Date(timeIntervalSince1970: self.updatedDate)
|
||||||
let ts = max(self.addedDate/1000, lastEventDate)
|
|
||||||
return Date(timeIntervalSince1970: ts)
|
|
||||||
} else {
|
|
||||||
return Date(timeIntervalSince1970: self.addedDate/1000)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import Foundation
|
|||||||
import RxDataSources
|
import RxDataSources
|
||||||
import SwiftDate
|
import SwiftDate
|
||||||
|
|
||||||
struct DateSection<T>: AnimatableSectionModelType where T: IdentifiableType, T: Equatable {
|
struct DateSection<T>: AnimatableSectionModelType, Equatable where T: IdentifiableType, T: Equatable {
|
||||||
|
|
||||||
var timestamp: Double = 0
|
var timestamp: Double = 0
|
||||||
var header: String
|
var header: String
|
||||||
|
|||||||
@ -82,6 +82,7 @@ class Vehicle: Object, Decodable, IdentifiableType {
|
|||||||
var isRightWheel = RealmOptional<Bool>()
|
var isRightWheel = RealmOptional<Bool>()
|
||||||
@objc dynamic var isJapanese: Bool = false
|
@objc dynamic var isJapanese: Bool = false
|
||||||
@objc dynamic var addedDate: TimeInterval = 0
|
@objc dynamic var addedDate: TimeInterval = 0
|
||||||
|
@objc dynamic var updatedDate: TimeInterval = 0
|
||||||
@objc dynamic var addedBy: String = ""
|
@objc dynamic var addedBy: String = ""
|
||||||
let photos = List<VehiclePhoto>()
|
let photos = List<VehiclePhoto>()
|
||||||
let ownershipPeriods = List<VehicleOwnershipPeriod>()
|
let ownershipPeriods = List<VehicleOwnershipPeriod>()
|
||||||
@ -133,7 +134,7 @@ class Vehicle: Object, Decodable, IdentifiableType {
|
|||||||
self.pts = try container.decodeIfPresent(String.self, forKey: .pts)
|
self.pts = try container.decodeIfPresent(String.self, forKey: .pts)
|
||||||
self.isRightWheel = try container.decode(RealmOptional<Bool>.self, forKey: .isRightWheel)
|
self.isRightWheel = try container.decode(RealmOptional<Bool>.self, forKey: .isRightWheel)
|
||||||
self.isJapanese = try container.decode(Bool.self, forKey: .isJapanese)
|
self.isJapanese = try container.decode(Bool.self, forKey: .isJapanese)
|
||||||
self.addedDate = try container.decode(TimeInterval.self, forKey: .addedDate)
|
self.addedDate = (try container.decode(TimeInterval.self, forKey: .addedDate))/1000
|
||||||
self.addedBy = try container.decode(String.self, forKey: .addedBy)
|
self.addedBy = try container.decode(String.self, forKey: .addedBy)
|
||||||
|
|
||||||
if let photosArray = try container.decodeIfPresent([VehiclePhoto].self, forKey: .photos) {
|
if let photosArray = try container.decodeIfPresent([VehiclePhoto].self, forKey: .photos) {
|
||||||
@ -148,6 +149,12 @@ class Vehicle: Object, Decodable, IdentifiableType {
|
|||||||
self.events.append(objectsIn: eventsArray)
|
self.events.append(objectsIn: eventsArray)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let lastEventDate = self.events.max(by: { $0.date < $1.date })?.date {
|
||||||
|
self.updatedDate = max(self.addedDate, lastEventDate)
|
||||||
|
} else {
|
||||||
|
self.updatedDate = self.addedDate
|
||||||
|
}
|
||||||
|
|
||||||
self.identifier = self.number
|
self.identifier = self.number
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,7 +166,8 @@ class Vehicle: Object, Decodable, IdentifiableType {
|
|||||||
init(_ number: String) {
|
init(_ number: String) {
|
||||||
self.identifier = number
|
self.identifier = number
|
||||||
self.number = number
|
self.number = number
|
||||||
self.addedDate = Date().timeIntervalSince1970*1000
|
self.addedDate = Date().timeIntervalSince1970
|
||||||
|
self.updatedDate = self.addedDate
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNumber() -> String {
|
func getNumber() -> String {
|
||||||
|
|||||||
89
AutoCat/Utils/RxRealmDataSource.swift
Normal file
89
AutoCat/Utils/RxRealmDataSource.swift
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import UIKit
|
||||||
|
import RealmSwift
|
||||||
|
import RxDataSources
|
||||||
|
|
||||||
|
class RealmSectionedDataSource<Item,Cell>: NSObject, UITableViewDataSource where Item: Object & IdentifiableType & Dated, Cell: UITableViewCell & ConfigurableCell, Cell.Item == Item {
|
||||||
|
|
||||||
|
private var tv: UITableView
|
||||||
|
private var data: Results<Item>
|
||||||
|
private var notificationToken: NotificationToken?
|
||||||
|
private var sections: [DateSection<Item>] = []
|
||||||
|
private var cellIdentifier: String
|
||||||
|
|
||||||
|
init(table: UITableView, data: Results<Item>, cellIdentifier: String = String(describing: Cell.self)) {
|
||||||
|
self.tv = table
|
||||||
|
self.data = data
|
||||||
|
self.cellIdentifier = cellIdentifier
|
||||||
|
super.init()
|
||||||
|
self.tv.dataSource = self
|
||||||
|
|
||||||
|
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):
|
||||||
|
print("Deletions: \(deletions.count), Insertions: \(insertions.count), Modifications: \(modifications.count)")
|
||||||
|
let newSections = self.data.groupedByDate()
|
||||||
|
let diff = newSections.difference(from: self.sections)
|
||||||
|
|
||||||
|
self.tv.beginUpdates()
|
||||||
|
let delPaths = deletions.map(self.indexPath)
|
||||||
|
self.tv.deleteRows(at: delPaths, with: .automatic)
|
||||||
|
//diff.filter { if case .remove = $0 }
|
||||||
|
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)
|
||||||
|
self.tv.endUpdates()
|
||||||
|
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.items.count {
|
||||||
|
print("Index \(index) -> (\(i), \(index - sectionStartIndex))")
|
||||||
|
return IndexPath(row: index - sectionStartIndex, section: i)
|
||||||
|
}
|
||||||
|
|
||||||
|
sectionStartIndex += section.items.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].items.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].items[indexPath.row]
|
||||||
|
cell.configure(with: item)
|
||||||
|
return cell
|
||||||
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||||
|
return self.sections[section].header
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Public
|
||||||
|
|
||||||
|
func item(at indexPath: IndexPath) -> Item {
|
||||||
|
return self.sections[indexPath.section].items[indexPath.row]
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user