Replacing settings screen with SwiftUI version. Adding SettingService.

This commit is contained in:
Selim Mustafaev 2024-08-25 22:50:32 +03:00
parent f78becf791
commit 17d00fbf65
20 changed files with 691 additions and 224 deletions

View File

@ -7,6 +7,11 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
7A06E0AC2C7065AC005731AC /* SettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A06E0AB2C7065AB005731AC /* SettingsScreen.swift */; };
7A06E0AE2C7065C7005731AC /* SettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A06E0AD2C7065C7005731AC /* SettingsViewModel.swift */; };
7A06E0B02C7065D8005731AC /* SettingsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A06E0AF2C7065D8005731AC /* SettingsCoordinator.swift */; };
7A06E0B32C707E13005731AC /* SettingsServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A06E0B22C707E13005731AC /* SettingsServiceProtocol.swift */; };
7A06E0B52C707E2B005731AC /* SettingsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A06E0B42C707E2B005731AC /* SettingsService.swift */; };
7A1022692C55197D00B84627 /* RealmSwift in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 7ADF23052C25B5BF002624FF /* RealmSwift */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 7A1022692C55197D00B84627 /* RealmSwift in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 7ADF23052C25B5BF002624FF /* RealmSwift */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
7A10226C2C551EC500B84627 /* LocationEditScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A10226B2C551EC500B84627 /* LocationEditScreen.swift */; }; 7A10226C2C551EC500B84627 /* LocationEditScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A10226B2C551EC500B84627 /* LocationEditScreen.swift */; };
7A10226E2C551EE000B84627 /* LocationEditViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A10226D2C551EE000B84627 /* LocationEditViewModel.swift */; }; 7A10226E2C551EE000B84627 /* LocationEditViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A10226D2C551EE000B84627 /* LocationEditViewModel.swift */; };
@ -50,6 +55,7 @@
7A33381124990DAE00D878F1 /* FiltersController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A33381024990DAE00D878F1 /* FiltersController.swift */; }; 7A33381124990DAE00D878F1 /* FiltersController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A33381024990DAE00D878F1 /* FiltersController.swift */; };
7A3399AB299063370087DF98 /* SearchControllerExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3399AA299063370087DF98 /* SearchControllerExt.swift */; }; 7A3399AB299063370087DF98 /* SearchControllerExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3399AA299063370087DF98 /* SearchControllerExt.swift */; };
7A35177B27E23F8800DC538C /* Eureka in Frameworks */ = {isa = PBXBuildFile; productRef = 7A35177A27E23F8800DC538C /* Eureka */; }; 7A35177B27E23F8800DC538C /* Eureka in Frameworks */ = {isa = PBXBuildFile; productRef = 7A35177A27E23F8800DC538C /* Eureka */; };
7A3E12D72C7B42B700EE710D /* UserDefaults+Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3E12D62C7B42B700EE710D /* UserDefaults+Settings.swift */; };
7A3E30F32C18840600567704 /* ActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3E30F22C18840600567704 /* ActivityItemSource.swift */; }; 7A3E30F32C18840600567704 /* ActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3E30F22C18840600567704 /* ActivityItemSource.swift */; };
7A3F07AB24360DC800E59687 /* Dated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3F07AA24360DC800E59687 /* Dated.swift */; }; 7A3F07AB24360DC800E59687 /* Dated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3F07AA24360DC800E59687 /* Dated.swift */; };
7A3F07AD2436350B00E59687 /* SearchController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3F07AC2436350B00E59687 /* SearchController.swift */; }; 7A3F07AD2436350B00E59687 /* SearchController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A3F07AC2436350B00E59687 /* SearchController.swift */; };
@ -59,6 +65,7 @@
7A599C362C18AC7F00D47C18 /* ApiError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A599C352C18AC7F00D47C18 /* ApiError.swift */; }; 7A599C362C18AC7F00D47C18 /* ApiError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A599C352C18AC7F00D47C18 /* ApiError.swift */; };
7A599C392C18B22900D47C18 /* FbRefreshTokenModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A599C382C18B22900D47C18 /* FbRefreshTokenModel.swift */; }; 7A599C392C18B22900D47C18 /* FbRefreshTokenModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A599C382C18B22900D47C18 /* FbRefreshTokenModel.swift */; };
7A599C3B2C18B36A00D47C18 /* FbVerifyTokenModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A599C3A2C18B36A00D47C18 /* FbVerifyTokenModel.swift */; }; 7A599C3B2C18B36A00D47C18 /* FbVerifyTokenModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A599C3A2C18B36A00D47C18 /* FbVerifyTokenModel.swift */; };
7A5D7E0C2C71EB25002C17E7 /* ToggleRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5D7E0B2C71EB25002C17E7 /* ToggleRowView.swift */; };
7A5D84B92C1AD3C200C2209B /* DtoConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5D84B82C1AD3C200C2209B /* DtoConvertible.swift */; }; 7A5D84B92C1AD3C200C2209B /* DtoConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5D84B82C1AD3C200C2209B /* DtoConvertible.swift */; };
7A5D84BC2C1AD81000C2209B /* VehicleOwnershipPeriod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5D84BB2C1AD81000C2209B /* VehicleOwnershipPeriod.swift */; }; 7A5D84BC2C1AD81000C2209B /* VehicleOwnershipPeriod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5D84BB2C1AD81000C2209B /* VehicleOwnershipPeriod.swift */; };
7A5D84BE2C1AE44700C2209B /* VehiclePhoto.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5D84BD2C1AE44700C2209B /* VehiclePhoto.swift */; }; 7A5D84BE2C1AE44700C2209B /* VehiclePhoto.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5D84BD2C1AE44700C2209B /* VehiclePhoto.swift */; };
@ -161,7 +168,6 @@
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 */; }; 7AEFC3BE2529D3CC00BADFB2 /* ConfigurableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEFC3BD2529D3CC00BADFB2 /* ConfigurableCell.swift */; };
7AEFE728240455E200910EB7 /* SettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEFE727240455E200910EB7 /* SettingsController.swift */; };
7AF6D2042677C03B0086EA64 /* AutoCatCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7AF6D1EF2677C03B0086EA64 /* AutoCatCore.framework */; }; 7AF6D2042677C03B0086EA64 /* AutoCatCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7AF6D1EF2677C03B0086EA64 /* AutoCatCore.framework */; };
7AF6D2052677C03B0086EA64 /* AutoCatCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 7AF6D1EF2677C03B0086EA64 /* AutoCatCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 7AF6D2052677C03B0086EA64 /* AutoCatCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 7AF6D1EF2677C03B0086EA64 /* AutoCatCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
7AF6D2122677C12E0086EA64 /* Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A000AA124C2EEDE001F5B00 /* Location.swift */; }; 7AF6D2122677C12E0086EA64 /* Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A000AA124C2EEDE001F5B00 /* Location.swift */; };
@ -242,6 +248,11 @@
7A000AA124C2EEDE001F5B00 /* Location.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Location.swift; sourceTree = "<group>"; }; 7A000AA124C2EEDE001F5B00 /* Location.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Location.swift; sourceTree = "<group>"; };
7A0420A925619AEC00034941 /* Osago.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Osago.swift; sourceTree = "<group>"; }; 7A0420A925619AEC00034941 /* Osago.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Osago.swift; sourceTree = "<group>"; };
7A0516192414FF0900FC55AC /* DateSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateSection.swift; sourceTree = "<group>"; }; 7A0516192414FF0900FC55AC /* DateSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateSection.swift; sourceTree = "<group>"; };
7A06E0AB2C7065AB005731AC /* SettingsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreen.swift; sourceTree = "<group>"; };
7A06E0AD2C7065C7005731AC /* SettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = "<group>"; };
7A06E0AF2C7065D8005731AC /* SettingsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsCoordinator.swift; sourceTree = "<group>"; };
7A06E0B22C707E13005731AC /* SettingsServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsServiceProtocol.swift; sourceTree = "<group>"; };
7A06E0B42C707E2B005731AC /* SettingsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsService.swift; sourceTree = "<group>"; };
7A10226B2C551EC500B84627 /* LocationEditScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationEditScreen.swift; sourceTree = "<group>"; }; 7A10226B2C551EC500B84627 /* LocationEditScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationEditScreen.swift; sourceTree = "<group>"; };
7A10226D2C551EE000B84627 /* LocationEditViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationEditViewModel.swift; sourceTree = "<group>"; }; 7A10226D2C551EE000B84627 /* LocationEditViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationEditViewModel.swift; sourceTree = "<group>"; };
7A10226F2C551EFD00B84627 /* LocationEditCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationEditCoordinator.swift; sourceTree = "<group>"; }; 7A10226F2C551EFD00B84627 /* LocationEditCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationEditCoordinator.swift; sourceTree = "<group>"; };
@ -291,6 +302,7 @@
7A33381024990DAE00D878F1 /* FiltersController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersController.swift; sourceTree = "<group>"; }; 7A33381024990DAE00D878F1 /* FiltersController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersController.swift; sourceTree = "<group>"; };
7A333813249A532400D878F1 /* Filter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Filter.swift; sourceTree = "<group>"; }; 7A333813249A532400D878F1 /* Filter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Filter.swift; sourceTree = "<group>"; };
7A3399AA299063370087DF98 /* SearchControllerExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchControllerExt.swift; sourceTree = "<group>"; }; 7A3399AA299063370087DF98 /* SearchControllerExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchControllerExt.swift; sourceTree = "<group>"; };
7A3E12D62C7B42B700EE710D /* UserDefaults+Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+Settings.swift"; sourceTree = "<group>"; };
7A3E30F22C18840600567704 /* ActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityItemSource.swift; sourceTree = "<group>"; }; 7A3E30F22C18840600567704 /* ActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityItemSource.swift; sourceTree = "<group>"; };
7A3F07AA24360DC800E59687 /* Dated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dated.swift; sourceTree = "<group>"; }; 7A3F07AA24360DC800E59687 /* Dated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dated.swift; sourceTree = "<group>"; };
7A3F07AC2436350B00E59687 /* SearchController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchController.swift; sourceTree = "<group>"; }; 7A3F07AC2436350B00E59687 /* SearchController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchController.swift; sourceTree = "<group>"; };
@ -303,6 +315,7 @@
7A599C352C18AC7F00D47C18 /* ApiError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiError.swift; sourceTree = "<group>"; }; 7A599C352C18AC7F00D47C18 /* ApiError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiError.swift; sourceTree = "<group>"; };
7A599C382C18B22900D47C18 /* FbRefreshTokenModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FbRefreshTokenModel.swift; sourceTree = "<group>"; }; 7A599C382C18B22900D47C18 /* FbRefreshTokenModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FbRefreshTokenModel.swift; sourceTree = "<group>"; };
7A599C3A2C18B36A00D47C18 /* FbVerifyTokenModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FbVerifyTokenModel.swift; sourceTree = "<group>"; }; 7A599C3A2C18B36A00D47C18 /* FbVerifyTokenModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FbVerifyTokenModel.swift; sourceTree = "<group>"; };
7A5D7E0B2C71EB25002C17E7 /* ToggleRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToggleRowView.swift; sourceTree = "<group>"; };
7A5D84B82C1AD3C200C2209B /* DtoConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DtoConvertible.swift; sourceTree = "<group>"; }; 7A5D84B82C1AD3C200C2209B /* DtoConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DtoConvertible.swift; sourceTree = "<group>"; };
7A5D84BB2C1AD81000C2209B /* VehicleOwnershipPeriod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleOwnershipPeriod.swift; sourceTree = "<group>"; }; 7A5D84BB2C1AD81000C2209B /* VehicleOwnershipPeriod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleOwnershipPeriod.swift; sourceTree = "<group>"; };
7A5D84BD2C1AE44700C2209B /* VehiclePhoto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehiclePhoto.swift; sourceTree = "<group>"; }; 7A5D84BD2C1AE44700C2209B /* VehiclePhoto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehiclePhoto.swift; sourceTree = "<group>"; };
@ -409,7 +422,6 @@
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>"; };
7AE8424D26109F78002F6B31 /* Exportable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Exportable.swift; sourceTree = "<group>"; }; 7AE8424D26109F78002F6B31 /* Exportable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Exportable.swift; sourceTree = "<group>"; };
7AEFC3BD2529D3CC00BADFB2 /* ConfigurableCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurableCell.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>"; };
7AF6D1EF2677C03B0086EA64 /* AutoCatCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AutoCatCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7AF6D1EF2677C03B0086EA64 /* AutoCatCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AutoCatCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
7AF6D1F12677C03B0086EA64 /* AutoCatCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AutoCatCore.h; sourceTree = "<group>"; }; 7AF6D1F12677C03B0086EA64 /* AutoCatCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AutoCatCore.h; sourceTree = "<group>"; };
7AF6D1F22677C03B0086EA64 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 7AF6D1F22677C03B0086EA64 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@ -472,6 +484,26 @@
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
7A06E0AA2C706550005731AC /* SettingsScreen */ = {
isa = PBXGroup;
children = (
7A06E0AB2C7065AB005731AC /* SettingsScreen.swift */,
7A06E0AD2C7065C7005731AC /* SettingsViewModel.swift */,
7A06E0AF2C7065D8005731AC /* SettingsCoordinator.swift */,
);
path = SettingsScreen;
sourceTree = "<group>";
};
7A06E0B12C707DD7005731AC /* SettingsService */ = {
isa = PBXGroup;
children = (
7A06E0B22C707E13005731AC /* SettingsServiceProtocol.swift */,
7A06E0B42C707E2B005731AC /* SettingsService.swift */,
7A3E12D62C7B42B700EE710D /* UserDefaults+Settings.swift */,
);
path = SettingsService;
sourceTree = "<group>";
};
7A0B969D257D6CB3000B39AD /* eureka */ = { 7A0B969D257D6CB3000B39AD /* eureka */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -564,7 +596,6 @@
7A27ADC6249D43210035F39E /* RegionsController.swift */, 7A27ADC6249D43210035F39E /* RegionsController.swift */,
7A11471723FDEBFA00B424AF /* ReportController.swift */, 7A11471723FDEBFA00B424AF /* ReportController.swift */,
7A3F07AC2436350B00E59687 /* SearchController.swift */, 7A3F07AC2436350B00E59687 /* SearchController.swift */,
7AEFE727240455E200910EB7 /* SettingsController.swift */,
7AC3554B29696A1C00889457 /* MainTabController.swift */, 7AC3554B29696A1C00889457 /* MainTabController.swift */,
7AC3554D29696C4500889457 /* DummyNewController.swift */, 7AC3554D29696C4500889457 /* DummyNewController.swift */,
7AC3554F29696D5A00889457 /* NewNumberController.swift */, 7AC3554F29696D5A00889457 /* NewNumberController.swift */,
@ -628,6 +659,7 @@
7A1441632C297E9800E79018 /* Screens */ = { 7A1441632C297E9800E79018 /* Screens */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
7A06E0AA2C706550005731AC /* SettingsScreen */,
7A1022752C557E3F00B84627 /* LocationPickerScreen */, 7A1022752C557E3F00B84627 /* LocationPickerScreen */,
7A10226A2C551EA200B84627 /* LocationEditScreen */, 7A10226A2C551EA200B84627 /* LocationEditScreen */,
7A71580A2C44451B00852088 /* AdsScreen */, 7A71580A2C44451B00852088 /* AdsScreen */,
@ -680,6 +712,7 @@
7A45FB362C2706D000618694 /* Services */ = { 7A45FB362C2706D000618694 /* Services */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
7A06E0B12C707DD7005731AC /* SettingsService */,
7A60D24B2C5A9D2700D13F7B /* LocationService */, 7A60D24B2C5A9D2700D13F7B /* LocationService */,
7AB5873D2C42FF4000FA7B66 /* ApiService */, 7AB5873D2C42FF4000FA7B66 /* ApiService */,
7AB587302C42D35900FA7B66 /* StorageService */, 7AB587302C42D35900FA7B66 /* StorageService */,
@ -966,6 +999,7 @@
7A961C6D2C4C3C9E00CE2211 /* LinkRowView.swift */, 7A961C6D2C4C3C9E00CE2211 /* LinkRowView.swift */,
7AAAFAD92C4D1AFE0050410D /* Zoomable.swift */, 7AAAFAD92C4D1AFE0050410D /* Zoomable.swift */,
7A1022712C554A1300B84627 /* CustomHostingController.swift */, 7A1022712C554A1300B84627 /* CustomHostingController.swift */,
7A5D7E0B2C71EB25002C17E7 /* ToggleRowView.swift */,
); );
path = SwiftUI; path = SwiftUI;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1200,6 +1234,7 @@
7A961C6C2C4C3C8600CE2211 /* TextRowView.swift in Sources */, 7A961C6C2C4C3C8600CE2211 /* TextRowView.swift in Sources */,
7AEFC3BE2529D3CC00BADFB2 /* ConfigurableCell.swift in Sources */, 7AEFC3BE2529D3CC00BADFB2 /* ConfigurableCell.swift in Sources */,
7A1022772C557EC400B84627 /* LocationPickerScreen.swift in Sources */, 7A1022772C557EC400B84627 /* LocationPickerScreen.swift in Sources */,
7A5D7E0C2C71EB25002C17E7 /* ToggleRowView.swift in Sources */,
7A7158092C44087E00852088 /* OsagoCoordinator.swift in Sources */, 7A7158092C44087E00852088 /* OsagoCoordinator.swift in Sources */,
7A1441662C297EDE00E79018 /* NotesScreen.swift in Sources */, 7A1441662C297EDE00E79018 /* NotesScreen.swift in Sources */,
7A11470123FDE7E500B424AF /* AppDelegate.swift in Sources */, 7A11470123FDE7E500B424AF /* AppDelegate.swift in Sources */,
@ -1224,6 +1259,7 @@
7A17CE4A2A2E820300626A6E /* UIStackView.swift in Sources */, 7A17CE4A2A2E820300626A6E /* UIStackView.swift in Sources */,
7A1DC38E2517ED98002E9C99 /* BlockBarButtonItem.swift in Sources */, 7A1DC38E2517ED98002E9C99 /* BlockBarButtonItem.swift in Sources */,
7AE26A3324EEF9EC00625033 /* UIViewControllerExt.swift in Sources */, 7AE26A3324EEF9EC00625033 /* UIViewControllerExt.swift in Sources */,
7A06E0AE2C7065C7005731AC /* SettingsViewModel.swift in Sources */,
7A961C6E2C4C3C9E00CE2211 /* LinkRowView.swift in Sources */, 7A961C6E2C4C3C9E00CE2211 /* LinkRowView.swift in Sources */,
7A27ADF3249F8B650035F39E /* RecordsController.swift in Sources */, 7A27ADF3249F8B650035F39E /* RecordsController.swift in Sources */,
7A3E30F32C18840600567704 /* ActivityItemSource.swift in Sources */, 7A3E30F32C18840600567704 /* ActivityItemSource.swift in Sources */,
@ -1232,7 +1268,6 @@
7AC3554E29696C4500889457 /* DummyNewController.swift in Sources */, 7AC3554E29696C4500889457 /* DummyNewController.swift in Sources */,
7A7158122C444A6400852088 /* AdsViewModel.swift in Sources */, 7A7158122C444A6400852088 /* AdsViewModel.swift in Sources */,
7A659B5B24A3768A0043A0F2 /* Substrings.swift in Sources */, 7A659B5B24A3768A0043A0F2 /* Substrings.swift in Sources */,
7AEFE728240455E200910EB7 /* SettingsController.swift in Sources */,
7A71580E2C4445A200852088 /* AdsCoordinator.swift in Sources */, 7A71580E2C4445A200852088 /* AdsCoordinator.swift in Sources */,
7AFBE8CA2C3081C7003C491D /* ACProgressHud+Modifiers.swift in Sources */, 7AFBE8CA2C3081C7003C491D /* ACProgressHud+Modifiers.swift in Sources */,
7A27ADF7249FEF690035F39E /* Recorder.swift in Sources */, 7A27ADF7249FEF690035F39E /* Recorder.swift in Sources */,
@ -1276,10 +1311,12 @@
7A8A220B248D67B60073DFD9 /* VehicleReportImage.swift in Sources */, 7A8A220B248D67B60073DFD9 /* VehicleReportImage.swift in Sources */,
7AFBE8C42C302561003C491D /* ACHudContainer.swift in Sources */, 7AFBE8C42C302561003C491D /* ACHudContainer.swift in Sources */,
7AC3555B296995B200889457 /* UIEdgeInsets.swift in Sources */, 7AC3555B296995B200889457 /* UIEdgeInsets.swift in Sources */,
7A06E0AC2C7065AC005731AC /* SettingsScreen.swift in Sources */,
7A7158002C43EA6900852088 /* OwnersScreen.swift in Sources */, 7A7158002C43EA6900852088 /* OwnersScreen.swift in Sources */,
7A1441702C2998B200E79018 /* Formatters.swift in Sources */, 7A1441702C2998B200E79018 /* Formatters.swift in Sources */,
7ADF6C95250D037700F237B2 /* ShowEventController.swift in Sources */, 7ADF6C95250D037700F237B2 /* ShowEventController.swift in Sources */,
7A71580C2C44453200852088 /* AdsScreen.swift in Sources */, 7A71580C2C44453200852088 /* AdsScreen.swift in Sources */,
7A06E0B02C7065D8005731AC /* SettingsCoordinator.swift in Sources */,
7A91894F29A2BD8700519C74 /* GestureRecognizers.swift in Sources */, 7A91894F29A2BD8700519C74 /* GestureRecognizers.swift in Sources */,
7A27ADC7249D43210035F39E /* RegionsController.swift in Sources */, 7A27ADC7249D43210035F39E /* RegionsController.swift in Sources */,
7AFBE8CC2C3085C6003C491D /* ACProgressView.swift in Sources */, 7AFBE8CC2C3085C6003C491D /* ACProgressView.swift in Sources */,
@ -1343,12 +1380,15 @@
7AF6D21E2677C1680086EA64 /* PlateNumber.swift in Sources */, 7AF6D21E2677C1680086EA64 /* PlateNumber.swift in Sources */,
7A5D84C62C1AE72E00C2209B /* VehicleName.swift in Sources */, 7A5D84C62C1AE72E00C2209B /* VehicleName.swift in Sources */,
7A64A2122C19E2A100284124 /* VehicleModelDto.swift in Sources */, 7A64A2122C19E2A100284124 /* VehicleModelDto.swift in Sources */,
7A06E0B32C707E13005731AC /* SettingsServiceProtocol.swift in Sources */,
7A06E0B52C707E2B005731AC /* SettingsService.swift in Sources */,
7AF6D21F2677C1680086EA64 /* Response.swift in Sources */, 7AF6D21F2677C1680086EA64 /* Response.swift in Sources */,
7A60D24D2C5A9D4900D13F7B /* LocationService.swift in Sources */, 7A60D24D2C5A9D4900D13F7B /* LocationService.swift in Sources */,
7A60D24F2C5A9DA800D13F7B /* LocationServiceProtocol.swift in Sources */, 7A60D24F2C5A9DA800D13F7B /* LocationServiceProtocol.swift in Sources */,
7A761C07267E8E7F0005F28F /* AnyEncodable.swift in Sources */, 7A761C07267E8E7F0005F28F /* AnyEncodable.swift in Sources */,
7A64A2032C19DA1000284124 /* VehicleDto.swift in Sources */, 7A64A2032C19DA1000284124 /* VehicleDto.swift in Sources */,
7AB587322C42D38E00FA7B66 /* StorageServiceProtocol.swift in Sources */, 7AB587322C42D38E00FA7B66 /* StorageServiceProtocol.swift in Sources */,
7A3E12D72C7B42B700EE710D /* UserDefaults+Settings.swift in Sources */,
7A64A2222C19E99E00284124 /* DebugInfoDto.swift in Sources */, 7A64A2222C19E99E00284124 /* DebugInfoDto.swift in Sources */,
7A5D84BC2C1AD81000C2209B /* VehicleOwnershipPeriod.swift in Sources */, 7A5D84BC2C1AD81000C2209B /* VehicleOwnershipPeriod.swift in Sources */,
7A64A2202C19E93500284124 /* VehicleNoteDto.swift in Sources */, 7A64A2202C19E93500284124 /* VehicleNoteDto.swift in Sources */,
@ -1567,7 +1607,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements; CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 134; CURRENT_PROJECT_VERSION = 135;
DEVELOPMENT_TEAM = 46DTTB8X4S; DEVELOPMENT_TEAM = 46DTTB8X4S;
INFOPLIST_FILE = AutoCat/Info.plist; INFOPLIST_FILE = AutoCat/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = AutoCat; INFOPLIST_KEY_CFBundleDisplayName = AutoCat;
@ -1594,7 +1634,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements; CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 134; CURRENT_PROJECT_VERSION = 135;
DEVELOPMENT_TEAM = 46DTTB8X4S; DEVELOPMENT_TEAM = 46DTTB8X4S;
INFOPLIST_FILE = AutoCat/Info.plist; INFOPLIST_FILE = AutoCat/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = AutoCat; INFOPLIST_KEY_CFBundleDisplayName = AutoCat;

View File

@ -1,9 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23089" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="pme-aR-UNJ"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23091" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="pme-aR-UNJ">
<device id="retina4_7" orientation="portrait" appearance="dark"/> <device id="retina4_7" orientation="portrait" appearance="dark"/>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23079"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23077"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/> <capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@ -55,17 +54,17 @@
<color key="backgroundColor" systemColor="systemBackgroundColor"/> <color key="backgroundColor" systemColor="systemBackgroundColor"/>
<prototypes> <prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="EventCell" id="QIb-Hv-tvk" customClass="EventCell" customModule="AutoCat" customModuleProvider="target"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="EventCell" id="QIb-Hv-tvk" customClass="EventCell" customModule="AutoCat" customModuleProvider="target">
<rect key="frame" x="0.0" y="50" width="375" height="435"/> <rect key="frame" x="0.0" y="50" width="375" height="436"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="QIb-Hv-tvk" id="Ypt-ch-fGT"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="QIb-Hv-tvk" id="Ypt-ch-fGT">
<rect key="frame" x="0.0" y="0.0" width="375" height="435"/> <rect key="frame" x="0.0" y="0.0" width="375" height="436"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<stackView opaque="NO" contentMode="scaleToFill" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="HP8-oO-yhP"> <stackView opaque="NO" contentMode="scaleToFill" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="HP8-oO-yhP">
<rect key="frame" x="16" y="8" width="343" height="419"/> <rect key="frame" x="16" y="8" width="343" height="420"/>
<subviews> <subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="k4Z-KM-byE"> <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="k4Z-KM-byE">
<rect key="frame" x="0.0" y="0.0" width="335" height="419"/> <rect key="frame" x="0.0" y="0.0" width="335" height="420"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xcQ-Wz-gJ0"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xcQ-Wz-gJ0">
<rect key="frame" x="0.0" y="0.0" width="335" height="201"/> <rect key="frame" x="0.0" y="0.0" width="335" height="201"/>
@ -74,7 +73,7 @@
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1tQ-zM-6T9"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1tQ-zM-6T9">
<rect key="frame" x="0.0" y="209" width="335" height="210"/> <rect key="frame" x="0.0" y="209" width="335" height="211"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/> <fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
<color key="textColor" systemColor="secondaryLabelColor"/> <color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@ -82,7 +81,7 @@
</subviews> </subviews>
</stackView> </stackView>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="750" verticalHuggingPriority="251" image="person" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="CFI-xa-eLs"> <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="750" verticalHuggingPriority="251" image="person" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="CFI-xa-eLs">
<rect key="frame" x="343" y="1" width="0.0" height="416.5"/> <rect key="frame" x="343" y="1" width="0.0" height="417.5"/>
</imageView> </imageView>
</subviews> </subviews>
</stackView> </stackView>
@ -342,22 +341,6 @@
</objects> </objects>
<point key="canvasLocation" x="5688.8000000000002" y="142.57871064467767"/> <point key="canvasLocation" x="5688.8000000000002" y="142.57871064467767"/>
</scene> </scene>
<!--Settings-->
<scene sceneID="G47-o0-Caa">
<objects>
<viewController id="4jU-Z3-PF2" customClass="SettingsController" customModule="AutoCat" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="NFd-we-MVH">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<viewLayoutGuide key="safeArea" id="Uix-8K-fxh"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
</view>
<tabBarItem key="tabBarItem" title="Settings" image="settings" landscapeImage="settings-compact" id="zEL-ph-E2f"/>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="trD-gZ-yAv" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="3262" y="918"/>
</scene>
<!--Voice records--> <!--Voice records-->
<scene sceneID="9pI-G0-wG0"> <scene sceneID="9pI-G0-wG0">
<objects> <objects>
@ -655,7 +638,6 @@
<segue destination="RK6-pn-2Bg" kind="relationship" relationship="viewControllers" id="KNz-WF-Kyy"/> <segue destination="RK6-pn-2Bg" kind="relationship" relationship="viewControllers" id="KNz-WF-Kyy"/>
<segue destination="dxo-XS-x8Q" kind="relationship" relationship="viewControllers" id="MgV-gx-h7p"/> <segue destination="dxo-XS-x8Q" kind="relationship" relationship="viewControllers" id="MgV-gx-h7p"/>
<segue destination="GCa-Re-j14" kind="relationship" relationship="viewControllers" id="FGp-f6-fUh"/> <segue destination="GCa-Re-j14" kind="relationship" relationship="viewControllers" id="FGp-f6-fUh"/>
<segue destination="4jU-Z3-PF2" kind="relationship" relationship="viewControllers" id="aH2-IT-86l"/>
</connections> </connections>
</tabBarController> </tabBarController>
<placeholder placeholderIdentifier="IBFirstResponder" id="AJs-8F-Qbu" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="AJs-8F-Qbu" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
@ -928,8 +910,6 @@
<image name="record-compact" width="23" height="23"/> <image name="record-compact" width="23" height="23"/>
<image name="search" width="23" height="23"/> <image name="search" width="23" height="23"/>
<image name="search-compact" width="17" height="17"/> <image name="search-compact" width="17" height="17"/>
<image name="settings" width="25" height="25"/>
<image name="settings-compact" width="18" height="18"/>
<image name="square.and.arrow.up" catalog="system" width="110" height="128"/> <image name="square.and.arrow.up" catalog="system" width="110" height="128"/>
<image name="text.bubble" catalog="system" width="128" height="110"/> <image name="text.bubble" catalog="system" width="128" height="110"/>
<systemColor name="secondaryLabelColor"> <systemColor name="secondaryLabelColor">

View File

@ -46,12 +46,11 @@ class GoogleSignInController: UIViewController, WKNavigationDelegate {
} }
} }
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction) async -> WKNavigationActionPolicy {
if let url = navigationAction.request.url { if let url = navigationAction.request.url {
if let components = URLComponents(url: url, resolvingAgainstBaseURL: false) { if let components = URLComponents(url: url, resolvingAgainstBaseURL: false) {
if let queryItems = components.queryItems { if let queryItems = components.queryItems {
if let code = queryItems.first(where: { $0.name == "code" })?.value { if let code = queryItems.first(where: { $0.name == "code" })?.value {
decisionHandler(.cancel)
Task { @MainActor in Task { @MainActor in
do { do {
@ -62,12 +61,14 @@ class GoogleSignInController: UIViewController, WKNavigationDelegate {
HUD.flash(.labeledError(title: nil, subtitle: error.localizedDescription)) HUD.flash(.labeledError(title: nil, subtitle: error.localizedDescription))
} }
} }
return .cancel
} }
} }
} }
} }
decisionHandler(.allow) return .allow
} }
@IBAction func close(_ sender: UIBarButtonItem) { @IBAction func close(_ sender: UIBarButtonItem) {

View File

@ -4,6 +4,8 @@ import AutoCatCore
class MainTabController: UITabBarController, UITabBarControllerDelegate { class MainTabController: UITabBarController, UITabBarControllerDelegate {
var settingsCoordinator: SettingsCoordinator?
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
self.delegate = self self.delegate = self
@ -14,6 +16,15 @@ class MainTabController: UITabBarController, UITabBarControllerDelegate {
viewControllers?.remove(at: 2) viewControllers?.remove(at: 2)
#endif #endif
Task { await addSettings() }
}
func addSettings() async {
let coordinator = SettingsCoordinator(tabController: self)
settingsCoordinator = coordinator
try? await coordinator.start()
} }
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool { func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {

View File

@ -91,11 +91,10 @@ class RecordsController: UIViewController, UITableViewDelegate {
var alert: UIAlertController? var alert: UIAlertController?
var url: URL! var url: URL!
Task { Task { @MainActor in
do { do {
async let locationTask = RxLocationManager.requestCurrentLocation() let event = try await RxLocationManager.requestCurrentLocation()
async let permissionTask: () = recorder.requestPermissions() try await recorder.requestPermissions()
let (event, _) = try await (locationTask, permissionTask)
await makeStartSoundIfNeeded() await makeStartSoundIfNeeded()

View File

@ -129,10 +129,6 @@ class ReportController: FormViewController, MediaBrowserViewControllerDataSource
} }
.cellUpdate { cell, _ in cell.accessoryType = .disclosureIndicator } .cellUpdate { cell, _ in cell.accessoryType = .disclosureIndicator }
.onCellSelection { _, row in .onCellSelection { _, row in
// let controller = AdsController()
// controller.ads = self.vehicle?.ads ?? []
// self.navigationController?.pushViewController(controller, animated: true)
if let ads = self.vehicle?.ads, let navController = self.navigationController { if let ads = self.vehicle?.ads, let navController = self.navigationController {
let coordinator = AdsCoordinator(navController: navController, ads: ads) let coordinator = AdsCoordinator(navController: navController, ads: ads)
Task { try await coordinator.start() } Task { try await coordinator.start() }

View File

@ -1,151 +0,0 @@
import UIKit
import Eureka
import AuthenticationServices
import AutoCatCore
class SettingsController: FormViewController {
override func viewDidLoad() {
super.viewDidLoad()
form +++ Section()
<<< LabelRow() { row in
row.title = NSLocalizedString("Version", comment: "")
row.value = Bundle.main.fullVersion
}
form +++ Section(NSLocalizedString("Profile", comment: ""))
<<< LabelRow("AutoCatAccount") { row in
row.title = NSLocalizedString("AutoCat Account", comment: "")
row.value = Settings.shared.user.email
}
<<< LabelRow("GoogleAccount") { row in
row.title = NSLocalizedString("Google", comment: "")
if let jwtString = Settings.shared.user.firebaseIdToken, let jwt = JWT<FirebasePayload>(string: jwtString) {
row.value = jwt.payload.email
} else {
row.value = NSLocalizedString("Log In", comment: "")
}
row.cellUpdate { cell, _ in
cell.accessoryType = .disclosureIndicator
}
}.onCellSelection { cell, row in
if Settings.shared.user.firebaseIdToken != nil {
self.displayLogoutSheet(for: "GoogleAccount")
} else {
self.loginToGoogle()
}
}
+++ Section(header: NSLocalizedString("Plate number recognition", comment: ""), footer: NSLocalizedString("Recognize plate numbers in alternative form. For example 'ЕВА 123 777' instead of 'Е123ВА 777'", comment: ""))
<<< SwitchRow("AlternateRecognition") { row in
row.title = NSLocalizedString("Alternative order", comment: "")
row.value = Settings.shared.recognizeAlternativeOrder
}.onChange { row in
if let val = row.value {
Settings.shared.recognizeAlternativeOrder = val
}
}
+++ Section(footer: NSLocalizedString("If enabled, app will try to recognize shortened plate numbers (without region) and add default region", comment: ""))
<<< SwitchRow("ShortenedNumbers") { row in
row.title = NSLocalizedString("Shortened numbers", comment: "")
row.value = Settings.shared.recognizeShortenedNumbers
}.onChange { row in
if let val = row.value {
Settings.shared.recognizeShortenedNumbers = val
}
}
<<< TextRow("") { row in
row.title = NSLocalizedString("Default region", comment: "")
row.placeholder = "161"
row.value = Settings.shared.defaultRegion
row.hidden = Condition.function(["ShortenedNumbers"], { form in
return !((form.rowBy(tag: "ShortenedNumbers") as? SwitchRow)?.value ?? false)
})
}.onChange { row in
if let val = row.value {
Settings.shared.defaultRegion = val
}
}
+++ Section(footer: NSLocalizedString("When enabled, you will hear short sound before starting audio recording. This will only work when audio record is started via Siri", comment: "")) { $0.tag = "BeepRecordSection" }
<<< SwitchRow("BeepRecord") { row in
row.title = NSLocalizedString("Beep before record", comment: "")
row.value = Settings.shared.recordBeep
}.onChange{ row in
if let val = row.value {
Settings.shared.recordBeep = val
}
}
+++ Section(NSLocalizedString("Debug", comment: ""))
<<< SwitchRow() { row in
row.title = NSLocalizedString("Show debug info", comment: "")
row.value = Settings.shared.showDebugInfo
}.onChange { row in
if let val = row.value {
Settings.shared.showDebugInfo = val
}
}
+++ Section("")
<<< ButtonRow("SignOut") { $0.title = NSLocalizedString("Sign Out", comment: "") }.onCellSelection { cell, row in
self.logout()
}
#if targetEnvironment(macCatalyst)
if let beepSection = self.form.sectionBy(tag: "BeepRecordSection") {
beepSection.hidden = true
beepSection.evaluateHidden()
}
#endif
}
func logout() {
Settings.shared.user.token = ""
let storyboard = UIStoryboard(name: "Main", bundle: nil)
self.view.window?.rootViewController = storyboard.instantiateViewController(identifier: "AuthController")
}
func displayLogoutSheet(for row: String) {
guard let jwtString = Settings.shared.user.firebaseIdToken, let jwt = JWT<FirebasePayload>(string: jwtString) else { return }
let msg = String.localizedStringWithFormat(NSLocalizedString("You are currently signed in with email %@. It will help to gather more data about vehicles.", comment: ""), jwt.payload.email)
let sheet = UIAlertController(title: jwt.payload.name, message: msg, preferredStyle: .actionSheet)
let cancel = UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel) { _ in sheet.dismiss(animated: true, completion: nil) }
let logout = UIAlertAction(title: NSLocalizedString("Sign Out", comment: ""), style: .destructive) { _ in
Settings.shared.user.firebaseIdToken = nil
Settings.shared.user.firebaseRefreshToken = nil
if let row = self.form.rowBy(tag: row) as? LabelRow {
row.value = NSLocalizedString("Log In", comment: "")
row.reload()
}
}
sheet.addAction(logout)
sheet.addAction(cancel)
self.present(sheet, animated: true, completion: nil)
}
func loginToGoogle() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if let vc = storyboard.instantiateViewController(identifier: "GoogleSignInController") as? GoogleSignInController {
vc.completion = {
guard let googleRow = self.form.rowBy(tag: "GoogleAccount") as? LabelRow else { return }
if let jwtString = Settings.shared.user.firebaseIdToken, let jwt = JWT<FirebasePayload>(string: jwtString) {
googleRow.value = jwt.payload.email
} else {
googleRow.value = NSLocalizedString("Log In", comment: "")
}
googleRow.reload()
}
self.present(vc, animated: true)
}
}
func loginToApple() {
}
}

View File

@ -0,0 +1,51 @@
//
// SettingsCoordinator.swift
// AutoCat
//
// Created by Selim Mustafaev on 17.08.2024.
// Copyright © 2024 Selim Mustafaev. All rights reserved.
//
import UIKit
import SwiftUI
import AutoCatCore
@MainActor
class SettingsCoordinator: Coordinator {
weak var viewController: UITabBarController?
var settingsController: UIViewController?
init(tabController: UITabBarController) {
self.viewController = tabController
}
func start() async throws {
let viewModel = SettingsViewModel(settingService: SettingsService.shared)
viewModel.coordinator = self
let controller = UIHostingController(rootView: SettingsScreen(viewModel: viewModel))
settingsController = controller
let navController = UINavigationController(rootViewController: controller)
navController.tabBarItem = UITabBarItem(title: NSLocalizedString("Settings", comment: ""),
image: UIImage(systemName: "gear"), tag: 0)
viewController?.viewControllers?.append(navController)
}
func openAuthScreen() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
viewController?.view.window?.rootViewController = storyboard.instantiateViewController(identifier: "AuthController")
}
func openGoogleOauthPage() {
print("===== openGoogleOauthPage")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if let vc = storyboard.instantiateViewController(identifier: "GoogleSignInController") as? GoogleSignInController {
settingsController?.present(vc, animated: true)
}
}
}

View File

@ -0,0 +1,87 @@
//
// SettingsScreen.swift
// AutoCat
//
// Created by Selim Mustafaev on 17.08.2024.
// Copyright © 2024 Selim Mustafaev. All rights reserved.
//
import SwiftUI
import AutoCatCore
struct SettingsScreen: View {
@State var viewModel: SettingsViewModel
@State var googleSheetOpened = false
var body: some View {
Form {
Section {
TextRowView(title: "Version", value: Bundle.main.fullVersion)
}
Section("Profile") {
SimpleTextRowView(title: "AutoCat Account", value: viewModel.autocatEmail)
SimpleTextRowView(title: "Google",
value: viewModel.googleEmail ?? "Log In",
showArrow: true)
.onTapGesture {
if viewModel.googleAuthorized {
googleSheetOpened = true
} else {
viewModel.googleSignIn()
}
}
if viewModel.canSignOut {
Button("Sign Out", action: viewModel.signOut)
}
}
Section("Plate number recognition") {
ToggleRowView(title: "Alternative order",
description: "Recognize plate numbers in alternative form. For example 'ЕВА 123 777' instead of 'Е123ВА 777'",
toggle: $viewModel.settingService.recognizeAlternativeOrder)
ToggleRowView(title: "Shortened numbers",
description: "If enabled, app will try to recognize shortened plate numbers (without region) and add default region",
toggle: $viewModel.settingService.recognizeShortenedNumbers)
if viewModel.settingService.recognizeShortenedNumbers {
LabeledContent("Default region") {
TextField("", text: $viewModel.settingService.defaultRegion)
.frame(width: 50)
.multilineTextAlignment(.trailing)
}
}
ToggleRowView(title: "Beep before record",
description: "When enabled, you will hear short sound before starting audio recording. This will only work when audio record is started via Siri",
toggle: $viewModel.settingService.recordBeep)
}
Section("Debug") {
ToggleRowView(title: "Show debug info",
description: nil,
toggle: $viewModel.settingService.showDebugInfo)
ToggleRowView(title: "Use test backend",
description: nil,
toggle: $viewModel.settingService.useTestBackend)
}
}
.navigationTitle("Settings")
.confirmationDialog(viewModel.googleUsername ?? "",
isPresented: $googleSheetOpened,
titleVisibility: .visible) {
Button("Sign Out", role: .destructive) {
viewModel.googleSignout()
}
} message: {
if let email = viewModel.googleEmail {
Text("You are currently signed in with email \(email). It will help to gather more data about vehicles.")
}
}
}
}
#Preview {
SettingsScreen(viewModel: .init(settingService: SettingsService(defaults: .standard)))
}

View File

@ -0,0 +1,62 @@
//
// SettingsViewModel.swift
// AutoCat
//
// Created by Selim Mustafaev on 17.08.2024.
// Copyright © 2024 Selim Mustafaev. All rights reserved.
//
import SwiftUI
import AutoCatCore
@MainActor
@Observable
class SettingsViewModel {
weak var coordinator: SettingsCoordinator?
var settingService: SettingsServiceProtocol
var autocatEmail: String {
settingService.user.email
}
var canSignOut: Bool {
!settingService.user.token.isEmpty
}
var googleAuthorized: Bool {
settingService.user.firebaseIdToken != nil
}
var googleUsername: String? {
guard let jwtString = settingService.user.firebaseIdToken,
let jwt = JWT<FirebasePayload>(string: jwtString) else { return nil }
return jwt.payload.name
}
var googleEmail: String? {
guard let jwtString = settingService.user.firebaseIdToken,
let jwt = JWT<FirebasePayload>(string: jwtString) else { return nil }
return jwt.payload.email
}
init(settingService: SettingsServiceProtocol) {
self.settingService = settingService
}
func signOut() {
settingService.user.token = ""
coordinator?.openAuthScreen()
}
func googleSignIn() {
coordinator?.openGoogleOauthPage()
}
func googleSignout() {
settingService.user.firebaseIdToken = nil
settingService.user.firebaseRefreshToken = nil
}
}

View File

@ -12,11 +12,18 @@ struct TextRowView: View {
let title: LocalizedStringKey let title: LocalizedStringKey
let value: String? let value: String?
let showArrow: Bool
init(title: LocalizedStringKey, value: String?, showArrow: Bool = false) {
self.title = title
self.value = value
self.showArrow = showArrow
}
var body: some View { var body: some View {
if #available(iOS 16.0, *) { if #available(iOS 16.0, *) {
ViewThatFits(in: .horizontal) { ViewThatFits(in: .horizontal) {
SimpleTextRowView(title: title, value: value) SimpleTextRowView(title: title, value: value, showArrow: showArrow)
VStack(alignment: .leading, spacing: 8) { VStack(alignment: .leading, spacing: 8) {
Text(title) Text(title)
HStack { HStack {
@ -25,11 +32,16 @@ struct TextRowView: View {
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
.multilineTextAlignment(.trailing) .multilineTextAlignment(.trailing)
.padding(.leading) .padding(.leading)
if showArrow {
Image(systemName: "chevron.right")
.padding(.leading, 8)
.foregroundStyle(.secondary)
}
} }
} }
} }
} else { } else {
SimpleTextRowView(title: title, value: value) SimpleTextRowView(title: title, value: value, showArrow: showArrow)
} }
} }
} }
@ -38,21 +50,35 @@ struct SimpleTextRowView: View {
let title: LocalizedStringKey let title: LocalizedStringKey
let value: String? let value: String?
let showArrow: Bool
init(title: LocalizedStringKey, value: String?, showArrow: Bool = false) {
self.title = title
self.value = value
self.showArrow = showArrow
}
var body: some View { var body: some View {
HStack(alignment: .firstTextBaseline, spacing: 0) { HStack(alignment: .firstTextBaseline, spacing: 0) {
Text(title) Text(title)
Spacer() Spacer()
Text(value ?? "") Text(value ?? "")
.lineLimit(1)
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
if showArrow {
Image(systemName: "chevron.right")
.padding(.leading, 8)
.foregroundStyle(.secondary)
}
} }
} }
} }
#Preview { #Preview {
List { List {
TextRowView(title: "Title", value: "Value") TextRowView(title: "Title", value: "Value", showArrow: true)
TextRowView(title: "Title", value: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas cursus posuere varius. Maecenas diam neque, lacinia molestie tristique eget, faucibus egestas nibh.") TextRowView(title: "Title", value: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas cursus posuere varius. Maecenas diam neque, lacinia molestie tristique eget, faucibus egestas nibh.", showArrow: true)
TextRowView(title: "Some longer title", value: "Value with long long text") TextRowView(title: "Some longer title", value: "Value with long long text")
SimpleTextRowView(title: "Some longer title", value: "Value with long long text")
} }
} }

View File

@ -0,0 +1,57 @@
//
// ToggleRowView.swift
// AutoCat
//
// Created by Selim Mustafaev on 18.08.2024.
// Copyright © 2024 Selim Mustafaev. All rights reserved.
//
import SwiftUI
extension VerticalAlignment {
enum CustomVerticalAlignment: AlignmentID {
static func defaultValue(in context: ViewDimensions) -> CGFloat {
context[.top]
}
}
static let customTop = VerticalAlignment(CustomVerticalAlignment.self)
}
struct ToggleRowView: View {
let title: LocalizedStringKey
let description: LocalizedStringKey?
let toggle: Binding<Bool>
var body: some View {
HStack(alignment: .customTop) {
VStack(alignment: .leading) {
Text(title)
.alignmentGuide(.customTop, computeValue: { $0[.firstTextBaseline] })
if let description {
Text(description)
.font(.footnote)
.foregroundStyle(.secondary)
}
}
Spacer()
Toggle(isOn: toggle) {
Text(" ")
.alignmentGuide(.customTop, computeValue: { $0[.firstTextBaseline] })
}
.fixedSize()
}
}
}
#Preview {
List {
ToggleRowView(title: "Title",
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas cursus posuere varius. Maecenas diam neque, lacinia molestie tristique eget, faucibus egestas nibh.",
toggle: .constant(true))
ToggleRowView(title: "Title", description: nil, toggle: .constant(true))
}
}

View File

@ -5,7 +5,7 @@ import AudioToolbox
import os.log import os.log
import ExceptionCatcher import ExceptionCatcher
class Recorder { final class Recorder {
let engine = AVAudioEngine() let engine = AVAudioEngine()
var fileRef: ExtAudioFileRef? = nil var fileRef: ExtAudioFileRef? = nil

View File

@ -6,8 +6,8 @@ public struct User: Codable, Sendable {
public var firebaseIdToken: String? public var firebaseIdToken: String?
public var firebaseRefreshToken: String? public var firebaseRefreshToken: String?
public init() { public init(email: String = "", token: String = "") {
self.email = "" self.email = email
self.token = "" self.token = token
} }
} }

View File

@ -2,7 +2,13 @@ import Foundation
public actor ApiService: ApiServiceProtocol { public actor ApiService: ApiServiceProtocol {
public static let shared = ApiService() public static let shared = ApiService(settingsService: SettingsService.shared)
let settingsService: SettingsServiceProtocol
init(settingsService: SettingsServiceProtocol) {
self.settingsService = settingsService
}
private let session: URLSession = { private let session: URLSession = {
let sessionConfig = URLSessionConfiguration.default let sessionConfig = URLSessionConfiguration.default
@ -13,12 +19,11 @@ public actor ApiService: ApiServiceProtocol {
// MARK: - Private wrappres // MARK: - Private wrappres
private func genError(_ msg: String, suggestion: String, code: Int = 0) -> Error {
return NSError(domain: "", code: code, userInfo: [NSLocalizedDescriptionKey: msg, NSLocalizedRecoverySuggestionErrorKey: suggestion])
}
private func createRequest<B,P>(api: String, method: String, body: B? = nil, params: [String:P]? = nil) -> URLRequest? where B: Encodable, P: LosslessStringConvertible { private func createRequest<B,P>(api: String, method: String, body: B? = nil, params: [String:P]? = nil) -> URLRequest? where B: Encodable, P: LosslessStringConvertible {
guard var urlComponents = URLComponents(string: Constants.baseUrl + api) else { return nil }
let baseUrl = settingsService.useTestBackend ? Constants.baseUrlDebug : Constants.baseUrl
guard var urlComponents = URLComponents(string: baseUrl + api) else { return nil }
if let params = params, method.uppercased() == "GET" { if let params = params, method.uppercased() == "GET" {
urlComponents.queryItems = params.map { URLQueryItem(name: $0, value: String($1)) } urlComponents.queryItems = params.map { URLQueryItem(name: $0, value: String($1)) }

View File

@ -0,0 +1,122 @@
//
// SettingsService.swift
// AutoCatCore
//
// Created by Selim Mustafaev on 17.08.2024.
// Copyright © 2024 Selim Mustafaev. All rights reserved.
//
import SwiftUI
@Observable
public final class SettingsService: SettingsServiceProtocol {
public static let shared = SettingsService(defaults: .standard)
let defaults: UserDefaults
var observations: [NSKeyValueObservation] = []
let jsonEncoder = JSONEncoder()
let jsonDecoder = JSONDecoder()
public var user: User {
get {
if let data = userData {
let result = try? jsonDecoder.decode(User.self, from: data)
return result ?? User()
} else {
return User()
}
}
set {
if let json = try? jsonEncoder.encode(newValue) {
userData = json
}
}
}
public var userData: Data? = nil {
didSet {
defaults.user = userData
}
}
public var recognizeAlternativeOrder: Bool = false {
didSet {
defaults.recognizeAlternativeOrder = recognizeAlternativeOrder
}
}
public var recognizeShortenedNumbers: Bool = false {
didSet {
defaults.recognizeShortenedNumbers = recognizeShortenedNumbers
}
}
public var defaultRegion: String = "161" {
didSet {
defaults.defaultRegion = defaultRegion
}
}
public var recordBeep: Bool = false {
didSet {
defaults.recordBeep = recordBeep
}
}
public var showDebugInfo: Bool = false {
didSet {
defaults.showDebugInfo = showDebugInfo
}
}
public var useTestBackend: Bool = false {
didSet {
defaults.useTestBackend = useTestBackend
}
}
public init(defaults: UserDefaults) {
self.defaults = defaults
observe(key: \.recognizeAlternativeOrder, for: \.recognizeAlternativeOrder)
observe(key: \.recognizeShortenedNumbers, for: \.recognizeShortenedNumbers)
observe(key: \.defaultRegion, for: \.defaultRegion)
observe(key: \.recordBeep, for: \.recordBeep)
observe(key: \.showDebugInfo, for: \.showDebugInfo)
observe(key: \.useTestBackend, for: \.useTestBackend)
observe(key: \.user, for: \.userData)
register(defaultValues: [
.recognizeAlternativeOrder: false,
.recognizeShortenedNumbers: false,
.defaultRegion: "761",
.recordBeep: false,
.showDebugInfo: false,
.useTestBackend: false
])
}
deinit {
observations.forEach { $0.invalidate() }
}
func observe<T>(key userDefaultsKey: KeyPath<UserDefaults,T>, for settingsKey: ReferenceWritableKeyPath<SettingsService,T>) where T: Equatable {
let observation = defaults.observe(userDefaultsKey, options: [.initial, .new]) { [weak self] _, change in
guard let self else { return }
if let new = change.newValue, self[keyPath: settingsKey] != new {
self[keyPath: settingsKey] = new
}
}
observations.append(observation)
}
func register(defaultValues: [SettingsKey: Any]) {
defaults.register(defaults: defaultValues.reduce(into: [:], { (partialResult: inout [String: Any], tuple) in
partialResult[tuple.key.rawValue] = tuple.value
}))
}
}

View File

@ -0,0 +1,21 @@
//
// SettingsServiceProtocol.swift
// AutoCatCore
//
// Created by Selim Mustafaev on 17.08.2024.
// Copyright © 2024 Selim Mustafaev. All rights reserved.
//
import Foundation
public protocol SettingsServiceProtocol {
var user: User { get set }
var recognizeAlternativeOrder: Bool { get set }
var recognizeShortenedNumbers: Bool { get set }
var defaultRegion: String { get set }
var recordBeep: Bool { get set }
var showDebugInfo: Bool { get set }
var useTestBackend: Bool { get set }
}

View File

@ -0,0 +1,74 @@
//
// UserDefaults+Settings.swift
// AutoCatCore
//
// Created by Selim Mustafaev on 25.08.2024.
// Copyright © 2024 Selim Mustafaev. All rights reserved.
//
import Foundation
enum SettingsKey: String {
case user
case recognizeAlternativeOrder
case recognizeShortenedNumbers
case defaultRegion
case recordBeep
case showDebugInfo
case useTestBackend
}
extension UserDefaults {
func bool(for key: SettingsKey) -> Bool {
bool(forKey: key.rawValue)
}
func string(for key: SettingsKey, defaultValue: String = "") -> String {
string(forKey: key.rawValue) ?? defaultValue
}
func data(for key: SettingsKey) -> Data? {
data(forKey: key.rawValue)
}
func set<T>(value: T, for key: SettingsKey) {
setValue(value, forKey: key.rawValue)
}
@objc dynamic var recognizeAlternativeOrder: Bool {
get { bool(for: .recognizeAlternativeOrder) }
set { set(value: newValue, for: .recognizeAlternativeOrder) }
}
@objc dynamic var recognizeShortenedNumbers: Bool {
get { bool(for: .recognizeShortenedNumbers) }
set { set(value: newValue, for: .recognizeShortenedNumbers) }
}
@objc dynamic var defaultRegion: String {
get { string(for: .defaultRegion) }
set { set(value: newValue, for: .defaultRegion) }
}
@objc dynamic var recordBeep: Bool {
get { bool(for: .recordBeep) }
set { set(value: newValue, for: .recordBeep) }
}
@objc dynamic var showDebugInfo: Bool {
get { bool(for: .showDebugInfo) }
set { set(value: newValue, for: .showDebugInfo) }
}
@objc dynamic var useTestBackend: Bool {
get { bool(for: .useTestBackend) }
set { set(value: newValue, for: .useTestBackend) }
}
@objc dynamic var user: Data? {
get { data(for: .user) }
set { set(value: newValue, for: .user) }
}
}

View File

@ -11,6 +11,8 @@ public enum Constants {
#endif #endif
} }
public static var baseUrlDebug = "http://192.168.1.2:3000/"
public static let pnLettersMap: [Character: Character] = [ public static let pnLettersMap: [Character: Character] = [
"А": "A", "В": "B", "Е": "E", "К": "K", "М": "M", "Н": "H", "О": "O", "Р": "P", "С": "C", "Т": "T", "У": "Y", "Х": "X" "А": "A", "В": "B", "Е": "E", "К": "K", "М": "M", "Н": "H", "О": "O", "Р": "P", "С": "C", "Т": "T", "У": "Y", "Х": "X"
] ]

View File

@ -0,0 +1,84 @@
//
// SettingsServiceTests.swift
// AutoCatCoreTests
//
// Created by Selim Mustafaev on 17.08.2024.
// Copyright © 2024 Selim Mustafaev. All rights reserved.
//
import Foundation
import Testing
@testable import AutoCatCore
extension Encodable {
var data: Data? {
let encoder = JSONEncoder()
return try? encoder.encode(self)
}
}
struct SettingsServiceTests {
let testRegion = "xxx"
let testEmail = "test@test.test"
let testToken = "testToken"
let testUser: User
let dbName = "testUserDefaults"
let defaults: UserDefaults
var settingsService: SettingsService
init() {
self.testUser = User(email: testEmail, token: testToken)
self.defaults = UserDefaults(suiteName: dbName)!
self.defaults.removePersistentDomain(forName: dbName)
self.settingsService = SettingsService(defaults: defaults)
}
@Test("Creating default user")
mutating func defaultUser() async throws {
#expect(settingsService.user.email == "")
#expect(settingsService.user.token == "")
}
@Test("Loading saved user")
func loadSavedUser() async throws {
let data = try #require(testUser.data)
defaults.setPersistentDomain(["user": data], forName: dbName)
let service = SettingsService(defaults: defaults)
#expect(service.user.email == testEmail)
#expect(service.user.token == testToken)
}
@Test("Save user")
func saveUser() async throws {
settingsService.user = testUser
let userData = defaults.data(forKey: "user")
#expect(userData == testUser.data)
}
@Test("Save settings", .serialized, arguments: [true, false])
func saveSettings(value: Bool) {
settingsService.recognizeAlternativeOrder = value
settingsService.recognizeShortenedNumbers = value
settingsService.defaultRegion = testRegion
settingsService.recordBeep = value
settingsService.showDebugInfo = value
settingsService.useTestBackend = value
#expect(defaults.bool(forKey: SettingsKey.recognizeAlternativeOrder.rawValue) == value)
#expect(defaults.bool(forKey: SettingsKey.recognizeShortenedNumbers.rawValue) == value)
#expect(defaults.string(forKey: SettingsKey.defaultRegion.rawValue) == testRegion)
#expect(defaults.bool(forKey: SettingsKey.recordBeep.rawValue) == value)
#expect(defaults.bool(forKey: SettingsKey.showDebugInfo.rawValue) == value)
#expect(defaults.bool(forKey: SettingsKey.useTestBackend.rawValue) == value)
}
}