diff --git a/AutoCat.xcodeproj/project.pbxproj b/AutoCat.xcodeproj/project.pbxproj index 39a08d2..f9d8fd9 100644 --- a/AutoCat.xcodeproj/project.pbxproj +++ b/AutoCat.xcodeproj/project.pbxproj @@ -37,8 +37,6 @@ 7A14416E2C297F7C00E79018 /* Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A14416D2C297F7C00E79018 /* Coordinator.swift */; }; 7A1441702C2998B200E79018 /* Formatters.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A14416F2C2998B200E79018 /* Formatters.swift */; }; 7A176DB22C43071A00999D6B /* ApiServiceStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A176DB12C43071A00999D6B /* ApiServiceStub.swift */; }; - 7A176DB72C432F8800999D6B /* Mockable in Frameworks */ = {isa = PBXBuildFile; productRef = 7A176DB62C432F8800999D6B /* Mockable */; }; - 7A176DB92C432F8800999D6B /* MockableTest in Frameworks */ = {isa = PBXBuildFile; productRef = 7A176DB82C432F8800999D6B /* MockableTest */; }; 7A17CE4A2A2E820300626A6E /* UIStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A17CE492A2E820300626A6E /* UIStackView.swift */; }; 7A17CE4C2A2E850200626A6E /* UISegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A17CE4B2A2E850200626A6E /* UISegmentedControl.swift */; }; 7A1CF80529A41C66007962DA /* RealmSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 7A1CF80429A41C66007962DA /* RealmSwift */; }; @@ -59,6 +57,9 @@ 7A3E30F32C18840600567704 /* ActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3E30F22C18840600567704 /* ActivityItemSource.swift */; }; 7A3F07AB24360DC800E59687 /* Dated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3F07AA24360DC800E59687 /* Dated.swift */; }; 7A3F07AD2436350B00E59687 /* SearchController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3F07AC2436350B00E59687 /* SearchController.swift */; }; + 7A4322912CB2CC8A00085CF6 /* FiltersScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4322902CB2CC8A00085CF6 /* FiltersScreen.swift */; }; + 7A4322932CB2CCAA00085CF6 /* FiltersViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4322922CB2CCAA00085CF6 /* FiltersViewModel.swift */; }; + 7A4322952CB2CD0F00085CF6 /* FiltersCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4322942CB2CD0F00085CF6 /* FiltersCoordinator.swift */; }; 7A45FB382C27073700618694 /* StorageService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A45FB372C27073700618694 /* StorageService.swift */; }; 7A530B7A24001D3300CBFE6E /* CheckController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A530B7924001D3300CBFE6E /* CheckController.swift */; }; 7A530B7E24017FEE00CBFE6E /* VehicleCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A530B7D24017FEE00CBFE6E /* VehicleCell.swift */; }; @@ -159,6 +160,8 @@ 7AC355592969746600889457 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC355582969746600889457 /* UIControl.swift */; }; 7AC3555B296995B200889457 /* UIEdgeInsets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC3555A296995B200889457 /* UIEdgeInsets.swift */; }; 7AC76D7B270083AE0084DB27 /* TextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC76D7A270083AE0084DB27 /* TextView.swift */; }; + 7ACBB91E2CB9B155005A5168 /* Mockable in Frameworks */ = {isa = PBXBuildFile; productRef = 7ACBB91D2CB9B155005A5168 /* Mockable */; }; + 7ACBB9202CB9B16C005A5168 /* Mockable in Frameworks */ = {isa = PBXBuildFile; productRef = 7ACBB91F2CB9B16C005A5168 /* Mockable */; }; 7ADF23062C25B5BF002624FF /* RealmSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 7ADF23052C25B5BF002624FF /* RealmSwift */; }; 7ADF6C93250B954900F237B2 /* Navigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF6C92250B954900F237B2 /* Navigation.swift */; }; 7ADF6C95250D037700F237B2 /* ShowEventController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADF6C94250D037700F237B2 /* ShowEventController.swift */; }; @@ -189,6 +192,8 @@ 7AF6D2212677C1680086EA64 /* PagedResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6841A913FABBB0AB20DEF4FC /* PagedResponse.swift */; }; 7AF6D2282677C2DC0086EA64 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A96AE30246B2FE400297C33 /* Constants.swift */; }; 7AF6D22A2677C3AD0086EA64 /* Exportable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AE8424D26109F78002F6B31 /* Exportable.swift */; }; + 7AF8606C2CB9B20C00954D2F /* Mockable in Frameworks */ = {isa = PBXBuildFile; productRef = 7AF8606B2CB9B20C00954D2F /* Mockable */; }; + 7AF8606E2CB9B86300954D2F /* Mockable in Frameworks */ = {isa = PBXBuildFile; productRef = 7AF8606D2CB9B86300954D2F /* Mockable */; }; 7AFBE8C02C3024E5003C491D /* ACHud.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFBE8BF2C3024E5003C491D /* ACHud.swift */; }; 7AFBE8C42C302561003C491D /* ACHudContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFBE8C32C302561003C491D /* ACHudContainer.swift */; }; 7AFBE8CA2C3081C7003C491D /* ACProgressHud+Modifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFBE8C92C3081C7003C491D /* ACProgressHud+Modifiers.swift */; }; @@ -308,6 +313,9 @@ 7A3E30F22C18840600567704 /* ActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityItemSource.swift; sourceTree = ""; }; 7A3F07AA24360DC800E59687 /* Dated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dated.swift; sourceTree = ""; }; 7A3F07AC2436350B00E59687 /* SearchController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchController.swift; sourceTree = ""; }; + 7A4322902CB2CC8A00085CF6 /* FiltersScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersScreen.swift; sourceTree = ""; }; + 7A4322922CB2CCAA00085CF6 /* FiltersViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersViewModel.swift; sourceTree = ""; }; + 7A4322942CB2CD0F00085CF6 /* FiltersCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersCoordinator.swift; sourceTree = ""; }; 7A43F9F7246C8A6200BA5B49 /* JWT.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JWT.swift; sourceTree = ""; }; 7A45FB372C27073700618694 /* StorageService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageService.swift; sourceTree = ""; }; 7A52AB292580112E002CD910 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; @@ -451,6 +459,7 @@ 7ADF23062C25B5BF002624FF /* RealmSwift in Frameworks */, 7AA7BC3625A5DFB80053A5D5 /* PKHUD in Frameworks */, 7AC3554A2969652F00889457 /* SwiftEntryKit in Frameworks */, + 7ACBB91E2CB9B155005A5168 /* Mockable in Frameworks */, 7AF6D2042677C03B0086EA64 /* AutoCatCore.framework in Frameworks */, 7A35177B27E23F8800DC538C /* Eureka in Frameworks */, 7A96AE2F246B2BCD00297C33 /* WebKit.framework in Frameworks */, @@ -463,6 +472,7 @@ files = ( 7AB5871D2C42C1CF00FA7B66 /* RealmSwift in Frameworks */, 7A2E6FA72C42B3AD00C40DA7 /* AutoCatCore.framework in Frameworks */, + 7AF8606C2CB9B20C00954D2F /* Mockable in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -470,7 +480,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 7A176DB92C432F8800999D6B /* MockableTest in Frameworks */, + 7ACBB9202CB9B16C005A5168 /* Mockable in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -480,7 +490,7 @@ files = ( 7A1CF80529A41C66007962DA /* RealmSwift in Frameworks */, 7AABB1F2267E9CC800D7AB32 /* SwiftDate in Frameworks */, - 7A176DB72C432F8800999D6B /* Mockable in Frameworks */, + 7AF8606E2CB9B86300954D2F /* Mockable in Frameworks */, 7A6C4D9E2C56BCA600982597 /* SwiftLocation in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -663,6 +673,7 @@ 7A1441632C297E9800E79018 /* Screens */ = { isa = PBXGroup; children = ( + 7A43228F2CB2CC5D00085CF6 /* FiltersScreen */, 7A06E0AA2C706550005731AC /* SettingsScreen */, 7A1022752C557E3F00B84627 /* LocationPickerScreen */, 7A10226A2C551EA200B84627 /* LocationEditScreen */, @@ -713,6 +724,16 @@ path = Extensions; sourceTree = ""; }; + 7A43228F2CB2CC5D00085CF6 /* FiltersScreen */ = { + isa = PBXGroup; + children = ( + 7A4322902CB2CC8A00085CF6 /* FiltersScreen.swift */, + 7A4322922CB2CCAA00085CF6 /* FiltersViewModel.swift */, + 7A4322942CB2CD0F00085CF6 /* FiltersCoordinator.swift */, + ); + path = FiltersScreen; + sourceTree = ""; + }; 7A45FB362C2706D000618694 /* Services */ = { isa = PBXGroup; children = ( @@ -1065,6 +1086,7 @@ 7A35177A27E23F8800DC538C /* Eureka */, 7AC355492969652F00889457 /* SwiftEntryKit */, 7ADF23052C25B5BF002624FF /* RealmSwift */, + 7ACBB91D2CB9B155005A5168 /* Mockable */, ); productName = AutoCat; productReference = 7A1146FD23FDE7E500B424AF /* AutoCat.app */; @@ -1089,6 +1111,7 @@ name = AutoCatCoreTests; packageProductDependencies = ( 7AB5871C2C42C1CF00FA7B66 /* RealmSwift */, + 7AF8606B2CB9B20C00954D2F /* Mockable */, ); productName = AutoCatCoreTests; productReference = 7A2E6FA32C42B3AD00C40DA7 /* AutoCatCoreTests.xctest */; @@ -1112,7 +1135,7 @@ ); name = AutoCatTests; packageProductDependencies = ( - 7A176DB82C432F8800999D6B /* MockableTest */, + 7ACBB91F2CB9B16C005A5168 /* Mockable */, ); productName = AutoCatTests; productReference = 7AB587222C42D27F00FA7B66 /* AutoCatTests.xctest */; @@ -1136,8 +1159,8 @@ packageProductDependencies = ( 7AABB1F1267E9CC800D7AB32 /* SwiftDate */, 7A1CF80429A41C66007962DA /* RealmSwift */, - 7A176DB62C432F8800999D6B /* Mockable */, 7A6C4D9D2C56BCA600982597 /* SwiftLocation */, + 7AF8606D2CB9B86300954D2F /* Mockable */, ); productName = AutoCatCore; productReference = 7AF6D1EF2677C03B0086EA64 /* AutoCatCore.framework */; @@ -1186,8 +1209,8 @@ 7A35177927E23F8800DC538C /* XCRemoteSwiftPackageReference "Eureka" */, 7AC355482969652F00889457 /* XCRemoteSwiftPackageReference "SwiftEntryKit" */, 7A1CF7FD29A41C2F007962DA /* XCRemoteSwiftPackageReference "realm-swift" */, - 7A176DB52C432F8800999D6B /* XCRemoteSwiftPackageReference "Mockable" */, 7A6C4D9C2C56BCA600982597 /* XCRemoteSwiftPackageReference "SwiftLocation" */, + 7ACBB91C2CB9B155005A5168 /* XCRemoteSwiftPackageReference "Mockable" */, ); productRefGroup = 7A1146FE23FDE7E500B424AF /* Products */; projectDirPath = ""; @@ -1248,6 +1271,7 @@ 7A961C6C2C4C3C8600CE2211 /* TextRowView.swift in Sources */, 7AEFC3BE2529D3CC00BADFB2 /* ConfigurableCell.swift in Sources */, 7A1022772C557EC400B84627 /* LocationPickerScreen.swift in Sources */, + 7A4322932CB2CCAA00085CF6 /* FiltersViewModel.swift in Sources */, 7A5D7E0C2C71EB25002C17E7 /* ToggleRowView.swift in Sources */, 7A7158092C44087E00852088 /* OsagoCoordinator.swift in Sources */, 7A1441662C297EDE00E79018 /* NotesScreen.swift in Sources */, @@ -1326,8 +1350,10 @@ 7AFBE8C42C302561003C491D /* ACHudContainer.swift in Sources */, 7AC3555B296995B200889457 /* UIEdgeInsets.swift in Sources */, 7A06E0AC2C7065AC005731AC /* SettingsScreen.swift in Sources */, + 7A4322952CB2CD0F00085CF6 /* FiltersCoordinator.swift in Sources */, 7A7158002C43EA6900852088 /* OwnersScreen.swift in Sources */, 7A1441702C2998B200E79018 /* Formatters.swift in Sources */, + 7A4322912CB2CC8A00085CF6 /* FiltersScreen.swift in Sources */, 7ADF6C95250D037700F237B2 /* ShowEventController.swift in Sources */, 7A71580C2C44453200852088 /* AdsScreen.swift in Sources */, 7A06E0B02C7065D8005731AC /* SettingsCoordinator.swift in Sources */, @@ -1623,7 +1649,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 135; + CURRENT_PROJECT_VERSION = 136; DEVELOPMENT_TEAM = 46DTTB8X4S; INFOPLIST_FILE = AutoCat/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = AutoCat; @@ -1650,7 +1676,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 135; + CURRENT_PROJECT_VERSION = 136; DEVELOPMENT_TEAM = 46DTTB8X4S; INFOPLIST_FILE = AutoCat/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = AutoCat; @@ -1893,14 +1919,6 @@ minimumVersion = 6.1.0; }; }; - 7A176DB52C432F8800999D6B /* XCRemoteSwiftPackageReference "Mockable" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/Kolos65/Mockable"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 0.0.9; - }; - }; 7A1CF7FD29A41C2F007962DA /* XCRemoteSwiftPackageReference "realm-swift" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/realm/realm-swift.git"; @@ -1949,6 +1967,14 @@ minimumVersion = 2.0.0; }; }; + 7ACBB91C2CB9B155005A5168 /* XCRemoteSwiftPackageReference "Mockable" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/Kolos65/Mockable"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 0.0.11; + }; + }; 7AF58D322402A91C00CE01A0 /* XCRemoteSwiftPackageReference "Kingfisher" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/onevcat/Kingfisher"; @@ -1960,16 +1986,6 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - 7A176DB62C432F8800999D6B /* Mockable */ = { - isa = XCSwiftPackageProductDependency; - package = 7A176DB52C432F8800999D6B /* XCRemoteSwiftPackageReference "Mockable" */; - productName = Mockable; - }; - 7A176DB82C432F8800999D6B /* MockableTest */ = { - isa = XCSwiftPackageProductDependency; - package = 7A176DB52C432F8800999D6B /* XCRemoteSwiftPackageReference "Mockable" */; - productName = MockableTest; - }; 7A1CF80429A41C66007962DA /* RealmSwift */ = { isa = XCSwiftPackageProductDependency; package = 7A1CF7FD29A41C2F007962DA /* XCRemoteSwiftPackageReference "realm-swift" */; @@ -2010,6 +2026,16 @@ package = 7AC355482969652F00889457 /* XCRemoteSwiftPackageReference "SwiftEntryKit" */; productName = SwiftEntryKit; }; + 7ACBB91D2CB9B155005A5168 /* Mockable */ = { + isa = XCSwiftPackageProductDependency; + package = 7ACBB91C2CB9B155005A5168 /* XCRemoteSwiftPackageReference "Mockable" */; + productName = Mockable; + }; + 7ACBB91F2CB9B16C005A5168 /* Mockable */ = { + isa = XCSwiftPackageProductDependency; + package = 7ACBB91C2CB9B155005A5168 /* XCRemoteSwiftPackageReference "Mockable" */; + productName = Mockable; + }; 7ADF23052C25B5BF002624FF /* RealmSwift */ = { isa = XCSwiftPackageProductDependency; package = 7A1CF7FD29A41C2F007962DA /* XCRemoteSwiftPackageReference "realm-swift" */; @@ -2020,6 +2046,16 @@ package = 7AF58D322402A91C00CE01A0 /* XCRemoteSwiftPackageReference "Kingfisher" */; productName = Kingfisher; }; + 7AF8606B2CB9B20C00954D2F /* Mockable */ = { + isa = XCSwiftPackageProductDependency; + package = 7ACBB91C2CB9B155005A5168 /* XCRemoteSwiftPackageReference "Mockable" */; + productName = Mockable; + }; + 7AF8606D2CB9B86300954D2F /* Mockable */ = { + isa = XCSwiftPackageProductDependency; + package = 7ACBB91C2CB9B155005A5168 /* XCRemoteSwiftPackageReference "Mockable" */; + productName = Mockable; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 7A1146F523FDE7E500B424AF /* Project object */; diff --git a/AutoCat.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/AutoCat.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index f1cb90f..b8692ad 100644 --- a/AutoCat.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/AutoCat.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "ae480e95b5199c1834610abd8aa26a821694aa5167d41c0aceddc8513e267012", + "originHash" : "457694e6162dd513aa4b1f33a0e4319bbd6de88b35dc652bf9fa41e06ea431da", "pins" : [ { "identity" : "eureka", @@ -33,8 +33,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/Kolos65/Mockable", "state" : { - "revision" : "da977ecb20974c4b1cf185f5fd38771b2d4674fb", - "version" : "0.0.10" + "revision" : "a9e5e1d222035567069ed6fff8429c327229b5f6", + "version" : "0.0.11" } }, { @@ -51,8 +51,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/realm/realm-core.git", "state" : { - "revision" : "c2552e1d36867cb42b28130e894a81fc17081062", - "version" : "14.12.0" + "revision" : "e474a8d2270a8b12ac63ac9504e4757e39814b99", + "version" : "14.13.0" } }, { @@ -60,8 +60,17 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/realm/realm-swift.git", "state" : { - "revision" : "5221a83dc720823e3017493394d00d49ccf13446", - "version" : "10.52.3" + "revision" : "863498d37a9f0e72caa65963da9641d8cdfc8228", + "version" : "10.54.0" + } + }, + { + "identity" : "swift-issue-reporting", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-issue-reporting", + "state" : { + "revision" : "770f990d3e4eececb57ac04a6076e22f8c97daeb", + "version" : "1.4.2" } }, { diff --git a/AutoCat.xcodeproj/xcshareddata/xcschemes/AutoCatCore.xcscheme b/AutoCat.xcodeproj/xcshareddata/xcschemes/AutoCatCore.xcscheme new file mode 100644 index 0000000..7f91a7a --- /dev/null +++ b/AutoCat.xcodeproj/xcshareddata/xcschemes/AutoCatCore.xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AutoCat.xcodeproj/xcuserdata/selim.xcuserdatad/xcschemes/xcschememanagement.plist b/AutoCat.xcodeproj/xcuserdata/selim.xcuserdatad/xcschemes/xcschememanagement.plist index 3188224..c8605b8 100644 --- a/AutoCat.xcodeproj/xcuserdata/selim.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/AutoCat.xcodeproj/xcuserdata/selim.xcuserdatad/xcschemes/xcschememanagement.plist @@ -12,217 +12,217 @@ AutoCatCore.xcscheme_^#shared#^_ orderHint - 1 + 2 DifferenceKit (Playground) 1.xcscheme isShown - + orderHint 9 DifferenceKit (Playground) 2.xcscheme isShown - + orderHint 10 DifferenceKit (Playground).xcscheme isShown - + orderHint 8 Eureka (Playground) 1.xcscheme isShown - + orderHint 3 Eureka (Playground) 2.xcscheme isShown - + orderHint 4 Eureka (Playground) 3.xcscheme isShown - + orderHint 18 Eureka (Playground) 4.xcscheme isShown - + orderHint 19 Eureka (Playground) 5.xcscheme isShown - + orderHint 21 Eureka (Playground) 6.xcscheme isShown - + orderHint 23 Eureka (Playground) 7.xcscheme isShown - + orderHint 25 Eureka (Playground) 8.xcscheme isShown - + orderHint 27 Eureka (Playground).xcscheme isShown - + orderHint - 2 + 3 GettingStarted (Playground) 1.xcscheme isShown - + orderHint 16 GettingStarted (Playground) 2.xcscheme isShown - + orderHint 17 GettingStarted (Playground) 3.xcscheme isShown - + orderHint 14 GettingStarted (Playground) 4.xcscheme isShown - + orderHint 20 GettingStarted (Playground) 5.xcscheme isShown - + orderHint 26 GettingStarted (Playground).xcscheme isShown - + orderHint - 12 + 1 Rx (Playground) 1.xcscheme isShown - + orderHint 6 Rx (Playground) 2.xcscheme isShown - + orderHint 7 Rx (Playground) 3.xcscheme isShown - + orderHint 22 Rx (Playground) 4.xcscheme isShown - + orderHint 24 Rx (Playground) 5.xcscheme isShown - + orderHint 28 Rx (Playground).xcscheme isShown - + orderHint 5 SwiftDate (Playground) 1.xcscheme isShown - + orderHint 13 SwiftDate (Playground) 2.xcscheme isShown - + orderHint 15 SwiftDate (Playground) 3.xcscheme isShown - + orderHint 29 SwiftDate (Playground) 4.xcscheme isShown - + orderHint 30 SwiftDate (Playground) 5.xcscheme isShown - + orderHint 31 SwiftDate (Playground).xcscheme isShown - + orderHint - 11 + 4 SuppressBuildableAutocreation @@ -230,7 +230,12 @@ 7A1146FC23FDE7E500B424AF primary - + + + 7AF6D1EE2677C03B0086EA64 + + primary + diff --git a/AutoCat/Controllers/Location/EventsController.swift b/AutoCat/Controllers/Location/EventsController.swift index f37d507..3c360d0 100644 --- a/AutoCat/Controllers/Location/EventsController.swift +++ b/AutoCat/Controllers/Location/EventsController.swift @@ -59,6 +59,8 @@ class EventsController: UIViewController, UITableViewDataSource, UITableViewDele } } + public var vehicleUpdated: ((VehicleDto) -> Void)? + private var pins: [EventPin] = [] override func viewDidLoad() { @@ -392,6 +394,7 @@ class EventsController: UIViewController, UITableViewDataSource, UITableViewDele } self.vehicle?.events = vehicle.events.sorted { $0.date > $1.date } + self.vehicleUpdated?(vehicle) HUD.hide() return true diff --git a/AutoCat/Controllers/ReportController.swift b/AutoCat/Controllers/ReportController.swift index a80a09d..1969160 100644 --- a/AutoCat/Controllers/ReportController.swift +++ b/AutoCat/Controllers/ReportController.swift @@ -26,6 +26,10 @@ class ReportController: FormViewController, MediaBrowserViewControllerDataSource self.form.allSections.forEach { $0.reload() } self.navigationController?.setNavigationBarHidden(self.vehicle == nil, animated: false) } + + if let vehicle { + vehicleUpdated?(vehicle) + } } } @@ -42,6 +46,8 @@ class ReportController: FormViewController, MediaBrowserViewControllerDataSource } } + public var vehicleUpdated: ((VehicleDto) -> Void)? + // MARK: - Lifecycle override func viewDidLoad() { @@ -83,6 +89,9 @@ class ReportController: FormViewController, MediaBrowserViewControllerDataSource let sb = UIStoryboard(name: "Main", bundle: nil) let controller = sb.instantiateViewController(identifier: "EventsController") as EventsController controller.vehicle = self.vehicle + controller.vehicleUpdated = { vehicle in + self.vehicle = vehicle + } self.navigationController?.pushViewController(controller, animated: true) } diff --git a/AutoCat/Controllers/SearchController.swift b/AutoCat/Controllers/SearchController.swift index 1327b2d..c3cbe98 100644 --- a/AutoCat/Controllers/SearchController.swift +++ b/AutoCat/Controllers/SearchController.swift @@ -100,7 +100,7 @@ class SearchController: UIViewController, UISearchResultsUpdating, UITableViewDe } // FIXME: Code duplication - func updateDetailController(with vehicle: VehicleDto) { + func updateDetailController(with vehicle: VehicleDto, indexPath: IndexPath) { if let splitViewController = self.view.window?.rootViewController as? UISplitViewController { var detail: UINavigationController? @@ -115,6 +115,10 @@ class SearchController: UIViewController, UISearchResultsUpdating, UITableViewDe detail.popToRootViewController(animated: true) let report = detail.viewControllers.first as? ReportController report?.vehicle = vehicle + report?.vehicleUpdated = { vehicle in + self.datasource.set(item: vehicle, at: indexPath) + self.tableView.reloadData() + } splitViewController.showDetailViewController(detail, sender: self) //self.performSegue(withIdentifier: "OpenDetailSegue", sender: self) } @@ -151,7 +155,7 @@ class SearchController: UIViewController, UISearchResultsUpdating, UITableViewDe let menu = UIMenu(children: [ UIAction(title: NSLocalizedString("Filter results", comment: ""), image: UIImage(systemName: "line.horizontal.3.decrease"), - handler: { _ in self.showFilter() }), + handler: { [weak self] _ in Task { try? await self?.showFilter() } }), UIAction(title: NSLocalizedString("Show on map", comment: ""), image: UIImage(systemName: "map"), handler: { _ in self.showOnMap() }), @@ -166,7 +170,7 @@ class SearchController: UIViewController, UISearchResultsUpdating, UITableViewDe } @IBAction func onFilterTapped(_ sender: UIBarButtonItem) { - showFilter() + Task { try? await showFilter() } } @IBAction func onMapTapped(_ sender: UIBarButtonItem) { @@ -179,7 +183,7 @@ class SearchController: UIViewController, UISearchResultsUpdating, UITableViewDe updateSearchResults(with: filter) } - func showFilter() { + func showFilter() async throws { let sb = UIStoryboard(name: "Main", bundle: nil) let controller = sb.instantiateViewController(identifier: "FiltersController") as FiltersController controller.filter = self.filter @@ -191,6 +195,15 @@ class SearchController: UIViewController, UISearchResultsUpdating, UITableViewDe self.updateSearchResults(with: self.filter) } self.navigationController?.pushViewController(controller, animated: true) + +// if let navigationController = self.navigationController { +// let coordinator = FiltersCoordinator(navController: navigationController, filter: filter) +// filter = try await coordinator.start() +// datasource.setSortParameter(self.filter.sortBy ?? .updatedDate) +// filter.needReset = true +// filter.scope = SearchScope(rawValue: self.searchController.searchBar.selectedScopeButtonIndex) ?? .plateNumber +// updateSearchResults(with: self.filter) +// } } func showOnMap() { @@ -291,7 +304,7 @@ class SearchController: UIViewController, UISearchResultsUpdating, UITableViewDe } } datasource.set(item: newVehicle, at: indexPath) - updateDetailController(with: newVehicle) + updateDetailController(with: newVehicle, indexPath: indexPath) } catch { HUD.hide() show(error: error) @@ -301,7 +314,7 @@ class SearchController: UIViewController, UISearchResultsUpdating, UITableViewDe func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let vehicle = self.datasource.item(at: indexPath) - self.updateDetailController(with: vehicle) + self.updateDetailController(with: vehicle, indexPath: indexPath) } func scrollViewDidScroll(_ scrollView: UIScrollView) { diff --git a/AutoCat/Screens/FiltersScreen/FiltersCoordinator.swift b/AutoCat/Screens/FiltersScreen/FiltersCoordinator.swift new file mode 100644 index 0000000..746746d --- /dev/null +++ b/AutoCat/Screens/FiltersScreen/FiltersCoordinator.swift @@ -0,0 +1,31 @@ +// +// FiltersCoordinator.swift +// AutoCat +// +// Created by Selim Mustafaev on 06.10.2024. +// Copyright © 2024 Selim Mustafaev. All rights reserved. +// + +import AutoCatCore +import UIKit + +@MainActor +class FiltersCoordinator: Coordinator { + + let viewController: UINavigationController? + let filter: Filter + + init(navController: UINavigationController, filter: Filter) { + + self.viewController = navController + self.filter = filter + } + + func start() async throws -> Filter { + let viewModel = FiltersViewModel(filter: filter) + let controller = CustomHostingController(rootView: FiltersScreen(viewModel: viewModel)) + viewController?.pushViewController(controller, animated: true) + await controller.waitForDisappear() + return viewModel.filter + } +} diff --git a/AutoCat/Screens/FiltersScreen/FiltersScreen.swift b/AutoCat/Screens/FiltersScreen/FiltersScreen.swift new file mode 100644 index 0000000..a83b8d7 --- /dev/null +++ b/AutoCat/Screens/FiltersScreen/FiltersScreen.swift @@ -0,0 +1,47 @@ +// +// FiltersScreen.swift +// AutoCat +// +// Created by Selim Mustafaev on 06.10.2024. +// Copyright © 2024 Selim Mustafaev. All rights reserved. +// + +import SwiftUI +import AutoCatCore + +enum DetailScreen: Hashable { + + case brand + case model + case color + case year +} + +struct FiltersScreen: View { + + @State var viewModel: FiltersViewModel + + var body: some View { + Form { + Section("Main filters") { + NavigationLink(value: DetailScreen.brand) { + LabeledContent("Brand", value: viewModel.filter.brand ?? "Any") + } + LabeledContent("Model", value: viewModel.filter.model ?? "Any") + LabeledContent("Color", value: viewModel.filter.color ?? "Any") + LabeledContent("Year", value: viewModel.filter.year ?? "Any") + } + } + .navigationTitle("Filters") + .navigationBarItems(trailing: Button("Done", action: { + + })) + .navigationDestination(for: DetailScreen.self) { _ in + EmptyView() + } + } +} + +#Preview { + FiltersScreen(viewModel: .init(filter: Filter())) +} diff --git a/AutoCat/Screens/FiltersScreen/FiltersViewModel.swift b/AutoCat/Screens/FiltersScreen/FiltersViewModel.swift new file mode 100644 index 0000000..f46edb5 --- /dev/null +++ b/AutoCat/Screens/FiltersScreen/FiltersViewModel.swift @@ -0,0 +1,21 @@ +// +// FiltersViewModel.swift +// AutoCat +// +// Created by Selim Mustafaev on 06.10.2024. +// Copyright © 2024 Selim Mustafaev. All rights reserved. +// + +import SwiftUI +import AutoCatCore + +@MainActor +@Observable +class FiltersViewModel { + + var filter: Filter + + init(filter: Filter) { + self.filter = filter + } +} diff --git a/AutoCatCore/Models/DTO/VehicleDto.swift b/AutoCatCore/Models/DTO/VehicleDto.swift index 690742c..a4f77ac 100644 --- a/AutoCatCore/Models/DTO/VehicleDto.swift +++ b/AutoCatCore/Models/DTO/VehicleDto.swift @@ -97,11 +97,14 @@ extension VehicleDto: Decodable { self.photos = try container.decodeIfPresent([VehiclePhotoDto].self, forKey: .photos) ?? [] self.ownershipPeriods = try container.decodeIfPresent([VehicleOwnershipPeriodDto].self, forKey: .ownershipPeriods) ?? [] - self.events = try container.decodeIfPresent([VehicleEventDto].self, forKey: .events) ?? [] self.osagoContracts = try container.decodeIfPresent([OsagoDto].self, forKey: .osagoContracts) ?? [] self.ads = try container.decodeIfPresent([VehicleAdDto].self, forKey: .ads) ?? [] self.notes = try container.decodeIfPresent([VehicleNoteDto].self, forKey: .notes) ?? [] + if let events = try container.decodeIfPresent([VehicleEventDto].self, forKey: .events) { + self.events = events.sorted { $0.date > $1.date } + } + // All vehicles received from API are synchronized by definition self.synchronized = true } diff --git a/AutoCatTests/NotesTests.swift b/AutoCatTests/NotesTests.swift index f58c55c..108a5a0 100644 --- a/AutoCatTests/NotesTests.swift +++ b/AutoCatTests/NotesTests.swift @@ -8,14 +8,14 @@ import Testing import AutoCatCore -import MockableTest +import Mockable @testable import AutoCat @MainActor final class NotesTests { - var storageServiceMock: StorageServiceMock - var apiServiceMock: ApiServiceMock + var storageServiceMock: MockStorageServiceProtocol + var apiServiceMock: MockApiServiceProtocol var viewModel: NotesViewModel @@ -26,8 +26,8 @@ final class NotesTests { lazy var unrecognizedVehicleWithNote: VehicleDto = .unrecognized.addNote(text: noteText) init() { - storageServiceMock = StorageServiceMock() - apiServiceMock = ApiServiceMock() + storageServiceMock = MockStorageServiceProtocol() + apiServiceMock = MockApiServiceProtocol() ServiceContainer.shared.register(StorageServiceProtocol.self, instance: storageServiceMock) ServiceContainer.shared.register(ApiServiceProtocol.self, instance: apiServiceMock) @@ -35,22 +35,28 @@ final class NotesTests { } @Test("Add note (normal vehicle)") - func addNote() async throws { + func addNote() async { -// given(apiServiceMock) -// .add(notes: .any, to: .any).willReturn(vehicleWithNote) + print(vehicleWithNote) + + given(apiServiceMock) + .add(notes: .any, to: .any) + .willReturn(vehicleWithNote) + + given(storageServiceMock) + .updateVehicleIfExists(dto: .any) + .willReturn() - await apiServiceMock.setVehicle(vehicleWithNote) viewModel.vehicle = .normal await viewModel.addNote(text: noteText) -// verify(apiServiceMock) -// .add(notes: .any, to: .any).called(.once) -// -// verify(storageServiceMock) -// .addNote(text: .any, to: .any).called(.never) -// .updateVehicleIfExists(dto: .any).called(.once) + verify(apiServiceMock) + .add(notes: .any, to: .any).called(.once) + + verify(storageServiceMock) + .addNote(text: .any, to: .any).called(.never) + .updateVehicleIfExists(dto: .any).called(.once) #expect(viewModel.vehicle.notes.contains { $0.text == noteText }) #expect(viewModel.hud == nil) @@ -59,11 +65,21 @@ final class NotesTests { @Test("Add note (unrecognized vehicle)") func addNoteUnrecognized() async throws { - await storageServiceMock.setVehicle(vehicleWithNote) + given(storageServiceMock) + .addNote(text: .any, to: .any) + .willReturn(vehicleWithNote) + viewModel.vehicle = .unrecognized await viewModel.addNote(text: noteText) + verify(apiServiceMock) + .add(notes: .any, to: .any).called(.never) + + verify(storageServiceMock) + .addNote(text: .any, to: .any).called(.once) + .updateVehicleIfExists(dto: .any).called(.never) + #expect(viewModel.vehicle.notes.contains { $0.text == noteText }) #expect(viewModel.hud == nil) } @@ -72,11 +88,27 @@ final class NotesTests { func editNote() async throws { let noteId = try #require(vehicleWithNote.notes.first?.id) - await apiServiceMock.setVehicle(.normal.addNote(text: noteTextModified, id: noteId)) + let modifiedVehicle: VehicleDto = .normal.addNote(text: noteTextModified, id: noteId) + + given(apiServiceMock) + .edit(note: .any) + .willReturn(modifiedVehicle) + + given(storageServiceMock) + .updateVehicleIfExists(dto: .any) + .willReturn() + viewModel.vehicle = vehicleWithNote await viewModel.editNote(id: noteId, text: noteTextModified) + verify(apiServiceMock) + .edit(note: .any).called(.once) + + verify(storageServiceMock) + .editNote(id: .any, text: .any, for: .any).called(.never) + .updateVehicleIfExists(dto: .any).called(.once) + #expect(viewModel.vehicle.notes.contains { $0.text == noteTextModified }) #expect(viewModel.hud == nil) } @@ -86,11 +118,22 @@ final class NotesTests { let vehicle: VehicleDto = .unrecognized.addNote(text: noteText) let noteId = try #require(vehicle.notes.first?.id) - await storageServiceMock.setVehicle(.unrecognized.addNote(text: noteTextModified, id: noteId)) + + given(storageServiceMock) + .editNote(id: .value(noteId), text: .value(noteTextModified), for: .any) + .willReturn(.unrecognized.addNote(text: noteTextModified, id: noteId)) + viewModel.vehicle = vehicle await viewModel.editNote(id: noteId, text: noteTextModified) + verify(apiServiceMock) + .edit(note: .any).called(.never) + + verify(storageServiceMock) + .editNote(id: .any, text: .any, for: .any).called(.once) + .updateVehicleIfExists(dto: .any).called(.never) + #expect(viewModel.vehicle.notes.contains { $0.text == noteTextModified }) #expect(viewModel.hud == nil) } @@ -98,12 +141,27 @@ final class NotesTests { @Test("Delete note (normal vehicle)") func deleteNote() async throws { - await apiServiceMock.setVehicle(.normal) - viewModel.vehicle = vehicleWithNote let noteId = try #require(vehicleWithNote.notes.first?.id) + given(apiServiceMock) + .remove(note: .value(noteId)) + .willReturn(.normal) + + given(storageServiceMock) + .updateVehicleIfExists(dto: .any) + .willReturn() + + viewModel.vehicle = vehicleWithNote + await viewModel.deleteNote(id: noteId) + verify(apiServiceMock) + .remove(note: .any).called(.once) + + verify(storageServiceMock) + .deleteNote(id: .any, for: .any).called(.never) + .updateVehicleIfExists(dto: .any).called(.once) + #expect(!viewModel.vehicle.notes.contains { $0.text == noteText }) #expect(viewModel.hud == nil) } @@ -111,12 +169,23 @@ final class NotesTests { @Test("Delete note (unrecognized vehicle)") func deleteNoteUnrecognized() async throws { - await storageServiceMock.setVehicle(.unrecognized) - viewModel.vehicle = unrecognizedVehicleWithNote let noteId = try #require(unrecognizedVehicleWithNote.notes.first?.id) + given(storageServiceMock) + .deleteNote(id: .value(noteId), for: .any) + .willReturn(.unrecognized) + + viewModel.vehicle = unrecognizedVehicleWithNote + await viewModel.deleteNote(id: noteId) + verify(apiServiceMock) + .remove(note: .any).called(.never) + + verify(storageServiceMock) + .deleteNote(id: .value(noteId), for: .any).called(.once) + .updateVehicleIfExists(dto: .any).called(.never) + #expect(!viewModel.vehicle.notes.contains { $0.text == noteText }) #expect(viewModel.hud == nil) }