From 67977f1ebf1b295be2d01c354ed4cc67b1490921 Mon Sep 17 00:00:00 2001 From: Selim Mustafaev Date: Wed, 15 Jan 2025 22:11:08 +0300 Subject: [PATCH] Removing Kingfisher package. Adding License Plate view. Adding stub for new SwiftUI history screen. --- AutoCat.xcodeproj/project.pbxproj | 49 ++- .../xcshareddata/swiftpm/Package.resolved | 11 +- AutoCat/AppDelegate.swift | 28 -- AutoCat/Cells/VehiclePhotoCell.swift | 38 --- AutoCat/Controllers/MainTabController.swift | 10 + AutoCat/Extensions/VehicleReportImage.swift | 280 ------------------ AutoCat/SceneDelegate.swift | 39 ++- .../HistoryScreen/HistoryCoordinator.swift | 34 +++ .../Screens/HistoryScreen/HistoryScreen.swift | 28 ++ .../HistoryScreen/HistoryViewModel.swift | 33 +++ AutoCat/SwiftUI/LicensePlateView.swift | 73 +++++ .../StorageService/StorageService.swift | 5 + .../StorageServiceProtocol.swift | 10 +- 13 files changed, 252 insertions(+), 386 deletions(-) delete mode 100644 AutoCat/Cells/VehiclePhotoCell.swift delete mode 100644 AutoCat/Extensions/VehicleReportImage.swift create mode 100644 AutoCat/Screens/HistoryScreen/HistoryCoordinator.swift create mode 100644 AutoCat/Screens/HistoryScreen/HistoryScreen.swift create mode 100644 AutoCat/Screens/HistoryScreen/HistoryViewModel.swift create mode 100644 AutoCat/SwiftUI/LicensePlateView.swift diff --git a/AutoCat.xcodeproj/project.pbxproj b/AutoCat.xcodeproj/project.pbxproj index 8540678..65a1476 100644 --- a/AutoCat.xcodeproj/project.pbxproj +++ b/AutoCat.xcodeproj/project.pbxproj @@ -30,6 +30,9 @@ 7A11470D23FDE7E600B424AF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7A11470B23FDE7E600B424AF /* LaunchScreen.storyboard */; }; 7A11471623FDEB2A00B424AF /* MainSplitController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A11471523FDEB2A00B424AF /* MainSplitController.swift */; }; 7A11471A23FE839000B424AF /* AuthController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A11471923FE839000B424AF /* AuthController.swift */; }; + 7A131FD32D37B75500DC7755 /* HistoryScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A131FD22D37B75500DC7755 /* HistoryScreen.swift */; }; + 7A131FD52D37B76A00DC7755 /* HistoryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A131FD42D37B76A00DC7755 /* HistoryViewModel.swift */; }; + 7A131FD72D37B77E00DC7755 /* HistoryCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A131FD62D37B77E00DC7755 /* HistoryCoordinator.swift */; }; 7A1441662C297EDE00E79018 /* NotesScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1441652C297EDE00E79018 /* NotesScreen.swift */; }; 7A1441682C297EFD00E79018 /* NotesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1441672C297EFD00E79018 /* NotesViewModel.swift */; }; 7A14416C2C297F2100E79018 /* NotesCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A14416B2C297F2100E79018 /* NotesCoordinator.swift */; }; @@ -115,7 +118,6 @@ 7A71580E2C4445A200852088 /* AdsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A71580D2C4445A200852088 /* AdsCoordinator.swift */; }; 7A7158122C444A6400852088 /* AdsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7158112C444A6400852088 /* AdsViewModel.swift */; }; 7A71EF572D0A26B200943129 /* EventModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A71EF562D0A26B200943129 /* EventModel.swift */; }; - 7A7547E024032CB6004E8406 /* VehiclePhotoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7547DF24032CB6004E8406 /* VehiclePhotoCell.swift */; }; 7A761C042677F18E0005F28F /* ApiService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A11474323FF06CA00B424AF /* ApiService.swift */; }; 7A761C052677F1BC0005F28F /* CocoaError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A27ADF824A09CAD0035F39E /* CocoaError.swift */; }; 7A761C07267E8E7F0005F28F /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A15051124DB3E3000F39631 /* AnyEncodable.swift */; }; @@ -124,8 +126,8 @@ 7A761C0B267E8FF90005F28F /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A761C0A267E8FF90005F28F /* Error.swift */; }; 7A813DC32508EE4F00CC93B9 /* EventCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A813DC22508EE4F00CC93B9 /* EventCell.swift */; }; 7A8A2209248D10EC0073DFD9 /* ResizeImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A2208248D10EC0073DFD9 /* ResizeImage.swift */; }; - 7A8A220B248D67B60073DFD9 /* VehicleReportImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A220A248D67B60073DFD9 /* VehicleReportImage.swift */; }; 7A8AB76525A0DB8F00ECF2C1 /* BundleVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8AB76425A0DB8F00ECF2C1 /* BundleVersion.swift */; }; + 7A912F372D381B7400002938 /* LicensePlateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A912F362D381B7400002938 /* LicensePlateView.swift */; }; 7A91894F29A2BD8700519C74 /* GestureRecognizers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A91894E29A2BD8700519C74 /* GestureRecognizers.swift */; }; 7A961C6C2C4C3C8600CE2211 /* TextRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A961C6B2C4C3C8600CE2211 /* TextRowView.swift */; }; 7A961C6E2C4C3C9E00CE2211 /* LinkRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A961C6D2C4C3C9E00CE2211 /* LinkRowView.swift */; }; @@ -134,7 +136,6 @@ 7A99406426E4BFAE002E9CB6 /* VehicleNoteCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A99406326E4BFAE002E9CB6 /* VehicleNoteCell.swift */; }; 7A9FEEC82529AB23001CA50E /* RxRealmDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9FEEC72529AB23001CA50E /* RxRealmDataSource.swift */; }; 7AA514E02D0B75B3001CAC50 /* StorageService+Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AA514DF2D0B75B3001CAC50 /* StorageService+Events.swift */; }; - 7AA7BC3325A5DFB80053A5D5 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 7AF58D332402A91C00CE01A0 /* Kingfisher */; }; 7AA7BC3525A5DFB80053A5D5 /* ExceptionCatcher in Frameworks */ = {isa = PBXBuildFile; productRef = 7A813DC02508C4D900CC93B9 /* ExceptionCatcher */; }; 7AA7BC3625A5DFB80053A5D5 /* PKHUD in Frameworks */ = {isa = PBXBuildFile; productRef = 7AABDE1C2532F3EB0041AFC6 /* PKHUD */; }; 7AAAFAD32C4D0FD00050410D /* ACImageSliderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AAAFAD22C4D0FD00050410D /* ACImageSliderView.swift */; }; @@ -289,6 +290,9 @@ 7A11474623FF2AA500B424AF /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; 7A11474823FF2B2D00B424AF /* Response.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Response.swift; sourceTree = ""; }; 7A11474D23FFEE8800B424AF /* SVProgressHUD.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SVProgressHUD.framework; path = Carthage/Build/iOS/SVProgressHUD.framework; sourceTree = ""; }; + 7A131FD22D37B75500DC7755 /* HistoryScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryScreen.swift; sourceTree = ""; }; + 7A131FD42D37B76A00DC7755 /* HistoryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryViewModel.swift; sourceTree = ""; }; + 7A131FD62D37B77E00DC7755 /* HistoryCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryCoordinator.swift; sourceTree = ""; }; 7A1441652C297EDE00E79018 /* NotesScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotesScreen.swift; sourceTree = ""; }; 7A1441672C297EFD00E79018 /* NotesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotesViewModel.swift; sourceTree = ""; }; 7A14416B2C297F2100E79018 /* NotesCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotesCoordinator.swift; sourceTree = ""; }; @@ -387,14 +391,13 @@ 7A71580D2C4445A200852088 /* AdsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdsCoordinator.swift; sourceTree = ""; }; 7A7158112C444A6400852088 /* AdsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdsViewModel.swift; sourceTree = ""; }; 7A71EF562D0A26B200943129 /* EventModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventModel.swift; sourceTree = ""; }; - 7A7547DF24032CB6004E8406 /* VehiclePhotoCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehiclePhotoCell.swift; sourceTree = ""; }; 7A761C0A267E8FF90005F28F /* Error.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Error.swift; sourceTree = ""; }; 7A813DBD2506A57100CC93B9 /* AuthenticationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AuthenticationServices.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/AuthenticationServices.framework; sourceTree = DEVELOPER_DIR; }; 7A813DC22508EE4F00CC93B9 /* EventCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventCell.swift; sourceTree = ""; }; 7A8A2208248D10EC0073DFD9 /* ResizeImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResizeImage.swift; sourceTree = ""; }; - 7A8A220A248D67B60073DFD9 /* VehicleReportImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleReportImage.swift; sourceTree = ""; }; 7A8AB76425A0DB8F00ECF2C1 /* BundleVersion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BundleVersion.swift; sourceTree = ""; }; 7A8AB76725A0DC8200ECF2C1 /* DebugInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugInfo.swift; sourceTree = ""; }; + 7A912F362D381B7400002938 /* LicensePlateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LicensePlateView.swift; sourceTree = ""; }; 7A91894E29A2BD8700519C74 /* GestureRecognizers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GestureRecognizers.swift; sourceTree = ""; }; 7A92D0AB240425B100EF3B77 /* ATGMediaBrowser.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ATGMediaBrowser.framework; path = Carthage/Build/iOS/ATGMediaBrowser.framework; sourceTree = ""; }; 7A961C6B2C4C3C8600CE2211 /* TextRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRowView.swift; sourceTree = ""; }; @@ -468,7 +471,6 @@ buildActionMask = 2147483647; files = ( 7AA7BC3525A5DFB80053A5D5 /* ExceptionCatcher in Frameworks */, - 7AA7BC3325A5DFB80053A5D5 /* Kingfisher in Frameworks */, 7ADF23062C25B5BF002624FF /* RealmSwift in Frameworks */, 7AA7BC3625A5DFB80053A5D5 /* PKHUD in Frameworks */, 7AC3554A2969652F00889457 /* SwiftEntryKit in Frameworks */, @@ -670,9 +672,20 @@ name = Frameworks; sourceTree = ""; }; + 7A131FD12D37B74100DC7755 /* HistoryScreen */ = { + isa = PBXGroup; + children = ( + 7A131FD22D37B75500DC7755 /* HistoryScreen.swift */, + 7A131FD42D37B76A00DC7755 /* HistoryViewModel.swift */, + 7A131FD62D37B77E00DC7755 /* HistoryCoordinator.swift */, + ); + path = HistoryScreen; + sourceTree = ""; + }; 7A1441632C297E9800E79018 /* Screens */ = { isa = PBXGroup; children = ( + 7A131FD12D37B74100DC7755 /* HistoryScreen */, 7AB9FE202D08C28E005DE374 /* EventsScreen */, 7ABD1B452D044A0900B43213 /* GalleryScreen */, 7A1E78F42CE9001A0004B740 /* ReportScreen */, @@ -729,7 +742,6 @@ 7A8A2208248D10EC0073DFD9 /* ResizeImage.swift */, 7A659B5A24A3768A0043A0F2 /* Substrings.swift */, 7AE26A3224EEF9EC00625033 /* UIViewControllerExt.swift */, - 7A8A220A248D67B60073DFD9 /* VehicleReportImage.swift */, 7A761C0A267E8FF90005F28F /* Error.swift */, 7AC76D7A270083AE0084DB27 /* TextView.swift */, ); @@ -761,7 +773,6 @@ isa = PBXGroup; children = ( 7A530B7D24017FEE00CBFE6E /* VehicleCell.swift */, - 7A7547DF24032CB6004E8406 /* VehiclePhotoCell.swift */, 7A1090E724A394F100B4F0B2 /* AudioRecordCell.swift */, 7A813DC22508EE4F00CC93B9 /* EventCell.swift */, 7AEFC3BD2529D3CC00BADFB2 /* ConfigurableCell.swift */, @@ -1070,6 +1081,7 @@ 7A2E11282CCE395300E5CA17 /* OptionalDatePicker.swift */, 7A4927D42CCE438600851C01 /* OptionalBinding.swift */, 7AABBE3A2CF9F85600346588 /* Binding+Map.swift */, + 7A912F362D381B7400002938 /* LicensePlateView.swift */, ); path = SwiftUI; sourceTree = ""; @@ -1115,7 +1127,6 @@ ); name = AutoCat; packageProductDependencies = ( - 7AF58D332402A91C00CE01A0 /* Kingfisher */, 7A813DC02508C4D900CC93B9 /* ExceptionCatcher */, 7AABDE1C2532F3EB0041AFC6 /* PKHUD */, 7AC355492969652F00889457 /* SwiftEntryKit */, @@ -1236,7 +1247,6 @@ ); mainGroup = 7A1146F423FDE7E500B424AF; packageReferences = ( - 7AF58D322402A91C00CE01A0 /* XCRemoteSwiftPackageReference "Kingfisher" */, 7A05160F241412CA00FC55AC /* XCRemoteSwiftPackageReference "SwiftDate" */, 7A813DBF2508C4D900CC93B9 /* XCRemoteSwiftPackageReference "ExceptionCatcher" */, 7AABDE1B2532F3EB0041AFC6 /* XCRemoteSwiftPackageReference "PKHUD" */, @@ -1354,6 +1364,7 @@ 7A7158072C44085600852088 /* OsagoScreen.swift in Sources */, 7ABD1B492D044A4700B43213 /* GalleryViewModel.swift in Sources */, 7AAAFAD32C4D0FD00050410D /* ACImageSliderView.swift in Sources */, + 7A912F372D381B7400002938 /* LicensePlateView.swift in Sources */, 7A3F07AB24360DC800E59687 /* Dated.swift in Sources */, 7AC76D7B270083AE0084DB27 /* TextView.swift in Sources */, 7A1090E824A394F100B4F0B2 /* AudioRecordCell.swift in Sources */, @@ -1387,11 +1398,11 @@ 7AB67E8E2435D1A000258F61 /* CustomButton.swift in Sources */, 7AC35554296973E100889457 /* ACButton.swift in Sources */, 7AAAFADC2C4D1E130050410D /* ACImageSliderView+Modifier.swift in Sources */, - 7A8A220B248D67B60073DFD9 /* VehicleReportImage.swift in Sources */, 7AFBE8C42C302561003C491D /* ACHudContainer.swift in Sources */, 7AC3555B296995B200889457 /* UIEdgeInsets.swift in Sources */, 7A06E0AC2C7065AC005731AC /* SettingsScreen.swift in Sources */, 7A4322952CB2CD0F00085CF6 /* FiltersCoordinator.swift in Sources */, + 7A131FD72D37B77E00DC7755 /* HistoryCoordinator.swift in Sources */, 7A7158002C43EA6900852088 /* OwnersScreen.swift in Sources */, 7A1441702C2998B200E79018 /* Formatters.swift in Sources */, 7A4322912CB2CC8A00085CF6 /* FiltersScreen.swift in Sources */, @@ -1401,6 +1412,7 @@ 7A06E0B02C7065D8005731AC /* SettingsCoordinator.swift in Sources */, 7A91894F29A2BD8700519C74 /* GestureRecognizers.swift in Sources */, 7AFBE8CC2C3085C6003C491D /* ACProgressView.swift in Sources */, + 7A131FD32D37B75500DC7755 /* HistoryScreen.swift in Sources */, 7AB9FE222D08C2A5005DE374 /* EventsScreen.swift in Sources */, 7ADF6C93250B954900F237B2 /* Navigation.swift in Sources */, 7A64AE752469DFB600ABE48E /* MediaBrowserViewController.swift in Sources */, @@ -1409,8 +1421,8 @@ 7ADF6C97250F41B000F237B2 /* PNKeyboard.swift in Sources */, 7A1022702C551EFD00B84627 /* LocationEditCoordinator.swift in Sources */, 7A7158042C43EAA200852088 /* OwnersCoordinator.swift in Sources */, - 7A7547E024032CB6004E8406 /* VehiclePhotoCell.swift in Sources */, 7A17CE4C2A2E850200626A6E /* UISegmentedControl.swift in Sources */, + 7A131FD52D37B76A00DC7755 /* HistoryViewModel.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2013,14 +2025,6 @@ minimumVersion = 0.0.11; }; }; - 7AF58D322402A91C00CE01A0 /* XCRemoteSwiftPackageReference "Kingfisher" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/onevcat/Kingfisher"; - requirement = { - branch = "8.0.0-alpha.1"; - kind = branch; - }; - }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -2074,11 +2078,6 @@ package = 7A1CF7FD29A41C2F007962DA /* XCRemoteSwiftPackageReference "realm-swift" */; productName = RealmSwift; }; - 7AF58D332402A91C00CE01A0 /* Kingfisher */ = { - isa = XCSwiftPackageProductDependency; - package = 7AF58D322402A91C00CE01A0 /* XCRemoteSwiftPackageReference "Kingfisher" */; - productName = Kingfisher; - }; 7AF8606B2CB9B20C00954D2F /* Mockable */ = { isa = XCSwiftPackageProductDependency; package = 7ACBB91C2CB9B155005A5168 /* XCRemoteSwiftPackageReference "Mockable" */; diff --git a/AutoCat.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/AutoCat.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 81045db..0c5c026 100644 --- a/AutoCat.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/AutoCat.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "d6bab97967371dd248cd5b3fdb49293879398c897bea34714c48fd3d4cb90fb1", + "originHash" : "6fccb9fdc0d29647d4f0b927aef60f375302d72b5b724992eab52ac0d8ec71c3", "pins" : [ { "identity" : "exceptioncatcher", @@ -10,15 +10,6 @@ "version" : "1.1.0" } }, - { - "identity" : "kingfisher", - "kind" : "remoteSourceControl", - "location" : "https://github.com/onevcat/Kingfisher", - "state" : { - "branch" : "8.0.0-alpha.1", - "revision" : "bb4e6ecf6c7a221dfc51c8e69f04fd3757fc519a" - } - }, { "identity" : "mockable", "kind" : "remoteSourceControl", diff --git a/AutoCat/AppDelegate.swift b/AutoCat/AppDelegate.swift index 9c2ed47..c4847c9 100644 --- a/AutoCat/AppDelegate.swift +++ b/AutoCat/AppDelegate.swift @@ -2,8 +2,6 @@ import UIKit import RealmSwift import PKHUD import AutoCatCore -import SwiftLocation -import CoreLocation enum QuickAction { case none @@ -72,11 +70,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { HUD.dimsBackground = true HUD.allowsInteraction = false - registerServices() setupAppearance() - //Logging.URLRequests = { _ in false }; - return true } @@ -92,29 +87,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { UINavigationBar.appearance().standardAppearance = navigationBarAppearance UINavigationBar.appearance().scrollEdgeAppearance = navigationBarAppearance } - - func registerServices() { - - let container = ServiceContainer.shared - - let settingsService = SettingsService(defaults: .standard) - - container.register(SettingsServiceProtocol.self, instance: settingsService) - container.register(ApiServiceProtocol.self, instance: ApiService()) - - let locationService = LocationService( - geocoder: CLGeocoder(), - locationManager: Location(), - settingsService: settingsService - ) - - container.register(LocationServiceProtocol.self, instance: locationService) - - Task { - container.register(StorageServiceProtocol.self, - instance: try await StorageService(settingsService: settingsService)) - } - } // MARK: UISceneSession Lifecycle diff --git a/AutoCat/Cells/VehiclePhotoCell.swift b/AutoCat/Cells/VehiclePhotoCell.swift deleted file mode 100644 index 7ce845d..0000000 --- a/AutoCat/Cells/VehiclePhotoCell.swift +++ /dev/null @@ -1,38 +0,0 @@ -import UIKit -import Kingfisher -import AutoCatCore - -@MainActor -class VehiclePhotoCell: UICollectionViewCell { - @IBOutlet weak var photo: UIImageView! - @IBOutlet weak var model: UILabel! - @IBOutlet weak var date: UILabel! - - let formatter = DateFormatter() - - override func awakeFromNib() { - super.awakeFromNib() - - DispatchQueue.main.async { - self.layer.cornerRadius = 8 - self.formatter.timeStyle = .none - self.formatter.dateStyle = .medium - } - } - - override func prepareForReuse() { - super.prepareForReuse() - self.photo.kf.cancelDownloadTask() - } - - func configure(with photoModel: VehiclePhoto) { - if let url = URL(string: photoModel.url) { - self.photo.kf.setImage(with: url) - } - - self.model.text = "\(photoModel.brand ?? "") \(photoModel.model ?? "")" - - let date = Date(timeIntervalSince1970: photoModel.date/1000) - self.date.text = formatter.string(from: date) - } -} diff --git a/AutoCat/Controllers/MainTabController.swift b/AutoCat/Controllers/MainTabController.swift index bd6b5d6..c3a811b 100644 --- a/AutoCat/Controllers/MainTabController.swift +++ b/AutoCat/Controllers/MainTabController.swift @@ -23,9 +23,19 @@ class MainTabController: UITabBarController, UITabBarControllerDelegate { traitOverrides.horizontalSizeClass = .compact } + setupHistoryTab() Task { await addSettings() } } + func setupHistoryTab() { + + let coordinator = HistoryCoordinator() + let controller = coordinator.start() + controller.tabBarItem = UITabBarItem(title: NSLocalizedString("History", comment: ""), + image: UIImage(systemName: "clock.arrow.circlepath"), tag: 0) + viewControllers?[0] = controller + } + func addSettings() async { let coordinator = SettingsCoordinator(tabController: self) diff --git a/AutoCat/Extensions/VehicleReportImage.swift b/AutoCat/Extensions/VehicleReportImage.swift deleted file mode 100644 index f51fd17..0000000 --- a/AutoCat/Extensions/VehicleReportImage.swift +++ /dev/null @@ -1,280 +0,0 @@ -import UIKit -import Kingfisher -import AutoCatCore - -extension VehicleDto { - - @MainActor - func drawLine(y: CGFloat, width: CGFloat, margin: CGFloat = 15,context: CGContext) { - let lineWidth = 1/UIScreen.main.scale - context.move(to: CGPoint(x: margin, y: y + lineWidth/2)) - context.addLine(to: CGPoint(x: width, y: y + lineWidth/2)) - context.closePath() - context.setLineWidth(lineWidth) - context.setStrokeColor(UIColor.opaqueSeparator.cgColor) - context.strokePath() - } - - @MainActor - func drawCell(y: CGFloat, width: CGFloat, height: CGFloat, title: String, value: String, lineMargin: CGFloat = 15, context: CGContext) { - let fontSize: CGFloat = 17 - let font = UIFont.systemFont(ofSize: fontSize) - let offest = (height - fontSize)/2 - let pStyle = NSMutableParagraphStyle() - pStyle.alignment = .right - let attributes: [NSAttributedString.Key: Any] = [.font: font, .foregroundColor: UIColor.label] - let valueAttributs: [NSAttributedString.Key: Any] = [.font: font, .foregroundColor: UIColor.secondaryLabel, .paragraphStyle: pStyle] - let rect = CGRect(x: 15, y: y + offest, width: width - 30, height: height) - UIColor.secondarySystemGroupedBackground.setFill() - UIRectFill(CGRect(x: 0, y: y, width: width, height: height)) - title.draw(with: rect, options: .usesLineFragmentOrigin, attributes: attributes, context: nil) - value.draw(with: rect, options: .usesLineFragmentOrigin, attributes: valueAttributs, context: nil) - self.drawLine(y: y + height - 1/UIScreen.main.scale, width: width, margin: lineMargin, context: context) - } - - @MainActor - func drawBigTextCell(y: CGFloat, width: CGFloat, title: String, value: String, lineMargin: CGFloat = 15, context: CGContext) -> CGFloat { - let pStyle = NSMutableParagraphStyle() - pStyle.alignment = .right - let attributes: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: 17), .foregroundColor: UIColor.label] - let valueAttributs: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: 15), .foregroundColor: UIColor.secondaryLabel, .paragraphStyle: pStyle] - UIColor.secondarySystemGroupedBackground.setFill() - - let text = NSMutableAttributedString(string: title + " - " + value) - text.setAttributes(attributes, range: NSRange(location: 0, length: title.count)) - text.setAttributes(valueAttributs, range: NSRange(location: title.count, length: value.count + 3)) - let textRect = text.boundingRect(with: CGSize(width: width - 30, height: 1000), options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil) - let height = textRect.size.height > 28 ? textRect.size.height + 16 : 44 - let offset = (height - textRect.size.height)/2 - let rect = CGRect(x: 15, y: y + offset, width: width - 30, height: height) - UIRectFill(CGRect(x: 0, y: y, width: width, height: height)) - text.draw(with: rect, options: .usesLineFragmentOrigin, context: nil) - self.drawLine(y: y + height - 1/UIScreen.main.scale, width: width, margin: lineMargin, context: context) - - return height - } - - @MainActor - func reportImage(width: CGFloat) -> UIImage { - var realHeight: CGFloat = 0 - let rect = CGRect(origin: .zero, size: CGSize(width: width, height: CGFloat(10000))) - let renderer = UIGraphicsImageRenderer(bounds: rect) - let image = renderer.image { rendererContext in - let cellHeight: CGFloat = 44 - let ctx = rendererContext.cgContext - let centeredStyle = NSMutableParagraphStyle() - centeredStyle.alignment = .center - let titleAttributes: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: 20), .paragraphStyle: centeredStyle, .foregroundColor: UIColor.label] - let headerAttributes: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: 13), .foregroundColor: UIColor.secondaryLabel] - - let w: CGFloat = width - var y: CGFloat = 0 - - UIColor.systemGroupedBackground.setFill() - UIRectFill(rect) - - y += 12 - "\(self.brand?.name?.original ?? "Unknown model") (\(self.getNumber()))".draw(with: CGRect(x: 0, y: y, width: w, height: 30), options: .usesLineFragmentOrigin, attributes: titleAttributes, context: nil) - y += 50 - "GENERAL".draw(with: CGRect(x: 15, y: y, width: w - 15, height: 24), options: .usesLineFragmentOrigin, attributes: headerAttributes, context: nil) - y += 24 - self.drawLine(y: y, width: w, margin: 0, context: ctx) - y += 1/UIScreen.main.scale - self.drawCell(y: y, width: w, height: cellHeight, title: "Year", value: String(self.year), context: ctx) - y += cellHeight - self.drawCell(y: y, width: w, height: cellHeight, title: "Color", value: self.color ?? "", context: ctx) - y += cellHeight - self.drawCell(y: y, width: w, height: cellHeight, title: "Category", value: self.category ?? "", context: ctx) - y += cellHeight - var position = "Unknown" - if let rightWheel = self.isRightWheel { - position = rightWheel ? "Right" : "Left" - } - self.drawCell(y: y, width: w, height: cellHeight, title: "Steering wheel position", value: position, context: ctx) - y += cellHeight - var japanese = "" - if let isJapanese = self.isJapanese { - japanese = isJapanese ? "Yes" : "No" - } - self.drawCell(y: y, width: w, height: cellHeight, title: "Japanese", value: japanese, lineMargin: 0, context: ctx) - y += cellHeight + 32 - - "IDENTIFIERS".draw(with: CGRect(x: 15, y: y, width: w - 15, height: 24), options: .usesLineFragmentOrigin, attributes: headerAttributes, context: nil) - y += 24 - self.drawLine(y: y, width: w, margin: 0, context: ctx) - y += 1/UIScreen.main.scale - self.drawCell(y: y, width: w, height: cellHeight, title: "Plate number", value: self.getNumber(), context: ctx) - y += cellHeight - self.drawCell(y: y, width: w, height: cellHeight, title: "VIN", value: self.vin1 ?? "", context: ctx) - y += cellHeight - self.drawCell(y: y, width: w, height: cellHeight, title: "STS", value: self.sts ?? "", context: ctx) - y += cellHeight - self.drawCell(y: y, width: w, height: cellHeight, title: "PTS", value: self.pts ?? "", context: ctx) - y += cellHeight + 32 - - "ENGINE".draw(with: CGRect(x: 15, y: y, width: w - 15, height: 24), options: .usesLineFragmentOrigin, attributes: headerAttributes, context: nil) - y += 24 - self.drawLine(y: y, width: w, margin: 0, context: ctx) - y += 1/UIScreen.main.scale - self.drawCell(y: y, width: w, height: cellHeight, title: "Number", value: self.engine?.number ?? "", context: ctx) - y += cellHeight - self.drawCell(y: y, width: w, height: cellHeight, title: "Fuel type", value: self.engine?.fuelType ?? "", context: ctx) - y += cellHeight - self.drawCell(y: y, width: w, height: cellHeight, title: "Volume (cm³)", value: String(self.engine?.volume ?? 0), context: ctx) - y += cellHeight - self.drawCell(y: y, width: w, height: cellHeight, title: "Power (HP)", value: String(self.engine?.powerHp ?? 0), context: ctx) - y += cellHeight - self.drawCell(y: y, width: w, height: cellHeight, title: "Power (kw)", value: String(self.engine?.powerKw ?? 0), context: ctx) - y += cellHeight + 32 - - "OWNERSHIP PERIODS (\(self.ownershipPeriods.count))".draw(with: CGRect(x: 15, y: y, width: w - 15, height: 24), options: .usesLineFragmentOrigin, attributes: headerAttributes, context: nil) - y += 24 - self.drawLine(y: y, width: w, margin: 0, context: ctx) - y += 1/UIScreen.main.scale - - let formatter = DateFormatter() - formatter.dateStyle = .long - formatter.timeStyle = .none - for period in self.ownershipPeriods { - self.drawCell(y: y, width: w, height: cellHeight, title: "Owner type", value: period.ownerType, context: ctx) - y += cellHeight - - let fromDate = Date(timeIntervalSince1970: TimeInterval(period.from/1000)) - self.drawCell(y: y, width: w, height: cellHeight, title: "From", value: formatter.string(from: fromDate), context: ctx) - y += cellHeight - - let toDate = Date(timeIntervalSince1970: TimeInterval(period.to/1000)) - let toString = period.to == 0 ? "now" : formatter.string(from: toDate) - self.drawCell(y: y, width: w, height: cellHeight, title: "To", value: toString, context: ctx) - y += cellHeight - - let height = self.drawBigTextCell(y: y, width: w, title: "Last operation", value: period.lastOperation, lineMargin: 0, context: ctx) - y += height + 8 - } - - y += 24 - - if !self.events.isEmpty { - "LOCATIONS".draw(with: CGRect(x: 15, y: y, width: w - 15, height: 24), options: .usesLineFragmentOrigin, attributes: headerAttributes, context: nil) - y += 24 - self.drawLine(y: y, width: w, margin: 0, context: ctx) - y += 1/UIScreen.main.scale - - for event in self.events { - let date = Date(timeIntervalSince1970: event.date) - self.drawCell(y: y, width: w, height: cellHeight, title: "Date", value: formatter.string(from: date), context: ctx) - y += cellHeight - - if let address = event.address { - self.drawCell(y: y, width: w, height: cellHeight, title: "Address", value: address, context: ctx) - y += cellHeight - } - - let location = String(format: "%.05f, %.05f", event.latitude, event.longitude) - self.drawCell(y: y, width: w, height: cellHeight, title: "Location", value: location, context: ctx) - y += cellHeight + 8 - } - } - - y += 24 - - "PHOTOS (\(self.photos.count))".draw(with: CGRect(x: 15, y: y, width: w - 15, height: 24), options: .usesLineFragmentOrigin, attributes: headerAttributes, context: nil) - y += 24 - self.drawLine(y: y, width: w, margin: 0, context: ctx) - y += 1/UIScreen.main.scale - - // TODO: Fix drawing photos in report image -// for photo in self.photos { -// let date = Date(timeIntervalSince1970: TimeInterval(photo.date/1000)) -// var name = "" -// if let brand = photo.brand, let model = photo.model { -// name = "\(brand) \(model)" -// } -// -// if let url = URL(string: photo.url) { -// if let image = ImageCache.default.retrieveImageInDiskCache(forKey: url.cacheKey) { -// let imgHeight = image.size.height*w/image.size.width -// let rect = CGRect(x: 0, y: y, width: w, height: imgHeight) -// image.draw(in: rect) -// y += imgHeight -// } -// } -// -// self.drawCell(y: y, width: w, height: cellHeight, title: name, value: formatter.string(from: date), lineMargin: 0, context: ctx) -// y += cellHeight -// -// y += 16 -// } - - realHeight = y - } - - return image.cutHeight(to: realHeight) - } - - func reportText() -> String { - let formatter = DateFormatter() - formatter.dateStyle = .long - formatter.timeStyle = .none - - var text = (self.brand?.name?.original ?? "") + "\n" - text += "Year: \(self.year)\n" - if let color = self.color { text += "Color: \(color)\n" } - if let category = self.category { text += "Category: \(category)\n" } - var position = "Unknown" - if let rightWheel = self.isRightWheel { - position = rightWheel ? "Right" : "Left" - } - var japanese = "" - if let isJapanese = self.isJapanese { - japanese = isJapanese ? "Yes" : "No" - } - text += "Steering wheel position: \(position)\n" - text += "Japanese: \(japanese)\n" - text += "Plate number: \(self.getNumber())\n" - if let vin = self.vin1 { text += "VIN: \(vin)\n" } - if let sts = self.sts { text += "STS: \(sts)\n" } - if let pts = self.pts { text += "PTS: \(pts)\n" } - if let engineNumber = self.engine?.number { text += "Engine number: \(engineNumber)\n" } - if let fuel = self.engine?.fuelType { text += "Fuel type: \(fuel)\n" } - if let volume = self.engine?.volume { text += "Volume (cm³): \(volume)\n" } - if let powerHp = self.engine?.powerHp { text += "Power (HP): \(powerHp)\n" } - if let powerHp = self.engine?.powerHp { text += "Power (HP): \(powerHp)\n" } - - if self.ownershipPeriods.count > 0 { - text += "\n" - text += "Ownership periods: \(self.ownershipPeriods.count)\n" - text += "\n" - for period in self.ownershipPeriods { - text += "Owner type: \(period.ownerType)\n" - let fromDate = Date(timeIntervalSince1970: TimeInterval(period.from/1000)) - text += "From: \(formatter.string(from: fromDate))\n" - let toDate = Date(timeIntervalSince1970: TimeInterval(period.to/1000)) - let toString = period.to == 0 ? "now" : formatter.string(from: toDate) - text += "To: \(toString)\n" - text += "Last operation: \(period.lastOperation)\n" - text += "\n" - } - } - - if !self.events.isEmpty { - text += "\n" - text += "Locations\n" - text += "\n" - for event in self.events { - let date = Date(timeIntervalSince1970: event.date) - text += "Date: \(formatter.string(from: date))\n" - - if let address = event.address { - text += "Address: \(address)\n" - } - - let location = String(format: "%.05f, %.05f", event.latitude, event.longitude) - text += "Location: \(location)\n" - text += "\n" - } - } - - return text - } -} diff --git a/AutoCat/SceneDelegate.swift b/AutoCat/SceneDelegate.swift index 2ddf75a..3d8b0be 100644 --- a/AutoCat/SceneDelegate.swift +++ b/AutoCat/SceneDelegate.swift @@ -3,10 +3,10 @@ import os.log import AVFoundation import PKHUD import AutoCatCore +import SwiftLocation +import CoreLocation class SceneDelegate: UIResponder, UIWindowSceneDelegate { - - @Service var settingsService: SettingsServiceProtocol var window: UIWindow? @@ -14,7 +14,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). - guard let windowScene = (scene as? UIWindowScene) else { return } guard let ad = UIApplication.shared.delegate as? AppDelegate else { return } if let activity = connectionOptions.userActivities.first { @@ -31,9 +30,43 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } } + Task { + try? await registerServices() + setupRootController(scene: scene) + } + } + + func registerServices() async throws { + + let container = ServiceContainer.shared + + let settingsService = SettingsService(defaults: .standard) + + container.register(SettingsServiceProtocol.self, instance: settingsService) + container.register(ApiServiceProtocol.self, instance: ApiService()) + + let locationService = LocationService( + geocoder: CLGeocoder(), + locationManager: Location(), + settingsService: settingsService + ) + + container.register(LocationServiceProtocol.self, instance: locationService) + + container.register(StorageServiceProtocol.self, + instance: try await StorageService(settingsService: settingsService)) + } + + func setupRootController(scene: UIScene) { + guard let windowScene = (scene as? UIWindowScene) else { + return + } + self.window = UIWindow(windowScene: windowScene) let storyboard = UIStoryboard(name: "Main", bundle: nil) + let settingsService = ServiceContainer.shared.resolve(SettingsServiceProtocol.self) + if settingsService.user.token.isEmpty { self.window?.rootViewController = storyboard.instantiateViewController(identifier: "AuthController") } else { diff --git a/AutoCat/Screens/HistoryScreen/HistoryCoordinator.swift b/AutoCat/Screens/HistoryScreen/HistoryCoordinator.swift new file mode 100644 index 0000000..63e93fd --- /dev/null +++ b/AutoCat/Screens/HistoryScreen/HistoryCoordinator.swift @@ -0,0 +1,34 @@ +// +// HistoryCoordinator.swift +// AutoCat +// +// Created by Selim Mustafaev on 15.01.2025. +// Copyright © 2025 Selim Mustafaev. All rights reserved. +// + +import UIKit +import SwiftUI +import AutoCatCore + +@MainActor +class HistoryCoordinator { + + var navController: UINavigationController? + + func start() -> UIViewController { + + let resolver = ServiceContainer.shared + let viewModel = HistoryViewModel( + apiService: resolver.resolve(ApiServiceProtocol.self), + storageService: resolver.resolve(StorageServiceProtocol.self) + ) + + let view = HistoryScreen(viewModel: viewModel) + let controller = UIHostingController(rootView: view) + + let navController = UINavigationController(rootViewController: controller) + self.navController = navController + + return navController + } +} diff --git a/AutoCat/Screens/HistoryScreen/HistoryScreen.swift b/AutoCat/Screens/HistoryScreen/HistoryScreen.swift new file mode 100644 index 0000000..45a3cda --- /dev/null +++ b/AutoCat/Screens/HistoryScreen/HistoryScreen.swift @@ -0,0 +1,28 @@ +// +// HistoryScreen.swift +// AutoCat +// +// Created by Selim Mustafaev on 15.01.2025. +// Copyright © 2025 Selim Mustafaev. All rights reserved. +// + +import SwiftUI +import AutoCatCore + +struct HistoryScreen: View { + + @State var viewModel: HistoryViewModel + + var body: some View { + List(viewModel.vehicles) { vehicle in + LicensePlateView(number: PlateNumber(vehicle.getNumber())) + .frame(width: 200) + } + .listStyle(.plain) + .navigationTitle(String.localizedStringWithFormat(NSLocalizedString("vehicles found", comment: ""), viewModel.vehicles.count)) + } +} + +//#Preview { +// HistoryScreen(viewModel: .init()) +//} diff --git a/AutoCat/Screens/HistoryScreen/HistoryViewModel.swift b/AutoCat/Screens/HistoryScreen/HistoryViewModel.swift new file mode 100644 index 0000000..5c3c932 --- /dev/null +++ b/AutoCat/Screens/HistoryScreen/HistoryViewModel.swift @@ -0,0 +1,33 @@ +// +// HistoryViewModel.swift +// AutoCat +// +// Created by Selim Mustafaev on 15.01.2025. +// Copyright © 2025 Selim Mustafaev. All rights reserved. +// + +import SwiftUI +import AutoCatCore + +@MainActor +@Observable +final class HistoryViewModel { + + let apiService: ApiServiceProtocol + let storageService: StorageServiceProtocol + + var vehicles: [VehicleDto] = [] + + init(apiService: ApiServiceProtocol, storageService: StorageServiceProtocol) { + + self.apiService = apiService + self.storageService = storageService + + Task { await loadVehicles() } + } + + func loadVehicles() async { + + vehicles = await storageService.loadVehicles() + } +} diff --git a/AutoCat/SwiftUI/LicensePlateView.swift b/AutoCat/SwiftUI/LicensePlateView.swift new file mode 100644 index 0000000..5908817 --- /dev/null +++ b/AutoCat/SwiftUI/LicensePlateView.swift @@ -0,0 +1,73 @@ +// +// LicensePlateView.swift +// AutoCat +// +// Created by Selim Mustafaev on 15.01.2025. +// Copyright © 2025 Selim Mustafaev. All rights reserved. +// + +import SwiftUI +import AutoCatCore + +struct LicensePlateView: View { + + private static let aspectRatio: CGFloat = 520.0/112.0 + private static let fontHeightCoeff: CGFloat = 58.0/76.0 + private static let borderMultiplier = 0.01 + private static let numberPartMultiplier = (1 - 3*LicensePlateView.borderMultiplier)*0.73 + private static let reguinPartMultiplier = (1 - 3*LicensePlateView.borderMultiplier)*0.27 + private static let outerCornerMultiplier = 0.03 + private static let innerCornerMultiplier = outerCornerMultiplier - borderMultiplier + + let number: PlateNumber + + var body: some View { + GeometryReader { geometry in + ZStack { + RoundedRectangle(cornerRadius: geometry.size.width*LicensePlateView.outerCornerMultiplier) + .fill(Color("PlateForeground")) + HStack(spacing: 0) { + ZStack { + RoundedRectangle(cornerRadius: geometry.size.width*LicensePlateView.innerCornerMultiplier) + .fill(Color("PlateBackground")) + Text(number.mainPart()) + .font(.custom("RoadNumbers", size: geometry.size.height)) + .kerning(3) + .offset(y: geometry.size.width*0.015) + } + .frame(width: geometry.size.width*LicensePlateView.numberPartMultiplier) + .padding(geometry.size.width*LicensePlateView.borderMultiplier) + + ZStack { + RoundedRectangle(cornerRadius: geometry.size.width*LicensePlateView.innerCornerMultiplier) + .fill(Color("PlateBackground")) + VStack(spacing: 0) { + Text(number.region()) + .font(.custom("RoadNumbers", size: geometry.size.height*0.7)) + HStack(spacing: 2) { + Text("RUS") + .font(.system(size: geometry.size.height*0.3)) + VStack(spacing: 0) { + Rectangle().fill(.white) + Rectangle().fill(.blue) + Rectangle().fill(.red) + } + .aspectRatio(1.5, contentMode: .fit) + .frame(width: geometry.size.width*0.08) + } + } + .offset(y: geometry.size.width*0.015) + } + .frame(width: geometry.size.width*LicensePlateView.reguinPartMultiplier) + .padding(.vertical, geometry.size.width*LicensePlateView.borderMultiplier) + .padding(.trailing, geometry.size.width*LicensePlateView.borderMultiplier) + } + } + } + .aspectRatio(LicensePlateView.aspectRatio, contentMode: .fit) + } +} + +#Preview { + LicensePlateView(number: PlateNumber("А123АА761")) +} diff --git a/AutoCatCore/Services/StorageService/StorageService.swift b/AutoCatCore/Services/StorageService/StorageService.swift index c9a0adb..48af223 100644 --- a/AutoCatCore/Services/StorageService/StorageService.swift +++ b/AutoCatCore/Services/StorageService/StorageService.swift @@ -56,4 +56,9 @@ public actor StorageService: StorageServiceProtocol { throw StorageError.vehicleNotFound } } + + public func loadVehicles() async -> [VehicleDto] { + + realm.objects(Vehicle.self).map(\.shallowDto) + } } diff --git a/AutoCatCore/Services/StorageService/StorageServiceProtocol.swift b/AutoCatCore/Services/StorageService/StorageServiceProtocol.swift index 0a08ac0..d0c93bd 100644 --- a/AutoCatCore/Services/StorageService/StorageServiceProtocol.swift +++ b/AutoCatCore/Services/StorageService/StorageServiceProtocol.swift @@ -11,11 +11,17 @@ import Mockable @Mockable public protocol StorageServiceProtocol: Sendable { + // Vehicles + func loadVehicles() async -> [VehicleDto] + func loadVehicle(number: String) async throws -> VehicleDto + func updateVehicleIfExists(dto: VehicleDto) async throws + + // Notes func addNote(text: String, to number: String) async throws -> VehicleDto func deleteNote(id: String, for number: String) async throws -> VehicleDto func editNote(id: String, text: String, for number: String) async throws -> VehicleDto - func updateVehicleIfExists(dto: VehicleDto) async throws - func loadVehicle(number: String) async throws -> VehicleDto + + // Events func add(event: VehicleEventDto, to number: String) async throws -> VehicleDto func remove(event id: String, from number: String) async throws -> VehicleDto func edit(event: VehicleEventDto, for number: String) async throws -> VehicleDto