diff --git a/AutoCat.xcodeproj/project.pbxproj b/AutoCat.xcodeproj/project.pbxproj index c575daa..0a54cb9 100644 --- a/AutoCat.xcodeproj/project.pbxproj +++ b/AutoCat.xcodeproj/project.pbxproj @@ -8,6 +8,14 @@ /* Begin PBXBuildFile section */ 6841A85D4B60DB71D1E68DA0 /* ImageGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6841AC687EA6293A0757678C /* ImageGrid.swift */; }; + 7A1022692C55197D00B84627 /* RealmSwift in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 7ADF23052C25B5BF002624FF /* RealmSwift */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 7A10226C2C551EC500B84627 /* LocationEditScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A10226B2C551EC500B84627 /* LocationEditScreen.swift */; }; + 7A10226E2C551EE000B84627 /* LocationEditViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A10226D2C551EE000B84627 /* LocationEditViewModel.swift */; }; + 7A1022702C551EFD00B84627 /* LocationEditCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A10226F2C551EFD00B84627 /* LocationEditCoordinator.swift */; }; + 7A1022722C554A1300B84627 /* CustomHostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1022712C554A1300B84627 /* CustomHostingController.swift */; }; + 7A1022772C557EC400B84627 /* LocationPickerScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1022762C557EC400B84627 /* LocationPickerScreen.swift */; }; + 7A1022792C557ED600B84627 /* LocationPickerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1022782C557ED600B84627 /* LocationPickerViewModel.swift */; }; + 7A10227B2C557EE900B84627 /* LocationPickerCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A10227A2C557EE900B84627 /* LocationPickerCoordinator.swift */; }; 7A1090E824A394F100B4F0B2 /* AudioRecordCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1090E724A394F100B4F0B2 /* AudioRecordCell.swift */; }; 7A1090EA24A3A26300B4F0B2 /* AudioPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1090E924A3A26300B4F0B2 /* AudioPlayer.swift */; }; 7A1090EC24A4E3E100B4F0B2 /* CellProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1090EB24A4E3E100B4F0B2 /* CellProgressView.swift */; }; @@ -80,6 +88,7 @@ 7A64AE752469DFB600ABE48E /* MediaBrowserViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A64AE712469DFB600ABE48E /* MediaBrowserViewController.swift */; }; 7A64AE762469DFB600ABE48E /* ContentTransformers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A64AE722469DFB600ABE48E /* ContentTransformers.swift */; }; 7A659B5B24A3768A0043A0F2 /* Substrings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A659B5A24A3768A0043A0F2 /* Substrings.swift */; }; + 7A6C4D9E2C56BCA600982597 /* SwiftLocation in Frameworks */ = {isa = PBXBuildFile; productRef = 7A6C4D9D2C56BCA600982597 /* SwiftLocation */; }; 7A6DD903242BF4A5009DE740 /* PlateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6DD902242BF4A5009DE740 /* PlateView.swift */; }; 7A6DD90824329144009DE740 /* CenterTextLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A6DD90724329144009DE740 /* CenterTextLayer.swift */; }; 7A6DD90A24329541009DE740 /* RoadNumbers2.0.otf in Resources */ = {isa = PBXBuildFile; fileRef = 7A6DD90924329541009DE740 /* RoadNumbers2.0.otf */; }; @@ -99,7 +108,6 @@ 7A761C08267E8EA20005F28F /* JWT.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A43F9F7246C8A6200BA5B49 /* JWT.swift */; }; 7A761C09267E8EE40005F28F /* Base64FS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A96AE32246C095700297C33 /* Base64FS.swift */; }; 7A761C0B267E8FF90005F28F /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A761C0A267E8FF90005F28F /* Error.swift */; }; - 7A813DBE2506A57100CC93B9 /* AuthenticationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A813DBD2506A57100CC93B9 /* AuthenticationServices.framework */; }; 7A813DC32508EE4F00CC93B9 /* EventCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A813DC22508EE4F00CC93B9 /* EventCell.swift */; }; 7A813DC5250AAF3C00CC93B9 /* LocationEditController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A813DC4250AAF3C00CC93B9 /* LocationEditController.swift */; }; 7A813DC9250B5C9700CC93B9 /* LocationRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A813DC8250B5C9700CC93B9 /* LocationRow.swift */; }; @@ -108,7 +116,6 @@ 7A8A220B248D67B60073DFD9 /* VehicleReportImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8A220A248D67B60073DFD9 /* VehicleReportImage.swift */; }; 7A8AB76525A0DB8F00ECF2C1 /* BundleVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8AB76425A0DB8F00ECF2C1 /* BundleVersion.swift */; }; 7A8AB76B25A1D95500ECF2C1 /* SourceStatusRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A8AB76A25A1D95500ECF2C1 /* SourceStatusRow.swift */; }; - 7A8C4A5B2C1C55680052DDF3 /* SwiftLocation in Frameworks */ = {isa = PBXBuildFile; productRef = 7A8C4A5A2C1C55680052DDF3 /* SwiftLocation */; }; 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 */; }; @@ -116,7 +123,6 @@ 7A96AE2F246B2BCD00297C33 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A96AE2E246B2BCD00297C33 /* WebKit.framework */; }; 7A99406426E4BFAE002E9CB6 /* VehicleNoteCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A99406326E4BFAE002E9CB6 /* VehicleNoteCell.swift */; }; 7A9FEEC82529AB23001CA50E /* RxRealmDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9FEEC72529AB23001CA50E /* RxRealmDataSource.swift */; }; - 7AA363362C25B64A00851D6D /* RealmSwift in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 7A1CF80429A41C66007962DA /* RealmSwift */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 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 */; }; @@ -211,7 +217,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 7AA363362C25B64A00851D6D /* RealmSwift in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -222,6 +227,7 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + 7A1022692C55197D00B84627 /* RealmSwift in Embed Frameworks */, 7AF6D2052677C03B0086EA64 /* AutoCatCore.framework in Embed Frameworks */, ); name = "Embed Frameworks"; @@ -235,6 +241,13 @@ 7A000AA124C2EEDE001F5B00 /* Location.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Location.swift; sourceTree = ""; }; 7A0420A925619AEC00034941 /* Osago.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Osago.swift; sourceTree = ""; }; 7A0516192414FF0900FC55AC /* DateSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateSection.swift; sourceTree = ""; }; + 7A10226B2C551EC500B84627 /* LocationEditScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationEditScreen.swift; sourceTree = ""; }; + 7A10226D2C551EE000B84627 /* LocationEditViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationEditViewModel.swift; sourceTree = ""; }; + 7A10226F2C551EFD00B84627 /* LocationEditCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationEditCoordinator.swift; sourceTree = ""; }; + 7A1022712C554A1300B84627 /* CustomHostingController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomHostingController.swift; sourceTree = ""; }; + 7A1022762C557EC400B84627 /* LocationPickerScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationPickerScreen.swift; sourceTree = ""; }; + 7A1022782C557ED600B84627 /* LocationPickerViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationPickerViewModel.swift; sourceTree = ""; }; + 7A10227A2C557EE900B84627 /* LocationPickerCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationPickerCoordinator.swift; sourceTree = ""; }; 7A1090E724A394F100B4F0B2 /* AudioRecordCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioRecordCell.swift; sourceTree = ""; }; 7A1090E924A3A26300B4F0B2 /* AudioPlayer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayer.swift; sourceTree = ""; }; 7A1090EB24A4E3E100B4F0B2 /* CellProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CellProgressView.swift; sourceTree = ""; }; @@ -415,7 +428,6 @@ files = ( 7AA7BC3525A5DFB80053A5D5 /* ExceptionCatcher in Frameworks */, 7AA7BC3325A5DFB80053A5D5 /* Kingfisher in Frameworks */, - 7A813DBE2506A57100CC93B9 /* AuthenticationServices.framework in Frameworks */, 7ADF23062C25B5BF002624FF /* RealmSwift in Frameworks */, 7AA7BC3625A5DFB80053A5D5 /* PKHUD in Frameworks */, 7AC3554A2969652F00889457 /* SwiftEntryKit in Frameworks */, @@ -449,7 +461,7 @@ 7A1CF80529A41C66007962DA /* RealmSwift in Frameworks */, 7AABB1F2267E9CC800D7AB32 /* SwiftDate in Frameworks */, 7A176DB72C432F8800999D6B /* Mockable in Frameworks */, - 7A8C4A5B2C1C55680052DDF3 /* SwiftLocation in Frameworks */, + 7A6C4D9E2C56BCA600982597 /* SwiftLocation in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -465,6 +477,26 @@ path = eureka; sourceTree = ""; }; + 7A10226A2C551EA200B84627 /* LocationEditScreen */ = { + isa = PBXGroup; + children = ( + 7A10226B2C551EC500B84627 /* LocationEditScreen.swift */, + 7A10226D2C551EE000B84627 /* LocationEditViewModel.swift */, + 7A10226F2C551EFD00B84627 /* LocationEditCoordinator.swift */, + ); + path = LocationEditScreen; + sourceTree = ""; + }; + 7A1022752C557E3F00B84627 /* LocationPickerScreen */ = { + isa = PBXGroup; + children = ( + 7A1022762C557EC400B84627 /* LocationPickerScreen.swift */, + 7A1022782C557ED600B84627 /* LocationPickerViewModel.swift */, + 7A10227A2C557EE900B84627 /* LocationPickerCoordinator.swift */, + ); + path = LocationPickerScreen; + sourceTree = ""; + }; 7A1146F423FDE7E500B424AF = { isa = PBXGroup; children = ( @@ -593,6 +625,8 @@ 7A1441632C297E9800E79018 /* Screens */ = { isa = PBXGroup; children = ( + 7A1022752C557E3F00B84627 /* LocationPickerScreen */, + 7A10226A2C551EA200B84627 /* LocationEditScreen */, 7A71580A2C44451B00852088 /* AdsScreen */, 7A7158052C44083F00852088 /* OsagoScreen */, 7A7157FE2C43EA5200852088 /* OwnersScreen */, @@ -909,6 +943,7 @@ 7A961C6B2C4C3C8600CE2211 /* TextRowView.swift */, 7A961C6D2C4C3C9E00CE2211 /* LinkRowView.swift */, 7AAAFAD92C4D1AFE0050410D /* Zoomable.swift */, + 7A1022712C554A1300B84627 /* CustomHostingController.swift */, ); path = SwiftUI; sourceTree = ""; @@ -1031,8 +1066,8 @@ packageProductDependencies = ( 7AABB1F1267E9CC800D7AB32 /* SwiftDate */, 7A1CF80429A41C66007962DA /* RealmSwift */, - 7A8C4A5A2C1C55680052DDF3 /* SwiftLocation */, 7A176DB62C432F8800999D6B /* Mockable */, + 7A6C4D9D2C56BCA600982597 /* SwiftLocation */, ); productName = AutoCatCore; productReference = 7AF6D1EF2677C03B0086EA64 /* AutoCatCore.framework */; @@ -1081,8 +1116,8 @@ 7A35177927E23F8800DC538C /* XCRemoteSwiftPackageReference "Eureka" */, 7AC355482969652F00889457 /* XCRemoteSwiftPackageReference "SwiftEntryKit" */, 7A1CF7FD29A41C2F007962DA /* XCRemoteSwiftPackageReference "realm-swift" */, - 7AF336F72C1C54EC002FB8A3 /* XCRemoteSwiftPackageReference "SwiftLocation" */, 7A176DB52C432F8800999D6B /* XCRemoteSwiftPackageReference "Mockable" */, + 7A6C4D9C2C56BCA600982597 /* XCRemoteSwiftPackageReference "SwiftLocation" */, ); productRefGroup = 7A1146FE23FDE7E500B424AF /* Products */; projectDirPath = ""; @@ -1142,6 +1177,7 @@ files = ( 7A961C6C2C4C3C8600CE2211 /* TextRowView.swift in Sources */, 7AEFC3BE2529D3CC00BADFB2 /* ConfigurableCell.swift in Sources */, + 7A1022772C557EC400B84627 /* LocationPickerScreen.swift in Sources */, 7A7158092C44087E00852088 /* OsagoCoordinator.swift in Sources */, 7A1441662C297EDE00E79018 /* NotesScreen.swift in Sources */, 7A11470123FDE7E500B424AF /* AppDelegate.swift in Sources */, @@ -1180,6 +1216,7 @@ 7A71580E2C4445A200852088 /* AdsCoordinator.swift in Sources */, 7AFBE8CA2C3081C7003C491D /* ACProgressHud+Modifiers.swift in Sources */, 7A27ADF7249FEF690035F39E /* Recorder.swift in Sources */, + 7A10226C2C551EC500B84627 /* LocationEditScreen.swift in Sources */, 7A7158072C44085600852088 /* OsagoScreen.swift in Sources */, 7AAAFAD32C4D0FD00050410D /* ACImageSliderView.swift in Sources */, 7A3F07AB24360DC800E59687 /* Dated.swift in Sources */, @@ -1195,14 +1232,18 @@ 7A64AE742469DFB600ABE48E /* MediaContentView.swift in Sources */, 7A1090EC24A4E3E100B4F0B2 /* CellProgressView.swift in Sources */, 7A96AE2D246B2B7400297C33 /* GoogleSignInController.swift in Sources */, + 7A10227B2C557EE900B84627 /* LocationPickerCoordinator.swift in Sources */, 7A176DB22C43071A00999D6B /* ApiServiceStub.swift in Sources */, 7A1090EA24A3A26300B4F0B2 /* AudioPlayer.swift in Sources */, 7A11471623FDEB2A00B424AF /* MainSplitController.swift in Sources */, 7A813DC5250AAF3C00CC93B9 /* LocationEditController.swift in Sources */, 7A6DD903242BF4A5009DE740 /* PlateView.swift in Sources */, + 7A1022722C554A1300B84627 /* CustomHostingController.swift in Sources */, 7ADF6C9F251201D200F237B2 /* GlobalEventsController.swift in Sources */, + 7A1022792C557ED600B84627 /* LocationPickerViewModel.swift in Sources */, 7A11470323FDE7E500B424AF /* SceneDelegate.swift in Sources */, 7A530B7E24017FEE00CBFE6E /* VehicleCell.swift in Sources */, + 7A10226E2C551EE000B84627 /* LocationEditViewModel.swift in Sources */, 7AFBE8CE2C308B53003C491D /* ACMessageView.swift in Sources */, 7A14416C2C297F2100E79018 /* NotesCoordinator.swift in Sources */, 7A813DCB250B5DC900CC93B9 /* LocationPickerController.swift in Sources */, @@ -1229,6 +1270,7 @@ 7A64AE752469DFB600ABE48E /* MediaBrowserViewController.swift in Sources */, 7A64AE732469DFB600ABE48E /* DismissAnimationController.swift in Sources */, 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 */, @@ -1501,7 +1543,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 127; + CURRENT_PROJECT_VERSION = 129; DEVELOPMENT_TEAM = 46DTTB8X4S; INFOPLIST_FILE = AutoCat/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = AutoCat; @@ -1527,7 +1569,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 127; + CURRENT_PROJECT_VERSION = 129; DEVELOPMENT_TEAM = 46DTTB8X4S; INFOPLIST_FILE = AutoCat/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = AutoCat; @@ -1787,6 +1829,14 @@ minimumVersion = 5.0.0; }; }; + 7A6C4D9C2C56BCA600982597 /* XCRemoteSwiftPackageReference "SwiftLocation" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/malcommac/SwiftLocation.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 6.0.0; + }; + }; 7A813DBF2508C4D900CC93B9 /* XCRemoteSwiftPackageReference "ExceptionCatcher" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/sindresorhus/ExceptionCatcher"; @@ -1811,14 +1861,6 @@ minimumVersion = 2.0.0; }; }; - 7AF336F72C1C54EC002FB8A3 /* XCRemoteSwiftPackageReference "SwiftLocation" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/malcommac/SwiftLocation.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 6.0.0; - }; - }; 7AF58D322402A91C00CE01A0 /* XCRemoteSwiftPackageReference "Kingfisher" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/onevcat/Kingfisher"; @@ -1850,16 +1892,16 @@ package = 7A35177927E23F8800DC538C /* XCRemoteSwiftPackageReference "Eureka" */; productName = Eureka; }; + 7A6C4D9D2C56BCA600982597 /* SwiftLocation */ = { + isa = XCSwiftPackageProductDependency; + package = 7A6C4D9C2C56BCA600982597 /* XCRemoteSwiftPackageReference "SwiftLocation" */; + productName = SwiftLocation; + }; 7A813DC02508C4D900CC93B9 /* ExceptionCatcher */ = { isa = XCSwiftPackageProductDependency; package = 7A813DBF2508C4D900CC93B9 /* XCRemoteSwiftPackageReference "ExceptionCatcher" */; productName = ExceptionCatcher; }; - 7A8C4A5A2C1C55680052DDF3 /* SwiftLocation */ = { - isa = XCSwiftPackageProductDependency; - package = 7AF336F72C1C54EC002FB8A3 /* XCRemoteSwiftPackageReference "SwiftLocation" */; - productName = SwiftLocation; - }; 7AABB1F1267E9CC800D7AB32 /* SwiftDate */ = { isa = XCSwiftPackageProductDependency; package = 7A05160F241412CA00FC55AC /* XCRemoteSwiftPackageReference "SwiftDate" */; diff --git a/AutoCat/Controllers/Location/EventsController.swift b/AutoCat/Controllers/Location/EventsController.swift index e8e9fb9..f37d507 100644 --- a/AutoCat/Controllers/Location/EventsController.swift +++ b/AutoCat/Controllers/Location/EventsController.swift @@ -253,27 +253,22 @@ class EventsController: UIViewController, UITableViewDataSource, UITableViewDele } func editEvent(event: VehicleEventDto) { - let sb = UIStoryboard(name: "Main", bundle: nil) - let controller = sb.instantiateViewController(identifier: "LocationEditController") as LocationEditController - controller.title = NSLocalizedString("Edit event", comment: "") - controller.date = Date(timeIntervalSince1970: event.date) - controller.placemark = Placemark(latitude: event.latitude, longitude: event.longitude, address: event.address) - controller.onDone = { newEvent in - var updatedEvent = newEvent - updatedEvent.id = event.id - self.navigationController?.popViewController(animated: true, completion: { - Task { + + if let navigationController { + let coordinator = LocationEditCoordinator(navController: navigationController, + event: event) + Task { + if let event = try await coordinator.start() { do { HUD.show(.progress) - let vehicle = try await ApiService.shared.edit(event: updatedEvent) + let vehicle = try await ApiService.shared.edit(event: event) self.update(vehicle: vehicle) } catch { HUD.show(error: error) } } - }) + } } - self.navigationController?.pushViewController(controller, animated: true) } func copyEvent(event: VehicleEventDto) { @@ -327,23 +322,21 @@ class EventsController: UIViewController, UITableViewDataSource, UITableViewDele return } - let sb = UIStoryboard(name: "Main", bundle: nil) - let controller = sb.instantiateViewController(identifier: "LocationEditController") as LocationEditController - controller.title = NSLocalizedString("Add new event", comment: "") - controller.onDone = { newEvent in - self.navigationController?.popViewController(animated: true, completion: { - Task { + if let navigationController { + let coordinator = LocationEditCoordinator(navController: navigationController) + + Task { + if let event = try await coordinator.start() { do { HUD.show(.progress) - let vehicle = try await ApiService.shared.add(event: newEvent, to: vehicle.getNumber()) + let vehicle = try await ApiService.shared.add(event: event, to: vehicle.getNumber()) self.update(vehicle: vehicle) } catch { HUD.show(error: error) } } - }) + } } - self.navigationController?.pushViewController(controller, animated: true) } @objc func pasteEvent(_ sender: UIBarButtonItem) { @@ -390,16 +383,16 @@ class EventsController: UIViewController, UITableViewDataSource, UITableViewDele func update(vehicle: VehicleDto) -> Bool { do { let realm = try Realm() - if let realmVehicle = realm.object(ofType: Vehicle.self, forPrimaryKey: vehicle.getNumber()) { + if realm.object(ofType: Vehicle.self, forPrimaryKey: vehicle.getNumber()) != nil { try ExceptionCatcher.catch { try realm.write { realm.add(Vehicle(dto: vehicle), update: .all) } } - } else { - self.vehicle?.events = vehicle.events } - self.updateInterface() + + self.vehicle?.events = vehicle.events.sorted { $0.date > $1.date } + HUD.hide() return true } catch { diff --git a/AutoCat/Controllers/RecordsController.swift b/AutoCat/Controllers/RecordsController.swift index c45aaec..310eed8 100644 --- a/AutoCat/Controllers/RecordsController.swift +++ b/AutoCat/Controllers/RecordsController.swift @@ -114,7 +114,9 @@ class RecordsController: UIViewController, UITableViewDelegate { let session = notification.object as? AVAudioSession else { return } if reason == .categoryChange && session.category == .playAndRecord { - alert = self.showRecordingAlert() + DispatchQueue.main.async { + alert = self.showRecordingAlert() + } } } #endif diff --git a/AutoCat/Screens/LocationEditScreen/LocationEditCoordinator.swift b/AutoCat/Screens/LocationEditScreen/LocationEditCoordinator.swift new file mode 100644 index 0000000..e1539dc --- /dev/null +++ b/AutoCat/Screens/LocationEditScreen/LocationEditCoordinator.swift @@ -0,0 +1,42 @@ +// +// LocationEditCoordinator.swift +// AutoCat +// +// Created by Selim Mustafaev on 27.07.2024. +// Copyright © 2024 Selim Mustafaev. All rights reserved. +// + +import UIKit +import SwiftUI +import AutoCatCore + +@MainActor +final class LocationEditCoordinator: Coordinator { + + let viewController: UINavigationController? + let event: VehicleEventDto + + init(navController: UINavigationController?, event: VehicleEventDto? = nil) { + self.viewController = navController + self.event = event ?? VehicleEventDto(lat: 0, lon: 0) + } + + func start() async throws -> VehicleEventDto? { + + let viewModel = LocationEditViewModel(event: event) + viewModel.coordinator = self + + let screen = LocationEditScreen(viewModel: viewModel) + let controller = CustomHostingController(rootView: screen) + viewController?.pushViewController(controller, animated: true) + await controller.waitForDisappear() + return viewModel.result + } + + func openLocationPicker(event: VehicleEventDto) async -> VehicleEventDto? { + + let coordinator = LocationPickerCoordinator(navController: viewController, + event: event) + return try? await coordinator.start() + } +} diff --git a/AutoCat/Screens/LocationEditScreen/LocationEditScreen.swift b/AutoCat/Screens/LocationEditScreen/LocationEditScreen.swift new file mode 100644 index 0000000..c8c6bbb --- /dev/null +++ b/AutoCat/Screens/LocationEditScreen/LocationEditScreen.swift @@ -0,0 +1,45 @@ +// +// LocationEditScreen.swift +// AutoCat +// +// Created by Selim Mustafaev on 27.07.2024. +// Copyright © 2024 Selim Mustafaev. All rights reserved. +// + +import SwiftUI +import AutoCatCore + +struct LocationEditScreen: View { + + @Environment(\.dismiss) var dismiss + + @StateObject var viewModel: LocationEditViewModel + + var body: some View { + List { + DatePicker("Date", selection: $viewModel.date) + .datePickerStyle(.compact) + TextRowView(title: "Location", value: viewModel.event.location) + .onTapGesture { + Task { await viewModel.pickLocation() } + } + } + .navigationTitle("Edit event") + .toolbar { + ToolbarItem(placement: .primaryAction) { + Button("Done") { + viewModel.done() + dismiss() + } + } + } + } +} + +#Preview { + + var event = VehicleEventDto(lat: 25.54984, lon: 36.34857) + event.address = "Ул. Ленина, 123" + + return LocationEditScreen(viewModel: .init(event: event)) +} diff --git a/AutoCat/Screens/LocationEditScreen/LocationEditViewModel.swift b/AutoCat/Screens/LocationEditScreen/LocationEditViewModel.swift new file mode 100644 index 0000000..a2d54c9 --- /dev/null +++ b/AutoCat/Screens/LocationEditScreen/LocationEditViewModel.swift @@ -0,0 +1,37 @@ +// +// LocationEditViewModel.swift +// AutoCat +// +// Created by Selim Mustafaev on 27.07.2024. +// Copyright © 2024 Selim Mustafaev. All rights reserved. +// + +import AutoCatCore +import SwiftUI + +@MainActor +final class LocationEditViewModel: ObservableObject { + + weak var coordinator: LocationEditCoordinator? + + @Published var event: VehicleEventDto + @Published var date: Date + + var result: VehicleEventDto? + + init(event: VehicleEventDto) { + self.event = event + self.date = Date(timeIntervalSince1970: event.date) + } + + func done() { + event.date = date.timeIntervalSince1970 + result = event + } + + func pickLocation() async { + if let newEvent = await coordinator?.openLocationPicker(event: event) { + event = newEvent + } + } +} diff --git a/AutoCat/Screens/LocationPickerScreen/LocationPickerCoordinator.swift b/AutoCat/Screens/LocationPickerScreen/LocationPickerCoordinator.swift new file mode 100644 index 0000000..5f5c4fd --- /dev/null +++ b/AutoCat/Screens/LocationPickerScreen/LocationPickerCoordinator.swift @@ -0,0 +1,33 @@ +// +// LocationPickerCoordinator.swift +// AutoCat +// +// Created by Selim Mustafaev on 27.07.2024. +// Copyright © 2024 Selim Mustafaev. All rights reserved. +// + +import UIKit +import SwiftUI +import AutoCatCore + +@MainActor +final class LocationPickerCoordinator: Coordinator { + + let viewController: UINavigationController? + let event: VehicleEventDto + + init(navController: UINavigationController?, event: VehicleEventDto) { + self.viewController = navController + self.event = event + } + + func start() async throws -> VehicleEventDto? { + + let viewModel = LocationPickerViewModel(event: event) + let screen = LocationPickerScreen(viewModel: viewModel) + let controller = CustomHostingController(rootView: screen) + viewController?.pushViewController(controller, animated: true) + await controller.waitForDisappear() + return viewModel.result + } +} diff --git a/AutoCat/Screens/LocationPickerScreen/LocationPickerScreen.swift b/AutoCat/Screens/LocationPickerScreen/LocationPickerScreen.swift new file mode 100644 index 0000000..abb4e6f --- /dev/null +++ b/AutoCat/Screens/LocationPickerScreen/LocationPickerScreen.swift @@ -0,0 +1,48 @@ +// +// LocationPickerScreen.swift +// AutoCat +// +// Created by Selim Mustafaev on 27.07.2024. +// Copyright © 2024 Selim Mustafaev. All rights reserved. +// + +import SwiftUI +import AutoCatCore +import MapKit + +struct LocationPickerScreen: View { + + @Environment(\.dismiss) var dismiss + + @StateObject var viewModel: LocationPickerViewModel + + var body: some View { + ZStack { + Map(coordinateRegion: $viewModel.region) + Image(systemName: "mappin.and.ellipse") + .resizable() + .aspectRatio(contentMode: .fit) + .frame(height: 48) + .offset(.init(width: 0, height: -10)) + .foregroundColor(.blue) + } + .ignoresSafeArea() + .navigationTitle(viewModel.event.location) + .toolbar { + ToolbarItem(placement: .primaryAction) { + Button("Done") { + viewModel.done() + dismiss() + } + } + } + } +} + +#Preview { + + var event = VehicleEventDto(lat: 47.250049, lon: 39.711821) + event.address = "Ул. Ленина, 123" + + return LocationPickerScreen(viewModel: .init(event: event)) +} diff --git a/AutoCat/Screens/LocationPickerScreen/LocationPickerViewModel.swift b/AutoCat/Screens/LocationPickerScreen/LocationPickerViewModel.swift new file mode 100644 index 0000000..348c90f --- /dev/null +++ b/AutoCat/Screens/LocationPickerScreen/LocationPickerViewModel.swift @@ -0,0 +1,74 @@ +// +// LocationPickerViewModel.swift +// AutoCat +// +// Created by Selim Mustafaev on 27.07.2024. +// Copyright © 2024 Selim Mustafaev. All rights reserved. +// + +import AutoCatCore +import Combine +import MapKit +import SwiftUI + +@MainActor +final class LocationPickerViewModel: ObservableObject { + + @Published var event: VehicleEventDto + @Published var region: MKCoordinateRegion { + didSet { + Task { await updateEvent(region: region) } + } + } + + var result: VehicleEventDto? + + var geocodingTask: Task? + + init(event: VehicleEventDto) { + self.event = event + + let center = CLLocationCoordinate2D(latitude: event.latitude, longitude: event.longitude) + self.region = MKCoordinateRegion(center: center, + latitudinalMeters: 1000, + longitudinalMeters: 1000) + + if event.latitude == 0 && event.longitude == 0 { + Task { await moveToCurrentLocation() } + } + } + + func moveToCurrentLocation() async { + do { + let currentEvent = try await RxLocationManager.requestCurrentLocation() + let center = CLLocationCoordinate2D(latitude: currentEvent.latitude, + longitude: currentEvent.longitude) + self.region = MKCoordinateRegion(center: center, + latitudinalMeters: 1000, + longitudinalMeters: 1000) + + event.latitude = currentEvent.latitude + event.longitude = currentEvent.longitude + event.address = try? await RxLocationManager.getAddressForLocation(latitude: region.center.latitude, + longitude: region.center.longitude) + } catch { + print(error) + } + } + + func updateEvent(region: MKCoordinateRegion) async { + geocodingTask?.cancel() + geocodingTask = Task { + event.latitude = region.center.latitude + event.longitude = region.center.longitude + try await Task.sleep(nanoseconds: 500_000_000) + event.address = try? await RxLocationManager.getAddressForLocation(latitude: region.center.latitude, + longitude: region.center.longitude) + geocodingTask = nil + } + } + + func done() { + result = event + } +} diff --git a/AutoCat/SwiftUI/CustomHostingController.swift b/AutoCat/SwiftUI/CustomHostingController.swift new file mode 100644 index 0000000..42ba917 --- /dev/null +++ b/AutoCat/SwiftUI/CustomHostingController.swift @@ -0,0 +1,27 @@ +// +// CustomHostingController.swift +// AutoCat +// +// Created by Selim Mustafaev on 27.07.2024. +// Copyright © 2024 Selim Mustafaev. All rights reserved. +// + +import UIKit +import SwiftUI + +final class CustomHostingController: UIHostingController { + + var continuation: CheckedContinuation? + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + + if isMovingFromParent || isBeingDismissed { + continuation?.resume() + } + } + + func waitForDisappear() async { + await withCheckedContinuation { continuation = $0 } + } +} diff --git a/AutoCat/SwiftUI/TextRowView.swift b/AutoCat/SwiftUI/TextRowView.swift index ba611da..c14bc0d 100644 --- a/AutoCat/SwiftUI/TextRowView.swift +++ b/AutoCat/SwiftUI/TextRowView.swift @@ -10,7 +10,7 @@ import SwiftUI struct TextRowView: View { - let title: String + let title: LocalizedStringKey let value: String? var body: some View { @@ -36,7 +36,7 @@ struct TextRowView: View { struct SimpleTextRowView: View { - let title: String + let title: LocalizedStringKey let value: String? var body: some View { diff --git a/AutoCatCore/Models/DTO/VehicleEventDto.swift b/AutoCatCore/Models/DTO/VehicleEventDto.swift index 6d8b878..7a4f1ee 100644 --- a/AutoCatCore/Models/DTO/VehicleEventDto.swift +++ b/AutoCatCore/Models/DTO/VehicleEventDto.swift @@ -41,6 +41,14 @@ public struct VehicleEventDto: Codable, Sendable { } } + public var location: String { + if let address { + return address + } else { + return "\(latitude), \(longitude)" + } + } + public func findAddress() async throws { guard address == nil else { return diff --git a/AutoCatCore/Utils/Location.swift b/AutoCatCore/Utils/Location.swift index 5d2e601..f848953 100644 --- a/AutoCatCore/Utils/Location.swift +++ b/AutoCatCore/Utils/Location.swift @@ -17,6 +17,7 @@ enum LocationError: LocalizedError { } } +@MainActor public class RxLocationManager { private let generalErrors: [CLError.Code] = [.locationUnknown, .denied, .network, .headingFailure, .rangingUnavailable, .rangingFailure] @@ -68,6 +69,7 @@ public class RxLocationManager { if let eventTask { return try await eventTask.value } else { + try await checkPermissions() let task = Task { let location = try await requestLocation() eventTask = nil