diff --git a/.gitignore b/.gitignore index 5b5b9b0..aec898f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ AutoCat2.xcodeproj/xcuserdata/ +AutoCat2.xcodeproj/project.xcworkspace/xcuserdata/ .DS_Store diff --git a/AutoCat2 (macOS).entitlements b/AutoCat2 (macOS).entitlements deleted file mode 100644 index ee95ab7..0000000 --- a/AutoCat2 (macOS).entitlements +++ /dev/null @@ -1,10 +0,0 @@ - - - - - com.apple.security.app-sandbox - - com.apple.security.network.client - - - diff --git a/AutoCat2.xcodeproj/project.pbxproj b/AutoCat2.xcodeproj/project.pbxproj index bf88cd4..9734037 100644 --- a/AutoCat2.xcodeproj/project.pbxproj +++ b/AutoCat2.xcodeproj/project.pbxproj @@ -7,126 +7,106 @@ objects = { /* Begin PBXBuildFile section */ - 7A2329B926AF122400264CFA /* MainViewSmall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2329B826AF122400264CFA /* MainViewSmall.swift */; }; - 7A2329BA26AF122400264CFA /* MainViewSmall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2329B826AF122400264CFA /* MainViewSmall.swift */; }; - 7A2329BD26AF123300264CFA /* MainViewBig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2329BB26AF123200264CFA /* MainViewBig.swift */; }; - 7A2329BF26AF140F00264CFA /* CheckView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2329BE26AF140F00264CFA /* CheckView.swift */; }; - 7A2329C026AF140F00264CFA /* CheckView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2329BE26AF140F00264CFA /* CheckView.swift */; }; - 7A2329C226AF141E00264CFA /* SearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2329C126AF141E00264CFA /* SearchView.swift */; }; - 7A2329C326AF141E00264CFA /* SearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2329C126AF141E00264CFA /* SearchView.swift */; }; - 7A2329C526AF142900264CFA /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2329C426AF142900264CFA /* SettingsView.swift */; }; - 7A2329C626AF142900264CFA /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2329C426AF142900264CFA /* SettingsView.swift */; }; - 7A2329C826AF143F00264CFA /* RecordsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2329C726AF143F00264CFA /* RecordsView.swift */; }; - 7A2329C926AF143F00264CFA /* RecordsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2329C726AF143F00264CFA /* RecordsView.swift */; }; - 7A2329CB26AF15AC00264CFA /* ReportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2329CA26AF15AC00264CFA /* ReportView.swift */; }; - 7A2329CC26AF15AC00264CFA /* ReportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2329CA26AF15AC00264CFA /* ReportView.swift */; }; - 7A40D5932691C6D8009B0BC4 /* Tests_iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D5922691C6D8009B0BC4 /* Tests_iOS.swift */; }; - 7A40D59D2691C6D8009B0BC4 /* Tests_macOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D59C2691C6D8009B0BC4 /* Tests_macOS.swift */; }; - 7A40D59E2691C6D8009B0BC4 /* AutoCat2.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D5792691C6D7009B0BC4 /* AutoCat2.xcdatamodeld */; }; - 7A40D59F2691C6D8009B0BC4 /* AutoCat2.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D5792691C6D7009B0BC4 /* AutoCat2.xcdatamodeld */; }; - 7A40D5A02691C6D8009B0BC4 /* AutoCat2App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D57B2691C6D7009B0BC4 /* AutoCat2App.swift */; }; - 7A40D5A12691C6D8009B0BC4 /* AutoCat2App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D57B2691C6D7009B0BC4 /* AutoCat2App.swift */; }; - 7A40D5A22691C6D8009B0BC4 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D57C2691C6D7009B0BC4 /* ContentView.swift */; }; - 7A40D5A32691C6D8009B0BC4 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D57C2691C6D7009B0BC4 /* ContentView.swift */; }; - 7A40D5A42691C6D8009B0BC4 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D57D2691C6D7009B0BC4 /* Persistence.swift */; }; - 7A40D5A52691C6D8009B0BC4 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D57D2691C6D7009B0BC4 /* Persistence.swift */; }; - 7A40D5A62691C6D8009B0BC4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7A40D57E2691C6D7009B0BC4 /* Assets.xcassets */; }; - 7A40D5A72691C6D8009B0BC4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7A40D57E2691C6D7009B0BC4 /* Assets.xcassets */; }; - 7A40D5E126924AEC009B0BC4 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D5E026924AEC009B0BC4 /* User.swift */; }; - 7A40D5E326924B09009B0BC4 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D5E226924B09009B0BC4 /* Settings.swift */; }; - 7A40D5E426924B09009B0BC4 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D5E226924B09009B0BC4 /* Settings.swift */; }; - 7A40D5E526924B0C009B0BC4 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D5E026924AEC009B0BC4 /* User.swift */; }; - 7A40D5E926938BEC009B0BC4 /* AuthView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D5E826938BEC009B0BC4 /* AuthView.swift */; }; - 7A40D5EA26938BEC009B0BC4 /* AuthView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D5E826938BEC009B0BC4 /* AuthView.swift */; }; - 7A40D5ED2693A1EA009B0BC4 /* AuthVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D5EC2693A1EA009B0BC4 /* AuthVM.swift */; }; - 7A40D5EE2693A1EA009B0BC4 /* AuthVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D5EC2693A1EA009B0BC4 /* AuthVM.swift */; }; - 7A40D5F62693A63A009B0BC4 /* SettingsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D5F52693A63A009B0BC4 /* SettingsTests.swift */; }; - 7A40D5FE2693A91F009B0BC4 /* CocoaError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D5FD2693A91F009B0BC4 /* CocoaError.swift */; }; - 7A40D5FF2693A91F009B0BC4 /* CocoaError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D5FD2693A91F009B0BC4 /* CocoaError.swift */; }; - 7A40D6022694FF5D009B0BC4 /* Api.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D6012694FF5D009B0BC4 /* Api.swift */; }; - 7A40D6032694FF5D009B0BC4 /* Api.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D6012694FF5D009B0BC4 /* Api.swift */; }; - 7A40D60826998DCF009B0BC4 /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D60726998DCF009B0BC4 /* Alert.swift */; }; - 7A40D60926998DCF009B0BC4 /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D60726998DCF009B0BC4 /* Alert.swift */; }; - 7A40D60C2699A070009B0BC4 /* MockURLProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A40D60B2699A070009B0BC4 /* MockURLProtocol.swift */; }; - 7A503C03269F382F002C1A0D /* login_success.json in Resources */ = {isa = PBXBuildFile; fileRef = 7A503C02269F382F002C1A0D /* login_success.json */; }; - 7A683999269612EA00B2188A /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A683998269612EA00B2188A /* Response.swift */; }; - 7A68399A269612EA00B2188A /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A683998269612EA00B2188A /* Response.swift */; }; - 7A971F0626AD6F2F007E527B /* ApiMethodMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A971F0526AD6F2F007E527B /* ApiMethodMock.swift */; }; - 7A971F0826AD7084007E527B /* LoginMethodMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A971F0726AD7084007E527B /* LoginMethodMock.swift */; }; - 7A971F0A26AD74FD007E527B /* ApiMethodMockProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A971F0926AD74FD007E527B /* ApiMethodMockProtocol.swift */; }; - 7A971F0D26AD7D4C007E527B /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A971F0C26AD7D4C007E527B /* AnyEncodable.swift */; }; - 7A971F0E26AD7D4C007E527B /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A971F0C26AD7D4C007E527B /* AnyEncodable.swift */; }; - 7A971F2026ADC351007E527B /* ApiError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A971F1F26ADC351007E527B /* ApiError.swift */; }; - 7A971F2126ADC351007E527B /* ApiError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A971F1F26ADC351007E527B /* ApiError.swift */; }; - 7A971F2326ADF74B007E527B /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A971F2226ADF74B007E527B /* MainView.swift */; }; - 7A971F2426ADF74B007E527B /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A971F2226ADF74B007E527B /* MainView.swift */; }; - 7AAEBD35274A47C500230C28 /* PlateNumberView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AAEBD34274A47C500230C28 /* PlateNumberView.swift */; }; - 7AAEBD36274A47C600230C28 /* PlateNumberView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AAEBD34274A47C500230C28 /* PlateNumberView.swift */; }; - 7ABC05A826B05AF9004BB5A7 /* Vehicle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABC05A726B05AF9004BB5A7 /* Vehicle.swift */; }; - 7ABC05A926B05AF9004BB5A7 /* Vehicle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABC05A726B05AF9004BB5A7 /* Vehicle.swift */; }; - 7ACD05D72695C08A00557667 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ACD05D62695C08A00557667 /* Constants.swift */; }; - 7ACD05D82695C08A00557667 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ACD05D62695C08A00557667 /* Constants.swift */; }; - 7ADE63712735E1FF00A152FE /* VName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADE63702735E1FF00A152FE /* VName.swift */; }; - 7ADE63732735E27200A152FE /* VBrand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADE63722735E27200A152FE /* VBrand.swift */; }; - 7ADE63742735E27200A152FE /* VBrand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADE63722735E27200A152FE /* VBrand.swift */; }; - 7ADE63752735E27D00A152FE /* VName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADE63702735E1FF00A152FE /* VName.swift */; }; - 7ADE637827368C4600A152FE /* VehicleService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADE637727368C4600A152FE /* VehicleService.swift */; }; - 7ADE637927368C4600A152FE /* VehicleService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADE637727368C4600A152FE /* VehicleService.swift */; }; - 7ADE637B273692F300A152FE /* StorageService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADE637A273692F300A152FE /* StorageService.swift */; }; - 7ADE637C273692F300A152FE /* StorageService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADE637A273692F300A152FE /* StorageService.swift */; }; - 7ADE637F2738143000A152FE /* CheckNumberPopup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ADE637E2738143000A152FE /* CheckNumberPopup.swift */; }; - 7AEFAEED26985A3400ED2C85 /* ACProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEFAEEC26985A3400ED2C85 /* ACProgressView.swift */; }; - 7AEFAEEE26985A3400ED2C85 /* ACProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEFAEEC26985A3400ED2C85 /* ACProgressView.swift */; }; - 7AF552D92696E5C100578083 /* ApiTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF552D82696E5C100578083 /* ApiTests.swift */; }; - 7AFADDA7274A49DB00D53EFC /* PlateNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFADDA6274A49DB00D53EFC /* PlateNumber.swift */; }; - 7AFADDA8274A49DB00D53EFC /* PlateNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFADDA6274A49DB00D53EFC /* PlateNumber.swift */; }; - 7AFADDAF274A64DD00D53EFC /* RoadNumbers.otf in Resources */ = {isa = PBXBuildFile; fileRef = 7AFADDAD274A64DD00D53EFC /* RoadNumbers.otf */; }; - 7AFADDB0274A64DD00D53EFC /* RoadNumbers.otf in Resources */ = {isa = PBXBuildFile; fileRef = 7AFADDAD274A64DD00D53EFC /* RoadNumbers.otf */; }; - 7AFADDB1274A64DD00D53EFC /* RoadNumbers2.0.otf in Resources */ = {isa = PBXBuildFile; fileRef = 7AFADDAE274A64DD00D53EFC /* RoadNumbers2.0.otf */; }; - 7AFADDB2274A64DD00D53EFC /* RoadNumbers2.0.otf in Resources */ = {isa = PBXBuildFile; fileRef = 7AFADDAE274A64DD00D53EFC /* RoadNumbers2.0.otf */; }; + 6841A0EE0ECCDB3519F728E6 /* HistoryController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6841A0A6580C6E9FF26DC6E2 /* HistoryController.swift */; }; + 6841A20034E0A9378C3B4BB4 /* ACTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6841A850CF48F0715B63D998 /* ACTextField.swift */; }; + 6841A35D2CC223CA7DCD51A2 /* SettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6841A5A586BC03AA21D73DF4 /* SettingsController.swift */; }; + 6841A592745CDD869709EFA7 /* MainTabController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6841A3368FA7CA658C3EEABB /* MainTabController.swift */; }; + 6841A5FE3E4927BF0EFF19F5 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6841A709FA6B606978425A26 /* UIViewController.swift */; }; + 6841A63ECAC11B8956C7539D /* ACButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6841AFB465BF16E122875D9A /* ACButton.swift */; }; + 6841A7D2375D35B0102D7DEC /* SwiftMaskTextfield.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6841A1A12194A8FA89FB52F6 /* SwiftMaskTextfield.swift */; }; + 6841A80BB89BFD8D18E79680 /* UITextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6841ADEEC165FA9124C5EF40 /* UITextField.swift */; }; + 6841A8FF53F0AADF96B138C1 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6841AFE790F6FC06838B1E2C /* UIControl.swift */; }; + 6841ABD5E4B126DEF3612BBD /* PNKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6841AB0052E9DB6914901EA3 /* PNKeyboard.swift */; }; + 6841AF924E165F1B3A3B5FB5 /* AuthController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6841ABEA0314E3B4E438C311 /* AuthController.swift */; }; + 7A24C19727EE212E00049E7F /* RoadNumbers.otf in Resources */ = {isa = PBXBuildFile; fileRef = 7A24C19527EE212E00049E7F /* RoadNumbers.otf */; }; + 7A24C19827EE212E00049E7F /* RoadNumbers2.0.otf in Resources */ = {isa = PBXBuildFile; fileRef = 7A24C19627EE212E00049E7F /* RoadNumbers2.0.otf */; }; + 7A24C19C27EE25B400049E7F /* PlateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A24C19A27EE25B400049E7F /* PlateView.swift */; }; + 7A24C19D27EE25B400049E7F /* FlagLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A24C19B27EE25B400049E7F /* FlagLayer.swift */; }; + 7A24C19F27EE26B900049E7F /* CenterTextLayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A24C19E27EE26B900049E7F /* CenterTextLayer.swift */; }; + 7A28282F27E720AC0049BDBF /* ACTabBarButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A28282E27E720AC0049BDBF /* ACTabBarButton.swift */; }; + 7A28283127E721A70049BDBF /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A28283027E721A70049BDBF /* UIView.swift */; }; + 7A28283327E7263B0049BDBF /* UIStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A28283227E7263B0049BDBF /* UIStackView.swift */; }; + 7A28283627E74C110049BDBF /* SwiftEntryKit in Frameworks */ = {isa = PBXBuildFile; productRef = 7A28283527E74C110049BDBF /* SwiftEntryKit */; }; + 7A28283827E74D930049BDBF /* CheckController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A28283727E74D930049BDBF /* CheckController.swift */; }; + 7A48B26727D9442A004D1A4B /* PKHUD in Frameworks */ = {isa = PBXBuildFile; productRef = 7A48B26627D9442A004D1A4B /* PKHUD */; }; + 7A49F4A327D4061900AEAAE0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F4A227D4061900AEAAE0 /* AppDelegate.swift */; }; + 7A49F4A527D4061900AEAAE0 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F4A427D4061900AEAAE0 /* SceneDelegate.swift */; }; + 7A49F4A727D4061900AEAAE0 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F4A627D4061900AEAAE0 /* ViewController.swift */; }; + 7A49F4AA27D4061900AEAAE0 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7A49F4A827D4061900AEAAE0 /* Main.storyboard */; }; + 7A49F4AC27D4061A00AEAAE0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7A49F4AB27D4061A00AEAAE0 /* Assets.xcassets */; }; + 7A49F4AF27D4061A00AEAAE0 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7A49F4AD27D4061A00AEAAE0 /* LaunchScreen.storyboard */; }; + 7A49F4BA27D4061B00AEAAE0 /* AutoCat2Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F4B927D4061B00AEAAE0 /* AutoCat2Tests.swift */; }; + 7A49F4C427D4061B00AEAAE0 /* AutoCat2UITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F4C327D4061B00AEAAE0 /* AutoCat2UITests.swift */; }; + 7A49F4C627D4061B00AEAAE0 /* AutoCat2UITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F4C527D4061B00AEAAE0 /* AutoCat2UITestsLaunchTests.swift */; }; + 7A49F4DB27D4064500AEAAE0 /* AutoCatCore.docc in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F4DA27D4064500AEAAE0 /* AutoCatCore.docc */; }; + 7A49F4E127D4064500AEAAE0 /* AutoCatCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A49F4D727D4064500AEAAE0 /* AutoCatCore.framework */; }; + 7A49F4E827D4064500AEAAE0 /* AutoCatCoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F4E727D4064500AEAAE0 /* AutoCatCoreTests.swift */; }; + 7A49F4E927D4064500AEAAE0 /* AutoCatCore.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A49F4D927D4064500AEAAE0 /* AutoCatCore.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7A49F4EC27D4064500AEAAE0 /* AutoCatCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A49F4D727D4064500AEAAE0 /* AutoCatCore.framework */; }; + 7A49F4ED27D4064500AEAAE0 /* AutoCatCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 7A49F4D727D4064500AEAAE0 /* AutoCatCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 7A49F4F927D406B200AEAAE0 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F4F627D406B200AEAAE0 /* Constants.swift */; }; + 7A49F4FA27D406B200AEAAE0 /* ApiError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F4F727D406B200AEAAE0 /* ApiError.swift */; }; + 7A49F4FB27D406B200AEAAE0 /* Api.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F4F827D406B200AEAAE0 /* Api.swift */; }; + 7A49F4FE27D406BA00AEAAE0 /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F4FD27D406BA00AEAAE0 /* AnyEncodable.swift */; }; + 7A49F50227D406C300AEAAE0 /* StorageService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F50027D406C300AEAAE0 /* StorageService.swift */; }; + 7A49F50327D406C300AEAAE0 /* VehicleService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F50127D406C300AEAAE0 /* VehicleService.swift */; }; + 7A49F50C27D406CB00AEAAE0 /* VBrand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F50527D406CB00AEAAE0 /* VBrand.swift */; }; + 7A49F50D27D406CB00AEAAE0 /* Vehicle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F50627D406CB00AEAAE0 /* Vehicle.swift */; }; + 7A49F50E27D406CB00AEAAE0 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F50727D406CB00AEAAE0 /* User.swift */; }; + 7A49F50F27D406CB00AEAAE0 /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F50827D406CB00AEAAE0 /* Response.swift */; }; + 7A49F51027D406CB00AEAAE0 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F50927D406CB00AEAAE0 /* Settings.swift */; }; + 7A49F51127D406CB00AEAAE0 /* PlateNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F50A27D406CB00AEAAE0 /* PlateNumber.swift */; }; + 7A49F51227D406CB00AEAAE0 /* VName.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F50B27D406CB00AEAAE0 /* VName.swift */; }; + 7A49F51527D40C6100AEAAE0 /* AutoCat2.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 7A49F51327D40C6100AEAAE0 /* AutoCat2.xcdatamodeld */; }; + 7A9F2AC327E71531006492A9 /* ACTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A9F2AC227E71531006492A9 /* ACTabBarController.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 7A40D58F2691C6D8009B0BC4 /* PBXContainerItemProxy */ = { + 7A49F4B627D4061B00AEAAE0 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 7A40D5742691C6D6009B0BC4 /* Project object */; + containerPortal = 7A49F49727D4061900AEAAE0 /* Project object */; proxyType = 1; - remoteGlobalIDString = 7A40D5822691C6D8009B0BC4; - remoteInfo = "AutoCat2 (iOS)"; + remoteGlobalIDString = 7A49F49E27D4061900AEAAE0; + remoteInfo = AutoCat2; }; - 7A40D5992691C6D8009B0BC4 /* PBXContainerItemProxy */ = { + 7A49F4C027D4061B00AEAAE0 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 7A40D5742691C6D6009B0BC4 /* Project object */; + containerPortal = 7A49F49727D4061900AEAAE0 /* Project object */; proxyType = 1; - remoteGlobalIDString = 7A40D5882691C6D8009B0BC4; - remoteInfo = "AutoCat2 (macOS)"; + remoteGlobalIDString = 7A49F49E27D4061900AEAAE0; + remoteInfo = AutoCat2; }; - 7A40D5F72693A63A009B0BC4 /* PBXContainerItemProxy */ = { + 7A49F4E227D4064500AEAAE0 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; - containerPortal = 7A40D5742691C6D6009B0BC4 /* Project object */; + containerPortal = 7A49F49727D4061900AEAAE0 /* Project object */; proxyType = 1; - remoteGlobalIDString = 7A40D5822691C6D8009B0BC4; - remoteInfo = "AutoCat2 (iOS)"; + remoteGlobalIDString = 7A49F4D627D4064500AEAAE0; + remoteInfo = AutoCatCore; + }; + 7A49F4E427D4064500AEAAE0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7A49F49727D4061900AEAAE0 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7A49F49E27D4061900AEAAE0; + remoteInfo = AutoCat2; + }; + 7A49F4EA27D4064500AEAAE0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7A49F49727D4061900AEAAE0 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 7A49F4D627D4064500AEAAE0; + remoteInfo = AutoCatCore; }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ - 7A40D5D52691C7D3009B0BC4 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; - 7A40D5DE2691C7EB009B0BC4 /* Embed Frameworks */ = { + 7A49F4F127D4064500AEAAE0 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( + 7A49F4ED27D4064500AEAAE0 /* AutoCatCore.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -134,437 +114,440 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 7A2329B826AF122400264CFA /* MainViewSmall.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewSmall.swift; sourceTree = ""; }; - 7A2329BB26AF123200264CFA /* MainViewBig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewBig.swift; sourceTree = ""; }; - 7A2329BE26AF140F00264CFA /* CheckView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckView.swift; sourceTree = ""; }; - 7A2329C126AF141E00264CFA /* SearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchView.swift; sourceTree = ""; }; - 7A2329C426AF142900264CFA /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; - 7A2329C726AF143F00264CFA /* RecordsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordsView.swift; sourceTree = ""; }; - 7A2329CA26AF15AC00264CFA /* ReportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportView.swift; sourceTree = ""; }; - 7A40D57A2691C6D7009B0BC4 /* Shared.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Shared.xcdatamodel; sourceTree = ""; }; - 7A40D57B2691C6D7009B0BC4 /* AutoCat2App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoCat2App.swift; sourceTree = ""; }; - 7A40D57C2691C6D7009B0BC4 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; - 7A40D57D2691C6D7009B0BC4 /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = ""; }; - 7A40D57E2691C6D7009B0BC4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 7A40D5832691C6D8009B0BC4 /* AutoCat2.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AutoCat2.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 7A40D5892691C6D8009B0BC4 /* AutoCat2.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AutoCat2.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 7A40D58E2691C6D8009B0BC4 /* Tests iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Tests iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - 7A40D5922691C6D8009B0BC4 /* Tests_iOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_iOS.swift; sourceTree = ""; }; - 7A40D5982691C6D8009B0BC4 /* Tests macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Tests macOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - 7A40D59C2691C6D8009B0BC4 /* Tests_macOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_macOS.swift; sourceTree = ""; }; - 7A40D5E026924AEC009B0BC4 /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; - 7A40D5E226924B09009B0BC4 /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = ""; }; - 7A40D5E826938BEC009B0BC4 /* AuthView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthView.swift; sourceTree = ""; }; - 7A40D5EC2693A1EA009B0BC4 /* AuthVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthVM.swift; sourceTree = ""; }; - 7A40D5F32693A63A009B0BC4 /* AutoCat2Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AutoCat2Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 7A40D5F52693A63A009B0BC4 /* SettingsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTests.swift; sourceTree = ""; }; - 7A40D5FD2693A91F009B0BC4 /* CocoaError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CocoaError.swift; sourceTree = ""; }; - 7A40D6012694FF5D009B0BC4 /* Api.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Api.swift; sourceTree = ""; }; - 7A40D60726998DCF009B0BC4 /* Alert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alert.swift; sourceTree = ""; }; - 7A40D60B2699A070009B0BC4 /* MockURLProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockURLProtocol.swift; sourceTree = ""; }; - 7A503C02269F382F002C1A0D /* login_success.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = login_success.json; sourceTree = ""; }; - 7A683998269612EA00B2188A /* Response.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Response.swift; sourceTree = ""; }; - 7A971F0526AD6F2F007E527B /* ApiMethodMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiMethodMock.swift; sourceTree = ""; }; - 7A971F0726AD7084007E527B /* LoginMethodMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginMethodMock.swift; sourceTree = ""; }; - 7A971F0926AD74FD007E527B /* ApiMethodMockProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiMethodMockProtocol.swift; sourceTree = ""; }; - 7A971F0C26AD7D4C007E527B /* AnyEncodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnyEncodable.swift; sourceTree = ""; }; - 7A971F1F26ADC351007E527B /* ApiError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiError.swift; sourceTree = ""; }; - 7A971F2226ADF74B007E527B /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = ""; }; - 7A971F2526ADFD35007E527B /* AutoCat2 (macOS).entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "AutoCat2 (macOS).entitlements"; sourceTree = ""; }; - 7AAEBD34274A47C500230C28 /* PlateNumberView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlateNumberView.swift; sourceTree = ""; }; - 7ABC05A726B05AF9004BB5A7 /* Vehicle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Vehicle.swift; sourceTree = ""; }; - 7ACD05D62695C08A00557667 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; - 7ADE63702735E1FF00A152FE /* VName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VName.swift; sourceTree = ""; }; - 7ADE63722735E27200A152FE /* VBrand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VBrand.swift; sourceTree = ""; }; - 7ADE637727368C4600A152FE /* VehicleService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleService.swift; sourceTree = ""; }; - 7ADE637A273692F300A152FE /* StorageService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageService.swift; sourceTree = ""; }; - 7ADE637E2738143000A152FE /* CheckNumberPopup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckNumberPopup.swift; sourceTree = ""; }; - 7AEFAEEC26985A3400ED2C85 /* ACProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ACProgressView.swift; sourceTree = ""; }; - 7AF552D82696E5C100578083 /* ApiTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiTests.swift; sourceTree = ""; }; - 7AFADDA6274A49DB00D53EFC /* PlateNumber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlateNumber.swift; sourceTree = ""; }; - 7AFADDAD274A64DD00D53EFC /* RoadNumbers.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = RoadNumbers.otf; sourceTree = ""; }; - 7AFADDAE274A64DD00D53EFC /* RoadNumbers2.0.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = RoadNumbers2.0.otf; sourceTree = ""; }; - 7AFADDB3274A65BB00D53EFC /* AutoCat2--macOS--Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "AutoCat2--macOS--Info.plist"; sourceTree = ""; }; - 7AFADDB4274A678D00D53EFC /* AutoCat2--iOS--Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "AutoCat2--iOS--Info.plist"; sourceTree = ""; }; + 6841A0A6580C6E9FF26DC6E2 /* HistoryController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HistoryController.swift; sourceTree = ""; }; + 6841A1A12194A8FA89FB52F6 /* SwiftMaskTextfield.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftMaskTextfield.swift; sourceTree = ""; }; + 6841A3368FA7CA658C3EEABB /* MainTabController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainTabController.swift; sourceTree = ""; }; + 6841A5A586BC03AA21D73DF4 /* SettingsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsController.swift; sourceTree = ""; }; + 6841A709FA6B606978425A26 /* UIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = ""; }; + 6841A850CF48F0715B63D998 /* ACTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ACTextField.swift; sourceTree = ""; }; + 6841AB0052E9DB6914901EA3 /* PNKeyboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PNKeyboard.swift; sourceTree = ""; }; + 6841ABEA0314E3B4E438C311 /* AuthController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthController.swift; sourceTree = ""; }; + 6841ADEEC165FA9124C5EF40 /* UITextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UITextField.swift; sourceTree = ""; }; + 6841AFB465BF16E122875D9A /* ACButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ACButton.swift; sourceTree = ""; }; + 6841AFE790F6FC06838B1E2C /* UIControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIControl.swift; sourceTree = ""; }; + 7A24C19527EE212E00049E7F /* RoadNumbers.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = RoadNumbers.otf; sourceTree = ""; }; + 7A24C19627EE212E00049E7F /* RoadNumbers2.0.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = RoadNumbers2.0.otf; sourceTree = ""; }; + 7A24C19A27EE25B400049E7F /* PlateView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlateView.swift; sourceTree = ""; }; + 7A24C19B27EE25B400049E7F /* FlagLayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlagLayer.swift; sourceTree = ""; }; + 7A24C19E27EE26B900049E7F /* CenterTextLayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CenterTextLayer.swift; sourceTree = ""; }; + 7A28282E27E720AC0049BDBF /* ACTabBarButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ACTabBarButton.swift; sourceTree = ""; }; + 7A28283027E721A70049BDBF /* UIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = ""; }; + 7A28283227E7263B0049BDBF /* UIStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIStackView.swift; sourceTree = ""; }; + 7A28283727E74D930049BDBF /* CheckController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckController.swift; sourceTree = ""; }; + 7A49F49F27D4061900AEAAE0 /* AutoCat2.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AutoCat2.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 7A49F4A227D4061900AEAAE0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7A49F4A427D4061900AEAAE0 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 7A49F4A627D4061900AEAAE0 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 7A49F4A927D4061900AEAAE0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 7A49F4AB27D4061A00AEAAE0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 7A49F4AE27D4061A00AEAAE0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 7A49F4B027D4061A00AEAAE0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 7A49F4B527D4061B00AEAAE0 /* AutoCat2Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AutoCat2Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 7A49F4B927D4061B00AEAAE0 /* AutoCat2Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoCat2Tests.swift; sourceTree = ""; }; + 7A49F4BF27D4061B00AEAAE0 /* AutoCat2UITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AutoCat2UITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 7A49F4C327D4061B00AEAAE0 /* AutoCat2UITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoCat2UITests.swift; sourceTree = ""; }; + 7A49F4C527D4061B00AEAAE0 /* AutoCat2UITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoCat2UITestsLaunchTests.swift; sourceTree = ""; }; + 7A49F4D727D4064500AEAAE0 /* AutoCatCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AutoCatCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 7A49F4D927D4064500AEAAE0 /* AutoCatCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AutoCatCore.h; sourceTree = ""; }; + 7A49F4DA27D4064500AEAAE0 /* AutoCatCore.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = AutoCatCore.docc; sourceTree = ""; }; + 7A49F4E027D4064500AEAAE0 /* AutoCatCoreTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AutoCatCoreTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 7A49F4E727D4064500AEAAE0 /* AutoCatCoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoCatCoreTests.swift; sourceTree = ""; }; + 7A49F4F627D406B200AEAAE0 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; + 7A49F4F727D406B200AEAAE0 /* ApiError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApiError.swift; sourceTree = ""; }; + 7A49F4F827D406B200AEAAE0 /* Api.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Api.swift; sourceTree = ""; }; + 7A49F4FD27D406BA00AEAAE0 /* AnyEncodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnyEncodable.swift; sourceTree = ""; }; + 7A49F50027D406C300AEAAE0 /* StorageService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StorageService.swift; sourceTree = ""; }; + 7A49F50127D406C300AEAAE0 /* VehicleService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VehicleService.swift; sourceTree = ""; }; + 7A49F50527D406CB00AEAAE0 /* VBrand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VBrand.swift; sourceTree = ""; }; + 7A49F50627D406CB00AEAAE0 /* Vehicle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Vehicle.swift; sourceTree = ""; }; + 7A49F50727D406CB00AEAAE0 /* User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; }; + 7A49F50827D406CB00AEAAE0 /* Response.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Response.swift; sourceTree = ""; }; + 7A49F50927D406CB00AEAAE0 /* Settings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = ""; }; + 7A49F50A27D406CB00AEAAE0 /* PlateNumber.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlateNumber.swift; sourceTree = ""; }; + 7A49F50B27D406CB00AEAAE0 /* VName.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VName.swift; sourceTree = ""; }; + 7A49F51427D40C6100AEAAE0 /* Shared.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Shared.xcdatamodel; sourceTree = ""; }; + 7A9F2AC227E71531006492A9 /* ACTabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ACTabBarController.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 7A40D5802691C6D8009B0BC4 /* Frameworks */ = { + 7A49F49C27D4061900AEAAE0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 7A49F4EC27D4064500AEAAE0 /* AutoCatCore.framework in Frameworks */, + 7A48B26727D9442A004D1A4B /* PKHUD in Frameworks */, + 7A28283627E74C110049BDBF /* SwiftEntryKit in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7A49F4B227D4061B00AEAAE0 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; - 7A40D5862691C6D8009B0BC4 /* Frameworks */ = { + 7A49F4BC27D4061B00AEAAE0 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; - 7A40D58B2691C6D8009B0BC4 /* Frameworks */ = { + 7A49F4D427D4064500AEAAE0 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; - 7A40D5952691C6D8009B0BC4 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 7A40D5F02693A63A009B0BC4 /* Frameworks */ = { + 7A49F4DD27D4064500AEAAE0 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 7A49F4E127D4064500AEAAE0 /* AutoCatCore.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 7A40D5732691C6D6009B0BC4 = { + 6841A0EB55A59DAC94129594 /* Extensions */ = { isa = PBXGroup; children = ( - 7AFADDB4274A678D00D53EFC /* AutoCat2--iOS--Info.plist */, - 7AFADDB3274A65BB00D53EFC /* AutoCat2--macOS--Info.plist */, - 7A971F2526ADFD35007E527B /* AutoCat2 (macOS).entitlements */, - 7A40D5782691C6D7009B0BC4 /* Shared */, - 7A40D5912691C6D8009B0BC4 /* Tests iOS */, - 7A40D59B2691C6D8009B0BC4 /* Tests macOS */, - 7A40D5F42693A63A009B0BC4 /* AutoCat2Tests */, - 7A40D5842691C6D8009B0BC4 /* Products */, - ); - sourceTree = ""; - }; - 7A40D5782691C6D7009B0BC4 /* Shared */ = { - isa = PBXGroup; - children = ( - 7AFADDAC274A64DD00D53EFC /* Fonts */, - 7ADE637627368C2A00A152FE /* Services */, - 7A971F0B26AD7D27007E527B /* ThirdParty */, - 7A40D6002694FF4C009B0BC4 /* Utils */, - 7A40D5FC2693A90F009B0BC4 /* Extensions */, - 7A40D5EB2693A1C3009B0BC4 /* ViewModels */, - 7A40D5E726938BC8009B0BC4 /* Views */, - 7A40D5E626924BAA009B0BC4 /* Property Wrappers */, - 7A40D5DF26924ADE009B0BC4 /* Models */, - 7A40D57B2691C6D7009B0BC4 /* AutoCat2App.swift */, - 7A40D57D2691C6D7009B0BC4 /* Persistence.swift */, - 7A40D57E2691C6D7009B0BC4 /* Assets.xcassets */, - 7A40D5792691C6D7009B0BC4 /* AutoCat2.xcdatamodeld */, - ); - path = Shared; - sourceTree = ""; - }; - 7A40D5842691C6D8009B0BC4 /* Products */ = { - isa = PBXGroup; - children = ( - 7A40D5832691C6D8009B0BC4 /* AutoCat2.app */, - 7A40D5892691C6D8009B0BC4 /* AutoCat2.app */, - 7A40D58E2691C6D8009B0BC4 /* Tests iOS.xctest */, - 7A40D5982691C6D8009B0BC4 /* Tests macOS.xctest */, - 7A40D5F32693A63A009B0BC4 /* AutoCat2Tests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - 7A40D5912691C6D8009B0BC4 /* Tests iOS */ = { - isa = PBXGroup; - children = ( - 7A40D5922691C6D8009B0BC4 /* Tests_iOS.swift */, - ); - path = "Tests iOS"; - sourceTree = ""; - }; - 7A40D59B2691C6D8009B0BC4 /* Tests macOS */ = { - isa = PBXGroup; - children = ( - 7A40D59C2691C6D8009B0BC4 /* Tests_macOS.swift */, - ); - path = "Tests macOS"; - sourceTree = ""; - }; - 7A40D5DF26924ADE009B0BC4 /* Models */ = { - isa = PBXGroup; - children = ( - 7A40D5E026924AEC009B0BC4 /* User.swift */, - 7A40D5E226924B09009B0BC4 /* Settings.swift */, - 7A683998269612EA00B2188A /* Response.swift */, - 7ABC05A726B05AF9004BB5A7 /* Vehicle.swift */, - 7ADE63702735E1FF00A152FE /* VName.swift */, - 7ADE63722735E27200A152FE /* VBrand.swift */, - 7AFADDA6274A49DB00D53EFC /* PlateNumber.swift */, - ); - path = Models; - sourceTree = ""; - }; - 7A40D5E626924BAA009B0BC4 /* Property Wrappers */ = { - isa = PBXGroup; - children = ( - ); - path = "Property Wrappers"; - sourceTree = ""; - }; - 7A40D5E726938BC8009B0BC4 /* Views */ = { - isa = PBXGroup; - children = ( - 7ADE637D2738139D00A152FE /* macOS */, - 7AEFAEEC26985A3400ED2C85 /* ACProgressView.swift */, - 7A2329BE26AF140F00264CFA /* CheckView.swift */, - 7A40D57C2691C6D7009B0BC4 /* ContentView.swift */, - 7A40D5E826938BEC009B0BC4 /* AuthView.swift */, - 7A971F2226ADF74B007E527B /* MainView.swift */, - 7A2329B826AF122400264CFA /* MainViewSmall.swift */, - 7A2329C126AF141E00264CFA /* SearchView.swift */, - 7A2329C426AF142900264CFA /* SettingsView.swift */, - 7A2329C726AF143F00264CFA /* RecordsView.swift */, - 7A2329CA26AF15AC00264CFA /* ReportView.swift */, - 7AAEBD34274A47C500230C28 /* PlateNumberView.swift */, - ); - path = Views; - sourceTree = ""; - }; - 7A40D5EB2693A1C3009B0BC4 /* ViewModels */ = { - isa = PBXGroup; - children = ( - 7A40D5EC2693A1EA009B0BC4 /* AuthVM.swift */, - ); - path = ViewModels; - sourceTree = ""; - }; - 7A40D5F42693A63A009B0BC4 /* AutoCat2Tests */ = { - isa = PBXGroup; - children = ( - 7A971F0326AD6EA1007E527B /* Api */, - 7A40D5F52693A63A009B0BC4 /* SettingsTests.swift */, - 7AF552D82696E5C100578083 /* ApiTests.swift */, - ); - path = AutoCat2Tests; - sourceTree = ""; - }; - 7A40D5FC2693A90F009B0BC4 /* Extensions */ = { - isa = PBXGroup; - children = ( - 7A40D5FD2693A91F009B0BC4 /* CocoaError.swift */, - 7A40D60726998DCF009B0BC4 /* Alert.swift */, + 6841AFE790F6FC06838B1E2C /* UIControl.swift */, + 6841ADEEC165FA9124C5EF40 /* UITextField.swift */, + 6841A709FA6B606978425A26 /* UIViewController.swift */, + 7A28283027E721A70049BDBF /* UIView.swift */, + 7A28283227E7263B0049BDBF /* UIStackView.swift */, ); path = Extensions; sourceTree = ""; }; - 7A40D6002694FF4C009B0BC4 /* Utils */ = { + 6841A450B0D0A7DB09CC746A /* ThirdParty */ = { isa = PBXGroup; children = ( - 7A40D6012694FF5D009B0BC4 /* Api.swift */, - 7A971F1F26ADC351007E527B /* ApiError.swift */, - 7ACD05D62695C08A00557667 /* Constants.swift */, - ); - path = Utils; - sourceTree = ""; - }; - 7A40D60A2699A04F009B0BC4 /* Mocks */ = { - isa = PBXGroup; - children = ( - 7A971F0526AD6F2F007E527B /* ApiMethodMock.swift */, - 7A971F0726AD7084007E527B /* LoginMethodMock.swift */, - ); - path = Mocks; - sourceTree = ""; - }; - 7A503C00269F370A002C1A0D /* Responses */ = { - isa = PBXGroup; - children = ( - 7A503C02269F382F002C1A0D /* login_success.json */, - ); - path = Responses; - sourceTree = ""; - }; - 7A971F0326AD6EA1007E527B /* Api */ = { - isa = PBXGroup; - children = ( - 7A971F0426AD6ED6007E527B /* Lib */, - 7A40D60A2699A04F009B0BC4 /* Mocks */, - 7A503C00269F370A002C1A0D /* Responses */, - ); - path = Api; - sourceTree = ""; - }; - 7A971F0426AD6ED6007E527B /* Lib */ = { - isa = PBXGroup; - children = ( - 7A40D60B2699A070009B0BC4 /* MockURLProtocol.swift */, - 7A971F0926AD74FD007E527B /* ApiMethodMockProtocol.swift */, - ); - path = Lib; - sourceTree = ""; - }; - 7A971F0B26AD7D27007E527B /* ThirdParty */ = { - isa = PBXGroup; - children = ( - 7A971F0C26AD7D4C007E527B /* AnyEncodable.swift */, + 7A24C19E27EE26B900049E7F /* CenterTextLayer.swift */, + 6841A1A12194A8FA89FB52F6 /* SwiftMaskTextfield.swift */, ); path = ThirdParty; sourceTree = ""; }; - 7ADE637627368C2A00A152FE /* Services */ = { + 6841A5B816D903B6AC0E8FDD /* Controllers */ = { isa = PBXGroup; children = ( - 7ADE637727368C4600A152FE /* VehicleService.swift */, - 7ADE637A273692F300A152FE /* StorageService.swift */, + 6841ABEA0314E3B4E438C311 /* AuthController.swift */, + 6841A3368FA7CA658C3EEABB /* MainTabController.swift */, + 6841A0A6580C6E9FF26DC6E2 /* HistoryController.swift */, + 6841A5A586BC03AA21D73DF4 /* SettingsController.swift */, + 7A28283727E74D930049BDBF /* CheckController.swift */, ); - path = Services; + path = Controllers; sourceTree = ""; }; - 7ADE637D2738139D00A152FE /* macOS */ = { + 6841AEAC436660C91E968C9F /* Components */ = { isa = PBXGroup; children = ( - 7A2329BB26AF123200264CFA /* MainViewBig.swift */, - 7ADE637E2738143000A152FE /* CheckNumberPopup.swift */, + 7A24C19927EE259000049E7F /* PlateView */, + 7A28282D27E720830049BDBF /* ACTabBar */, + 6841A0EB55A59DAC94129594 /* Extensions */, + 6841AFB465BF16E122875D9A /* ACButton.swift */, + 6841A850CF48F0715B63D998 /* ACTextField.swift */, + 6841AB0052E9DB6914901EA3 /* PNKeyboard.swift */, ); - path = macOS; + path = Components; sourceTree = ""; }; - 7AFADDAC274A64DD00D53EFC /* Fonts */ = { + 7A24C19427EE212E00049E7F /* Fonts */ = { isa = PBXGroup; children = ( - 7AFADDAD274A64DD00D53EFC /* RoadNumbers.otf */, - 7AFADDAE274A64DD00D53EFC /* RoadNumbers2.0.otf */, + 7A24C19527EE212E00049E7F /* RoadNumbers.otf */, + 7A24C19627EE212E00049E7F /* RoadNumbers2.0.otf */, ); path = Fonts; sourceTree = ""; }; + 7A24C19927EE259000049E7F /* PlateView */ = { + isa = PBXGroup; + children = ( + 7A24C19B27EE25B400049E7F /* FlagLayer.swift */, + 7A24C19A27EE25B400049E7F /* PlateView.swift */, + ); + path = PlateView; + sourceTree = ""; + }; + 7A28282D27E720830049BDBF /* ACTabBar */ = { + isa = PBXGroup; + children = ( + 7A9F2AC227E71531006492A9 /* ACTabBarController.swift */, + 7A28282E27E720AC0049BDBF /* ACTabBarButton.swift */, + ); + path = ACTabBar; + sourceTree = ""; + }; + 7A49F49627D4061900AEAAE0 = { + isa = PBXGroup; + children = ( + 7A49F4A127D4061900AEAAE0 /* AutoCat2 */, + 7A49F4B827D4061B00AEAAE0 /* AutoCat2Tests */, + 7A49F4C227D4061B00AEAAE0 /* AutoCat2UITests */, + 7A49F4D827D4064500AEAAE0 /* AutoCatCore */, + 7A49F4E627D4064500AEAAE0 /* AutoCatCoreTests */, + 7A49F4A027D4061900AEAAE0 /* Products */, + ); + sourceTree = ""; + }; + 7A49F4A027D4061900AEAAE0 /* Products */ = { + isa = PBXGroup; + children = ( + 7A49F49F27D4061900AEAAE0 /* AutoCat2.app */, + 7A49F4B527D4061B00AEAAE0 /* AutoCat2Tests.xctest */, + 7A49F4BF27D4061B00AEAAE0 /* AutoCat2UITests.xctest */, + 7A49F4D727D4064500AEAAE0 /* AutoCatCore.framework */, + 7A49F4E027D4064500AEAAE0 /* AutoCatCoreTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 7A49F4A127D4061900AEAAE0 /* AutoCat2 */ = { + isa = PBXGroup; + children = ( + 7A24C19427EE212E00049E7F /* Fonts */, + 7A49F4A227D4061900AEAAE0 /* AppDelegate.swift */, + 7A49F4A427D4061900AEAAE0 /* SceneDelegate.swift */, + 7A49F4A627D4061900AEAAE0 /* ViewController.swift */, + 7A49F4A827D4061900AEAAE0 /* Main.storyboard */, + 7A49F4AB27D4061A00AEAAE0 /* Assets.xcassets */, + 7A49F4AD27D4061A00AEAAE0 /* LaunchScreen.storyboard */, + 7A49F4B027D4061A00AEAAE0 /* Info.plist */, + 6841A5B816D903B6AC0E8FDD /* Controllers */, + 6841AEAC436660C91E968C9F /* Components */, + 6841A450B0D0A7DB09CC746A /* ThirdParty */, + ); + path = AutoCat2; + sourceTree = ""; + }; + 7A49F4B827D4061B00AEAAE0 /* AutoCat2Tests */ = { + isa = PBXGroup; + children = ( + 7A49F4B927D4061B00AEAAE0 /* AutoCat2Tests.swift */, + ); + path = AutoCat2Tests; + sourceTree = ""; + }; + 7A49F4C227D4061B00AEAAE0 /* AutoCat2UITests */ = { + isa = PBXGroup; + children = ( + 7A49F4C327D4061B00AEAAE0 /* AutoCat2UITests.swift */, + 7A49F4C527D4061B00AEAAE0 /* AutoCat2UITestsLaunchTests.swift */, + ); + path = AutoCat2UITests; + sourceTree = ""; + }; + 7A49F4D827D4064500AEAAE0 /* AutoCatCore */ = { + isa = PBXGroup; + children = ( + 7A49F51327D40C6100AEAAE0 /* AutoCat2.xcdatamodeld */, + 7A49F50427D406CB00AEAAE0 /* Models */, + 7A49F4FF27D406C300AEAAE0 /* Services */, + 7A49F4FC27D406BA00AEAAE0 /* ThirdParty */, + 7A49F4F527D406B200AEAAE0 /* Utils */, + 7A49F4D927D4064500AEAAE0 /* AutoCatCore.h */, + 7A49F4DA27D4064500AEAAE0 /* AutoCatCore.docc */, + ); + path = AutoCatCore; + sourceTree = ""; + }; + 7A49F4E627D4064500AEAAE0 /* AutoCatCoreTests */ = { + isa = PBXGroup; + children = ( + 7A49F4E727D4064500AEAAE0 /* AutoCatCoreTests.swift */, + ); + path = AutoCatCoreTests; + sourceTree = ""; + }; + 7A49F4F527D406B200AEAAE0 /* Utils */ = { + isa = PBXGroup; + children = ( + 7A49F4F627D406B200AEAAE0 /* Constants.swift */, + 7A49F4F727D406B200AEAAE0 /* ApiError.swift */, + 7A49F4F827D406B200AEAAE0 /* Api.swift */, + ); + path = Utils; + sourceTree = ""; + }; + 7A49F4FC27D406BA00AEAAE0 /* ThirdParty */ = { + isa = PBXGroup; + children = ( + 7A49F4FD27D406BA00AEAAE0 /* AnyEncodable.swift */, + ); + path = ThirdParty; + sourceTree = ""; + }; + 7A49F4FF27D406C300AEAAE0 /* Services */ = { + isa = PBXGroup; + children = ( + 7A49F50027D406C300AEAAE0 /* StorageService.swift */, + 7A49F50127D406C300AEAAE0 /* VehicleService.swift */, + ); + path = Services; + sourceTree = ""; + }; + 7A49F50427D406CB00AEAAE0 /* Models */ = { + isa = PBXGroup; + children = ( + 7A49F50527D406CB00AEAAE0 /* VBrand.swift */, + 7A49F50627D406CB00AEAAE0 /* Vehicle.swift */, + 7A49F50727D406CB00AEAAE0 /* User.swift */, + 7A49F50827D406CB00AEAAE0 /* Response.swift */, + 7A49F50927D406CB00AEAAE0 /* Settings.swift */, + 7A49F50A27D406CB00AEAAE0 /* PlateNumber.swift */, + 7A49F50B27D406CB00AEAAE0 /* VName.swift */, + ); + path = Models; + sourceTree = ""; + }; /* End PBXGroup section */ +/* Begin PBXHeadersBuildPhase section */ + 7A49F4D227D4064500AEAAE0 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 7A49F4E927D4064500AEAAE0 /* AutoCatCore.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + /* Begin PBXNativeTarget section */ - 7A40D5822691C6D8009B0BC4 /* AutoCat2 (iOS) */ = { + 7A49F49E27D4061900AEAAE0 /* AutoCat2 */ = { isa = PBXNativeTarget; - buildConfigurationList = 7A40D5AA2691C6D8009B0BC4 /* Build configuration list for PBXNativeTarget "AutoCat2 (iOS)" */; + buildConfigurationList = 7A49F4C927D4061B00AEAAE0 /* Build configuration list for PBXNativeTarget "AutoCat2" */; buildPhases = ( - 7A40D57F2691C6D8009B0BC4 /* Sources */, - 7A40D5802691C6D8009B0BC4 /* Frameworks */, - 7A40D5812691C6D8009B0BC4 /* Resources */, - 7A40D5D52691C7D3009B0BC4 /* Embed Frameworks */, + 7A49F49B27D4061900AEAAE0 /* Sources */, + 7A49F49C27D4061900AEAAE0 /* Frameworks */, + 7A49F49D27D4061900AEAAE0 /* Resources */, + 7A49F4F127D4064500AEAAE0 /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( + 7A49F4EB27D4064500AEAAE0 /* PBXTargetDependency */, ); - name = "AutoCat2 (iOS)"; + name = AutoCat2; packageProductDependencies = ( + 7A48B26627D9442A004D1A4B /* PKHUD */, + 7A28283527E74C110049BDBF /* SwiftEntryKit */, ); - productName = "AutoCat2 (iOS)"; - productReference = 7A40D5832691C6D8009B0BC4 /* AutoCat2.app */; + productName = AutoCat2; + productReference = 7A49F49F27D4061900AEAAE0 /* AutoCat2.app */; productType = "com.apple.product-type.application"; }; - 7A40D5882691C6D8009B0BC4 /* AutoCat2 (macOS) */ = { + 7A49F4B427D4061B00AEAAE0 /* AutoCat2Tests */ = { isa = PBXNativeTarget; - buildConfigurationList = 7A40D5AD2691C6D8009B0BC4 /* Build configuration list for PBXNativeTarget "AutoCat2 (macOS)" */; + buildConfigurationList = 7A49F4CC27D4061B00AEAAE0 /* Build configuration list for PBXNativeTarget "AutoCat2Tests" */; buildPhases = ( - 7A40D5852691C6D8009B0BC4 /* Sources */, - 7A40D5862691C6D8009B0BC4 /* Frameworks */, - 7A40D5872691C6D8009B0BC4 /* Resources */, - 7A40D5DE2691C7EB009B0BC4 /* Embed Frameworks */, + 7A49F4B127D4061B00AEAAE0 /* Sources */, + 7A49F4B227D4061B00AEAAE0 /* Frameworks */, + 7A49F4B327D4061B00AEAAE0 /* Resources */, ); buildRules = ( ); dependencies = ( - ); - name = "AutoCat2 (macOS)"; - packageProductDependencies = ( - ); - productName = "AutoCat2 (macOS)"; - productReference = 7A40D5892691C6D8009B0BC4 /* AutoCat2.app */; - productType = "com.apple.product-type.application"; - }; - 7A40D58D2691C6D8009B0BC4 /* Tests iOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = 7A40D5B02691C6D8009B0BC4 /* Build configuration list for PBXNativeTarget "Tests iOS" */; - buildPhases = ( - 7A40D58A2691C6D8009B0BC4 /* Sources */, - 7A40D58B2691C6D8009B0BC4 /* Frameworks */, - 7A40D58C2691C6D8009B0BC4 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 7A40D5902691C6D8009B0BC4 /* PBXTargetDependency */, - ); - name = "Tests iOS"; - productName = "Tests iOS"; - productReference = 7A40D58E2691C6D8009B0BC4 /* Tests iOS.xctest */; - productType = "com.apple.product-type.bundle.ui-testing"; - }; - 7A40D5972691C6D8009B0BC4 /* Tests macOS */ = { - isa = PBXNativeTarget; - buildConfigurationList = 7A40D5B32691C6D8009B0BC4 /* Build configuration list for PBXNativeTarget "Tests macOS" */; - buildPhases = ( - 7A40D5942691C6D8009B0BC4 /* Sources */, - 7A40D5952691C6D8009B0BC4 /* Frameworks */, - 7A40D5962691C6D8009B0BC4 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 7A40D59A2691C6D8009B0BC4 /* PBXTargetDependency */, - ); - name = "Tests macOS"; - productName = "Tests macOS"; - productReference = 7A40D5982691C6D8009B0BC4 /* Tests macOS.xctest */; - productType = "com.apple.product-type.bundle.ui-testing"; - }; - 7A40D5F22693A63A009B0BC4 /* AutoCat2Tests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 7A40D5F92693A63A009B0BC4 /* Build configuration list for PBXNativeTarget "AutoCat2Tests" */; - buildPhases = ( - 7A40D5EF2693A63A009B0BC4 /* Sources */, - 7A40D5F02693A63A009B0BC4 /* Frameworks */, - 7A40D5F12693A63A009B0BC4 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 7A40D5F82693A63A009B0BC4 /* PBXTargetDependency */, + 7A49F4B727D4061B00AEAAE0 /* PBXTargetDependency */, ); name = AutoCat2Tests; productName = AutoCat2Tests; - productReference = 7A40D5F32693A63A009B0BC4 /* AutoCat2Tests.xctest */; + productReference = 7A49F4B527D4061B00AEAAE0 /* AutoCat2Tests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 7A49F4BE27D4061B00AEAAE0 /* AutoCat2UITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7A49F4CF27D4061B00AEAAE0 /* Build configuration list for PBXNativeTarget "AutoCat2UITests" */; + buildPhases = ( + 7A49F4BB27D4061B00AEAAE0 /* Sources */, + 7A49F4BC27D4061B00AEAAE0 /* Frameworks */, + 7A49F4BD27D4061B00AEAAE0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 7A49F4C127D4061B00AEAAE0 /* PBXTargetDependency */, + ); + name = AutoCat2UITests; + productName = AutoCat2UITests; + productReference = 7A49F4BF27D4061B00AEAAE0 /* AutoCat2UITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; + 7A49F4D627D4064500AEAAE0 /* AutoCatCore */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7A49F4EE27D4064500AEAAE0 /* Build configuration list for PBXNativeTarget "AutoCatCore" */; + buildPhases = ( + 7A49F4D227D4064500AEAAE0 /* Headers */, + 7A49F4D327D4064500AEAAE0 /* Sources */, + 7A49F4D427D4064500AEAAE0 /* Frameworks */, + 7A49F4D527D4064500AEAAE0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = AutoCatCore; + productName = AutoCatCore; + productReference = 7A49F4D727D4064500AEAAE0 /* AutoCatCore.framework */; + productType = "com.apple.product-type.framework"; + }; + 7A49F4DF27D4064500AEAAE0 /* AutoCatCoreTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7A49F4F227D4064500AEAAE0 /* Build configuration list for PBXNativeTarget "AutoCatCoreTests" */; + buildPhases = ( + 7A49F4DC27D4064500AEAAE0 /* Sources */, + 7A49F4DD27D4064500AEAAE0 /* Frameworks */, + 7A49F4DE27D4064500AEAAE0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 7A49F4E327D4064500AEAAE0 /* PBXTargetDependency */, + 7A49F4E527D4064500AEAAE0 /* PBXTargetDependency */, + ); + name = AutoCatCoreTests; + productName = AutoCatCoreTests; + productReference = 7A49F4E027D4064500AEAAE0 /* AutoCatCoreTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ - 7A40D5742691C6D6009B0BC4 /* Project object */ = { + 7A49F49727D4061900AEAAE0 /* Project object */ = { isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1300; - LastUpgradeCheck = 1310; + LastSwiftUpdateCheck = 1320; + LastUpgradeCheck = 1320; TargetAttributes = { - 7A40D5822691C6D8009B0BC4 = { - CreatedOnToolsVersion = 13.0; + 7A49F49E27D4061900AEAAE0 = { + CreatedOnToolsVersion = 13.2.1; }; - 7A40D5882691C6D8009B0BC4 = { - CreatedOnToolsVersion = 13.0; + 7A49F4B427D4061B00AEAAE0 = { + CreatedOnToolsVersion = 13.2.1; + TestTargetID = 7A49F49E27D4061900AEAAE0; }; - 7A40D58D2691C6D8009B0BC4 = { - CreatedOnToolsVersion = 13.0; - TestTargetID = 7A40D5822691C6D8009B0BC4; + 7A49F4BE27D4061B00AEAAE0 = { + CreatedOnToolsVersion = 13.2.1; + TestTargetID = 7A49F49E27D4061900AEAAE0; }; - 7A40D5972691C6D8009B0BC4 = { - CreatedOnToolsVersion = 13.0; - TestTargetID = 7A40D5882691C6D8009B0BC4; + 7A49F4D627D4064500AEAAE0 = { + CreatedOnToolsVersion = 13.2.1; }; - 7A40D5F22693A63A009B0BC4 = { - CreatedOnToolsVersion = 13.0; - TestTargetID = 7A40D5822691C6D8009B0BC4; + 7A49F4DF27D4064500AEAAE0 = { + CreatedOnToolsVersion = 13.2.1; + TestTargetID = 7A49F49E27D4061900AEAAE0; }; }; }; - buildConfigurationList = 7A40D5772691C6D6009B0BC4 /* Build configuration list for PBXProject "AutoCat2" */; + buildConfigurationList = 7A49F49A27D4061900AEAAE0 /* Build configuration list for PBXProject "AutoCat2" */; compatibilityVersion = "Xcode 13.0"; developmentRegion = en; hasScannedForEncodings = 0; @@ -572,201 +555,201 @@ en, Base, ); - mainGroup = 7A40D5732691C6D6009B0BC4; + mainGroup = 7A49F49627D4061900AEAAE0; packageReferences = ( + 7A48B26527D9442A004D1A4B /* XCRemoteSwiftPackageReference "PKHUD" */, + 7A28283427E74C110049BDBF /* XCRemoteSwiftPackageReference "SwiftEntryKit" */, ); - productRefGroup = 7A40D5842691C6D8009B0BC4 /* Products */; + productRefGroup = 7A49F4A027D4061900AEAAE0 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( - 7A40D5822691C6D8009B0BC4 /* AutoCat2 (iOS) */, - 7A40D5882691C6D8009B0BC4 /* AutoCat2 (macOS) */, - 7A40D58D2691C6D8009B0BC4 /* Tests iOS */, - 7A40D5972691C6D8009B0BC4 /* Tests macOS */, - 7A40D5F22693A63A009B0BC4 /* AutoCat2Tests */, + 7A49F49E27D4061900AEAAE0 /* AutoCat2 */, + 7A49F4B427D4061B00AEAAE0 /* AutoCat2Tests */, + 7A49F4BE27D4061B00AEAAE0 /* AutoCat2UITests */, + 7A49F4D627D4064500AEAAE0 /* AutoCatCore */, + 7A49F4DF27D4064500AEAAE0 /* AutoCatCoreTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 7A40D5812691C6D8009B0BC4 /* Resources */ = { + 7A49F49D27D4061900AEAAE0 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 7AFADDAF274A64DD00D53EFC /* RoadNumbers.otf in Resources */, - 7A40D5A62691C6D8009B0BC4 /* Assets.xcassets in Resources */, - 7AFADDB1274A64DD00D53EFC /* RoadNumbers2.0.otf in Resources */, + 7A24C19727EE212E00049E7F /* RoadNumbers.otf in Resources */, + 7A49F4AF27D4061A00AEAAE0 /* LaunchScreen.storyboard in Resources */, + 7A24C19827EE212E00049E7F /* RoadNumbers2.0.otf in Resources */, + 7A49F4AC27D4061A00AEAAE0 /* Assets.xcassets in Resources */, + 7A49F4AA27D4061900AEAAE0 /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 7A40D5872691C6D8009B0BC4 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 7AFADDB0274A64DD00D53EFC /* RoadNumbers.otf in Resources */, - 7A40D5A72691C6D8009B0BC4 /* Assets.xcassets in Resources */, - 7AFADDB2274A64DD00D53EFC /* RoadNumbers2.0.otf in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 7A40D58C2691C6D8009B0BC4 /* Resources */ = { + 7A49F4B327D4061B00AEAAE0 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; - 7A40D5962691C6D8009B0BC4 /* Resources */ = { + 7A49F4BD27D4061B00AEAAE0 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( ); runOnlyForDeploymentPostprocessing = 0; }; - 7A40D5F12693A63A009B0BC4 /* Resources */ = { + 7A49F4D527D4064500AEAAE0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7A49F4DE27D4064500AEAAE0 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 7A503C03269F382F002C1A0D /* login_success.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 7A40D57F2691C6D8009B0BC4 /* Sources */ = { + 7A49F49B27D4061900AEAAE0 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 7A40D5E926938BEC009B0BC4 /* AuthView.swift in Sources */, - 7A971F2326ADF74B007E527B /* MainView.swift in Sources */, - 7ADE63732735E27200A152FE /* VBrand.swift in Sources */, - 7A971F0D26AD7D4C007E527B /* AnyEncodable.swift in Sources */, - 7ACD05D72695C08A00557667 /* Constants.swift in Sources */, - 7A40D5E326924B09009B0BC4 /* Settings.swift in Sources */, - 7ADE637827368C4600A152FE /* VehicleService.swift in Sources */, - 7AEFAEED26985A3400ED2C85 /* ACProgressView.swift in Sources */, - 7A40D5A02691C6D8009B0BC4 /* AutoCat2App.swift in Sources */, - 7A683999269612EA00B2188A /* Response.swift in Sources */, - 7A2329BF26AF140F00264CFA /* CheckView.swift in Sources */, - 7A40D60826998DCF009B0BC4 /* Alert.swift in Sources */, - 7A2329C226AF141E00264CFA /* SearchView.swift in Sources */, - 7ADE637B273692F300A152FE /* StorageService.swift in Sources */, - 7ADE63712735E1FF00A152FE /* VName.swift in Sources */, - 7A40D5A42691C6D8009B0BC4 /* Persistence.swift in Sources */, - 7A971F2026ADC351007E527B /* ApiError.swift in Sources */, - 7A2329CB26AF15AC00264CFA /* ReportView.swift in Sources */, - 7AAEBD35274A47C500230C28 /* PlateNumberView.swift in Sources */, - 7A40D5ED2693A1EA009B0BC4 /* AuthVM.swift in Sources */, - 7A40D59E2691C6D8009B0BC4 /* AutoCat2.xcdatamodeld in Sources */, - 7AFADDA7274A49DB00D53EFC /* PlateNumber.swift in Sources */, - 7ABC05A826B05AF9004BB5A7 /* Vehicle.swift in Sources */, - 7A2329B926AF122400264CFA /* MainViewSmall.swift in Sources */, - 7A2329C826AF143F00264CFA /* RecordsView.swift in Sources */, - 7A40D5E126924AEC009B0BC4 /* User.swift in Sources */, - 7A40D5FE2693A91F009B0BC4 /* CocoaError.swift in Sources */, - 7A40D6022694FF5D009B0BC4 /* Api.swift in Sources */, - 7A40D5A22691C6D8009B0BC4 /* ContentView.swift in Sources */, - 7A2329C526AF142900264CFA /* SettingsView.swift in Sources */, + 7A9F2AC327E71531006492A9 /* ACTabBarController.swift in Sources */, + 7A28283327E7263B0049BDBF /* UIStackView.swift in Sources */, + 7A49F4A727D4061900AEAAE0 /* ViewController.swift in Sources */, + 7A49F4A327D4061900AEAAE0 /* AppDelegate.swift in Sources */, + 7A49F4A527D4061900AEAAE0 /* SceneDelegate.swift in Sources */, + 6841AF924E165F1B3A3B5FB5 /* AuthController.swift in Sources */, + 6841A592745CDD869709EFA7 /* MainTabController.swift in Sources */, + 6841A8FF53F0AADF96B138C1 /* UIControl.swift in Sources */, + 6841A63ECAC11B8956C7539D /* ACButton.swift in Sources */, + 6841A20034E0A9378C3B4BB4 /* ACTextField.swift in Sources */, + 6841A80BB89BFD8D18E79680 /* UITextField.swift in Sources */, + 7A24C19F27EE26B900049E7F /* CenterTextLayer.swift in Sources */, + 6841A5FE3E4927BF0EFF19F5 /* UIViewController.swift in Sources */, + 7A28282F27E720AC0049BDBF /* ACTabBarButton.swift in Sources */, + 6841A0EE0ECCDB3519F728E6 /* HistoryController.swift in Sources */, + 7A28283127E721A70049BDBF /* UIView.swift in Sources */, + 6841A35D2CC223CA7DCD51A2 /* SettingsController.swift in Sources */, + 7A28283827E74D930049BDBF /* CheckController.swift in Sources */, + 6841A7D2375D35B0102D7DEC /* SwiftMaskTextfield.swift in Sources */, + 6841ABD5E4B126DEF3612BBD /* PNKeyboard.swift in Sources */, + 7A24C19C27EE25B400049E7F /* PlateView.swift in Sources */, + 7A24C19D27EE25B400049E7F /* FlagLayer.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 7A40D5852691C6D8009B0BC4 /* Sources */ = { + 7A49F4B127D4061B00AEAAE0 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 7AAEBD36274A47C600230C28 /* PlateNumberView.swift in Sources */, - 7ADE637927368C4600A152FE /* VehicleService.swift in Sources */, - 7A40D5EA26938BEC009B0BC4 /* AuthView.swift in Sources */, - 7A971F2426ADF74B007E527B /* MainView.swift in Sources */, - 7ADE63742735E27200A152FE /* VBrand.swift in Sources */, - 7A971F0E26AD7D4C007E527B /* AnyEncodable.swift in Sources */, - 7ACD05D82695C08A00557667 /* Constants.swift in Sources */, - 7A2329BD26AF123300264CFA /* MainViewBig.swift in Sources */, - 7A40D5A12691C6D8009B0BC4 /* AutoCat2App.swift in Sources */, - 7AEFAEEE26985A3400ED2C85 /* ACProgressView.swift in Sources */, - 7A40D5A52691C6D8009B0BC4 /* Persistence.swift in Sources */, - 7AFADDA8274A49DB00D53EFC /* PlateNumber.swift in Sources */, - 7A68399A269612EA00B2188A /* Response.swift in Sources */, - 7A2329C026AF140F00264CFA /* CheckView.swift in Sources */, - 7A40D60926998DCF009B0BC4 /* Alert.swift in Sources */, - 7A2329C326AF141E00264CFA /* SearchView.swift in Sources */, - 7A40D5E526924B0C009B0BC4 /* User.swift in Sources */, - 7A971F2126ADC351007E527B /* ApiError.swift in Sources */, - 7A2329CC26AF15AC00264CFA /* ReportView.swift in Sources */, - 7ADE637C273692F300A152FE /* StorageService.swift in Sources */, - 7ADE637F2738143000A152FE /* CheckNumberPopup.swift in Sources */, - 7A40D5EE2693A1EA009B0BC4 /* AuthVM.swift in Sources */, - 7A40D59F2691C6D8009B0BC4 /* AutoCat2.xcdatamodeld in Sources */, - 7ABC05A926B05AF9004BB5A7 /* Vehicle.swift in Sources */, - 7A2329BA26AF122400264CFA /* MainViewSmall.swift in Sources */, - 7A2329C926AF143F00264CFA /* RecordsView.swift in Sources */, - 7A40D5A32691C6D8009B0BC4 /* ContentView.swift in Sources */, - 7ADE63752735E27D00A152FE /* VName.swift in Sources */, - 7A40D5FF2693A91F009B0BC4 /* CocoaError.swift in Sources */, - 7A40D6032694FF5D009B0BC4 /* Api.swift in Sources */, - 7A40D5E426924B09009B0BC4 /* Settings.swift in Sources */, - 7A2329C626AF142900264CFA /* SettingsView.swift in Sources */, + 7A49F4BA27D4061B00AEAAE0 /* AutoCat2Tests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 7A40D58A2691C6D8009B0BC4 /* Sources */ = { + 7A49F4BB27D4061B00AEAAE0 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 7A40D5932691C6D8009B0BC4 /* Tests_iOS.swift in Sources */, + 7A49F4C627D4061B00AEAAE0 /* AutoCat2UITestsLaunchTests.swift in Sources */, + 7A49F4C427D4061B00AEAAE0 /* AutoCat2UITests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 7A40D5942691C6D8009B0BC4 /* Sources */ = { + 7A49F4D327D4064500AEAAE0 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 7A40D59D2691C6D8009B0BC4 /* Tests_macOS.swift in Sources */, + 7A49F50D27D406CB00AEAAE0 /* Vehicle.swift in Sources */, + 7A49F50327D406C300AEAAE0 /* VehicleService.swift in Sources */, + 7A49F4DB27D4064500AEAAE0 /* AutoCatCore.docc in Sources */, + 7A49F50227D406C300AEAAE0 /* StorageService.swift in Sources */, + 7A49F4FE27D406BA00AEAAE0 /* AnyEncodable.swift in Sources */, + 7A49F51227D406CB00AEAAE0 /* VName.swift in Sources */, + 7A49F51127D406CB00AEAAE0 /* PlateNumber.swift in Sources */, + 7A49F50C27D406CB00AEAAE0 /* VBrand.swift in Sources */, + 7A49F51027D406CB00AEAAE0 /* Settings.swift in Sources */, + 7A49F4F927D406B200AEAAE0 /* Constants.swift in Sources */, + 7A49F50F27D406CB00AEAAE0 /* Response.swift in Sources */, + 7A49F4FB27D406B200AEAAE0 /* Api.swift in Sources */, + 7A49F51527D40C6100AEAAE0 /* AutoCat2.xcdatamodeld in Sources */, + 7A49F50E27D406CB00AEAAE0 /* User.swift in Sources */, + 7A49F4FA27D406B200AEAAE0 /* ApiError.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; - 7A40D5EF2693A63A009B0BC4 /* Sources */ = { + 7A49F4DC27D4064500AEAAE0 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 7A40D60C2699A070009B0BC4 /* MockURLProtocol.swift in Sources */, - 7A971F0826AD7084007E527B /* LoginMethodMock.swift in Sources */, - 7AF552D92696E5C100578083 /* ApiTests.swift in Sources */, - 7A971F0A26AD74FD007E527B /* ApiMethodMockProtocol.swift in Sources */, - 7A40D5F62693A63A009B0BC4 /* SettingsTests.swift in Sources */, - 7A971F0626AD6F2F007E527B /* ApiMethodMock.swift in Sources */, + 7A49F4E827D4064500AEAAE0 /* AutoCatCoreTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - 7A40D5902691C6D8009B0BC4 /* PBXTargetDependency */ = { + 7A49F4B727D4061B00AEAAE0 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = 7A40D5822691C6D8009B0BC4 /* AutoCat2 (iOS) */; - targetProxy = 7A40D58F2691C6D8009B0BC4 /* PBXContainerItemProxy */; + target = 7A49F49E27D4061900AEAAE0 /* AutoCat2 */; + targetProxy = 7A49F4B627D4061B00AEAAE0 /* PBXContainerItemProxy */; }; - 7A40D59A2691C6D8009B0BC4 /* PBXTargetDependency */ = { + 7A49F4C127D4061B00AEAAE0 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = 7A40D5882691C6D8009B0BC4 /* AutoCat2 (macOS) */; - targetProxy = 7A40D5992691C6D8009B0BC4 /* PBXContainerItemProxy */; + target = 7A49F49E27D4061900AEAAE0 /* AutoCat2 */; + targetProxy = 7A49F4C027D4061B00AEAAE0 /* PBXContainerItemProxy */; }; - 7A40D5F82693A63A009B0BC4 /* PBXTargetDependency */ = { + 7A49F4E327D4064500AEAAE0 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = 7A40D5822691C6D8009B0BC4 /* AutoCat2 (iOS) */; - targetProxy = 7A40D5F72693A63A009B0BC4 /* PBXContainerItemProxy */; + target = 7A49F4D627D4064500AEAAE0 /* AutoCatCore */; + targetProxy = 7A49F4E227D4064500AEAAE0 /* PBXContainerItemProxy */; + }; + 7A49F4E527D4064500AEAAE0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7A49F49E27D4061900AEAAE0 /* AutoCat2 */; + targetProxy = 7A49F4E427D4064500AEAAE0 /* PBXContainerItemProxy */; + }; + 7A49F4EB27D4064500AEAAE0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 7A49F4D627D4064500AEAAE0 /* AutoCatCore */; + targetProxy = 7A49F4EA27D4064500AEAAE0 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ +/* Begin PBXVariantGroup section */ + 7A49F4A827D4061900AEAAE0 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 7A49F4A927D4061900AEAAE0 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 7A49F4AD27D4061A00AEAAE0 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 7A49F4AE27D4061A00AEAAE0 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + /* Begin XCBuildConfiguration section */ - 7A40D5A82691C6D8009B0BC4 /* Debug */ = { + 7A49F4C727D4061B00AEAAE0 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; @@ -811,23 +794,23 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; - MACOSX_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.2; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; - 7A40D5A92691C6D8009B0BC4 /* Release */ = { + 7A49F4C827D4061B00AEAAE0 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; @@ -866,260 +849,89 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; - MACOSX_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.2; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; + SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Release; - }; - 7A40D5AB2691C6D8009B0BC4 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 46DTTB8X4S; - ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = "AutoCat2--iOS--Info.plist"; - INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCat2; - PRODUCT_NAME = AutoCat2; - SDKROOT = iphoneos; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 7A40D5AC2691C6D8009B0BC4 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 46DTTB8X4S; - ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = "AutoCat2--iOS--Info.plist"; - INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; - INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; - INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCat2; - PRODUCT_NAME = AutoCat2; - SDKROOT = iphoneos; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; name = Release; }; - 7A40D5AE2691C6D8009B0BC4 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = "AutoCat2 (macOS).entitlements"; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 46DTTB8X4S; - ENABLE_APP_SANDBOX = YES; - ENABLE_HARDENED_RUNTIME = YES; - ENABLE_PREVIEWS = YES; - ENABLE_USER_SELECTED_FILES = readonly; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = "AutoCat2--macOS--Info.plist"; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - MACOSX_DEPLOYMENT_TARGET = 12.0; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCat2; - PRODUCT_NAME = AutoCat2; - SDKROOT = macosx; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - 7A40D5AF2691C6D8009B0BC4 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = "AutoCat2 (macOS).entitlements"; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 46DTTB8X4S; - ENABLE_APP_SANDBOX = YES; - ENABLE_HARDENED_RUNTIME = YES; - ENABLE_PREVIEWS = YES; - ENABLE_USER_SELECTED_FILES = readonly; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = "AutoCat2--macOS--Info.plist"; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - MACOSX_DEPLOYMENT_TARGET = 12.0; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCat2; - PRODUCT_NAME = AutoCat2; - SDKROOT = macosx; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - 7A40D5B12691C6D8009B0BC4 /* Debug */ = { + 7A49F4CA27D4061B00AEAAE0 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 46DTTB8X4S; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + INFOPLIST_FILE = AutoCat2/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UIMainStoryboardFile = Main; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 14.7; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", - "@loader_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = "pro.aliencat.Tests-iOS"; + PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCat2; PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; - SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = "AutoCat2 (iOS)"; + TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; - 7A40D5B22691C6D8009B0BC4 /* Release */ = { + 7A49F4CB27D4061B00AEAAE0 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 46DTTB8X4S; GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + INFOPLIST_FILE = AutoCat2/Info.plist; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; + INFOPLIST_KEY_UIMainStoryboardFile = Main; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 14.7; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", - "@loader_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = "pro.aliencat.Tests-iOS"; + PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCat2; PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; - SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = "AutoCat2 (iOS)"; - VALIDATE_PRODUCT = YES; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; - 7A40D5B42691C6D8009B0BC4 /* Debug */ = { + 7A49F4CD27D4061B00AEAAE0 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 46DTTB8X4S; - GENERATE_INFOPLIST_FILE = YES; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/../Frameworks", - ); - MACOSX_DEPLOYMENT_TARGET = 12.0; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = "pro.aliencat.Tests-macOS"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = macosx; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TEST_TARGET_NAME = "AutoCat2 (macOS)"; - }; - name = Debug; - }; - 7A40D5B52691C6D8009B0BC4 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 46DTTB8X4S; - GENERATE_INFOPLIST_FILE = YES; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - "@loader_path/../Frameworks", - ); - MACOSX_DEPLOYMENT_TARGET = 12.0; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = "pro.aliencat.Tests-macOS"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = macosx; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TEST_TARGET_NAME = "AutoCat2 (macOS)"; - }; - name = Release; - }; - 7A40D5FA2693A63A009B0BC4 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 46DTTB8X4S; GENERATE_INFOPLIST_FILE = YES; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); + IPHONEOS_DEPLOYMENT_TARGET = 15.2; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCat2Tests; PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -1127,102 +939,263 @@ }; name = Debug; }; - 7A40D5FB2693A63A009B0BC4 /* Release */ = { + 7A49F4CE27D4061B00AEAAE0 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 46DTTB8X4S; GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.2; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCat2Tests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AutoCat2.app/AutoCat2"; + }; + name = Release; + }; + 7A49F4D027D4061B00AEAAE0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 46DTTB8X4S; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCat2UITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = AutoCat2; + }; + name = Debug; + }; + 7A49F4D127D4061B00AEAAE0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 46DTTB8X4S; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCat2UITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = AutoCat2; + }; + name = Release; + }; + 7A49F4EF27D4064500AEAAE0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = 46DTTB8X4S; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 14.7; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", "@loader_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCat2Tests; + PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCatCore; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 7A49F4F027D4064500AEAAE0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = 46DTTB8X4S; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 14.7; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCatCore; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 7A49F4F327D4064500AEAAE0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 46DTTB8X4S; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCatCoreTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AutoCat2.app/AutoCat2"; + }; + name = Debug; + }; + 7A49F4F427D4064500AEAAE0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 46DTTB8X4S; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCatCoreTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SDKROOT = iphoneos; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AutoCat2.app/AutoCat2"; - VALIDATE_PRODUCT = YES; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 7A40D5772691C6D6009B0BC4 /* Build configuration list for PBXProject "AutoCat2" */ = { + 7A49F49A27D4061900AEAAE0 /* Build configuration list for PBXProject "AutoCat2" */ = { isa = XCConfigurationList; buildConfigurations = ( - 7A40D5A82691C6D8009B0BC4 /* Debug */, - 7A40D5A92691C6D8009B0BC4 /* Release */, + 7A49F4C727D4061B00AEAAE0 /* Debug */, + 7A49F4C827D4061B00AEAAE0 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 7A40D5AA2691C6D8009B0BC4 /* Build configuration list for PBXNativeTarget "AutoCat2 (iOS)" */ = { + 7A49F4C927D4061B00AEAAE0 /* Build configuration list for PBXNativeTarget "AutoCat2" */ = { isa = XCConfigurationList; buildConfigurations = ( - 7A40D5AB2691C6D8009B0BC4 /* Debug */, - 7A40D5AC2691C6D8009B0BC4 /* Release */, + 7A49F4CA27D4061B00AEAAE0 /* Debug */, + 7A49F4CB27D4061B00AEAAE0 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 7A40D5AD2691C6D8009B0BC4 /* Build configuration list for PBXNativeTarget "AutoCat2 (macOS)" */ = { + 7A49F4CC27D4061B00AEAAE0 /* Build configuration list for PBXNativeTarget "AutoCat2Tests" */ = { isa = XCConfigurationList; buildConfigurations = ( - 7A40D5AE2691C6D8009B0BC4 /* Debug */, - 7A40D5AF2691C6D8009B0BC4 /* Release */, + 7A49F4CD27D4061B00AEAAE0 /* Debug */, + 7A49F4CE27D4061B00AEAAE0 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 7A40D5B02691C6D8009B0BC4 /* Build configuration list for PBXNativeTarget "Tests iOS" */ = { + 7A49F4CF27D4061B00AEAAE0 /* Build configuration list for PBXNativeTarget "AutoCat2UITests" */ = { isa = XCConfigurationList; buildConfigurations = ( - 7A40D5B12691C6D8009B0BC4 /* Debug */, - 7A40D5B22691C6D8009B0BC4 /* Release */, + 7A49F4D027D4061B00AEAAE0 /* Debug */, + 7A49F4D127D4061B00AEAAE0 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 7A40D5B32691C6D8009B0BC4 /* Build configuration list for PBXNativeTarget "Tests macOS" */ = { + 7A49F4EE27D4064500AEAAE0 /* Build configuration list for PBXNativeTarget "AutoCatCore" */ = { isa = XCConfigurationList; buildConfigurations = ( - 7A40D5B42691C6D8009B0BC4 /* Debug */, - 7A40D5B52691C6D8009B0BC4 /* Release */, + 7A49F4EF27D4064500AEAAE0 /* Debug */, + 7A49F4F027D4064500AEAAE0 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 7A40D5F92693A63A009B0BC4 /* Build configuration list for PBXNativeTarget "AutoCat2Tests" */ = { + 7A49F4F227D4064500AEAAE0 /* Build configuration list for PBXNativeTarget "AutoCatCoreTests" */ = { isa = XCConfigurationList; buildConfigurations = ( - 7A40D5FA2693A63A009B0BC4 /* Debug */, - 7A40D5FB2693A63A009B0BC4 /* Release */, + 7A49F4F327D4064500AEAAE0 /* Debug */, + 7A49F4F427D4064500AEAAE0 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ +/* Begin XCRemoteSwiftPackageReference section */ + 7A28283427E74C110049BDBF /* XCRemoteSwiftPackageReference "SwiftEntryKit" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/huri000/SwiftEntryKit"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.0.0; + }; + }; + 7A48B26527D9442A004D1A4B /* XCRemoteSwiftPackageReference "PKHUD" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/pkluz/PKHUD.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 5.0.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 7A28283527E74C110049BDBF /* SwiftEntryKit */ = { + isa = XCSwiftPackageProductDependency; + package = 7A28283427E74C110049BDBF /* XCRemoteSwiftPackageReference "SwiftEntryKit" */; + productName = SwiftEntryKit; + }; + 7A48B26627D9442A004D1A4B /* PKHUD */ = { + isa = XCSwiftPackageProductDependency; + package = 7A48B26527D9442A004D1A4B /* XCRemoteSwiftPackageReference "PKHUD" */; + productName = PKHUD; + }; +/* End XCSwiftPackageProductDependency section */ + /* Begin XCVersionGroup section */ - 7A40D5792691C6D7009B0BC4 /* AutoCat2.xcdatamodeld */ = { + 7A49F51327D40C6100AEAAE0 /* AutoCat2.xcdatamodeld */ = { isa = XCVersionGroup; children = ( - 7A40D57A2691C6D7009B0BC4 /* Shared.xcdatamodel */, + 7A49F51427D40C6100AEAAE0 /* Shared.xcdatamodel */, ); - currentVersion = 7A40D57A2691C6D7009B0BC4 /* Shared.xcdatamodel */; + currentVersion = 7A49F51427D40C6100AEAAE0 /* Shared.xcdatamodel */; path = AutoCat2.xcdatamodeld; sourceTree = ""; versionGroupType = wrapper.xcdatamodel; }; /* End XCVersionGroup section */ }; - rootObject = 7A40D5742691C6D6009B0BC4 /* Project object */; + rootObject = 7A49F49727D4061900AEAAE0 /* Project object */; } diff --git a/AutoCat2.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/AutoCat2.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..d0c7372 --- /dev/null +++ b/AutoCat2.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,23 @@ +{ + "pins" : [ + { + "identity" : "pkhud", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pkluz/PKHUD.git", + "state" : { + "revision" : "8fd26f23057c6bebd6695524b1c3e05e93aba571", + "version" : "5.4.0" + } + }, + { + "identity" : "swiftentrykit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/huri000/SwiftEntryKit", + "state" : { + "revision" : "5ad36cccf0c4b9fea32f4e9b17a8e38f07563ef0", + "version" : "2.0.0" + } + } + ], + "version" : 2 +} diff --git a/AutoCat2.xcodeproj/xcuserdata/selim.xcuserdatad/xcschemes/xcschememanagement.plist b/AutoCat2.xcodeproj/xcuserdata/selim.xcuserdatad/xcschemes/xcschememanagement.plist index a2751d7..973e834 100644 --- a/AutoCat2.xcodeproj/xcuserdata/selim.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/AutoCat2.xcodeproj/xcuserdata/selim.xcuserdatad/xcschemes/xcschememanagement.plist @@ -4,39 +4,16 @@ SchemeUserState - AutoCat2 (iOS).xcscheme_^#shared#^_ + AutoCat2.xcscheme orderHint 0 - AutoCat2 (macOS).xcscheme_^#shared#^_ + AutoCatCore.xcscheme_^#shared#^_ orderHint 1 - AutoCatCore.xcscheme_^#shared#^_ - - orderHint - 2 - - - SuppressBuildableAutocreation - - 7A40D5822691C6D8009B0BC4 - - primary - - - 7A40D58D2691C6D8009B0BC4 - - primary - - - 7A40D5F22693A63A009B0BC4 - - primary - - diff --git a/AutoCat2/AppDelegate.swift b/AutoCat2/AppDelegate.swift new file mode 100644 index 0000000..1c0c270 --- /dev/null +++ b/AutoCat2/AppDelegate.swift @@ -0,0 +1,36 @@ +// +// AppDelegate.swift +// AutoCat2 +// +// Created by Selim Mustafaev on 05.03.2022. +// + +import UIKit + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } + + +} + diff --git a/Shared/Assets.xcassets/AccentColor.colorset/Contents.json b/AutoCat2/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from Shared/Assets.xcassets/AccentColor.colorset/Contents.json rename to AutoCat2/Assets.xcassets/AccentColor.colorset/Contents.json diff --git a/Shared/Assets.xcassets/AppIcon.appiconset/Contents.json b/AutoCat2/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 65% rename from Shared/Assets.xcassets/AppIcon.appiconset/Contents.json rename to AutoCat2/Assets.xcassets/AppIcon.appiconset/Contents.json index c136eaf..9221b9b 100644 --- a/Shared/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/AutoCat2/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -89,56 +89,6 @@ "idiom" : "ios-marketing", "scale" : "1x", "size" : "1024x1024" - }, - { - "idiom" : "mac", - "scale" : "1x", - "size" : "16x16" - }, - { - "idiom" : "mac", - "scale" : "2x", - "size" : "16x16" - }, - { - "idiom" : "mac", - "scale" : "1x", - "size" : "32x32" - }, - { - "idiom" : "mac", - "scale" : "2x", - "size" : "32x32" - }, - { - "idiom" : "mac", - "scale" : "1x", - "size" : "128x128" - }, - { - "idiom" : "mac", - "scale" : "2x", - "size" : "128x128" - }, - { - "idiom" : "mac", - "scale" : "1x", - "size" : "256x256" - }, - { - "idiom" : "mac", - "scale" : "2x", - "size" : "256x256" - }, - { - "idiom" : "mac", - "scale" : "1x", - "size" : "512x512" - }, - { - "idiom" : "mac", - "scale" : "2x", - "size" : "512x512" } ], "info" : { diff --git a/Shared/Assets.xcassets/Colors/Contents.json b/AutoCat2/Assets.xcassets/Colors/Contents.json similarity index 100% rename from Shared/Assets.xcassets/Colors/Contents.json rename to AutoCat2/Assets.xcassets/Colors/Contents.json diff --git a/Shared/Assets.xcassets/Colors/PlateForeground.colorset/Contents.json b/AutoCat2/Assets.xcassets/Colors/DarkKeyBackground.colorset/Contents.json similarity index 61% rename from Shared/Assets.xcassets/Colors/PlateForeground.colorset/Contents.json rename to AutoCat2/Assets.xcassets/Colors/DarkKeyBackground.colorset/Contents.json index 945ab6a..bb37c2e 100644 --- a/Shared/Assets.xcassets/Colors/PlateForeground.colorset/Contents.json +++ b/AutoCat2/Assets.xcassets/Colors/DarkKeyBackground.colorset/Contents.json @@ -2,13 +2,8 @@ "colors" : [ { "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.000", - "green" : "0.000", - "red" : "0.000" - } + "platform" : "ios", + "reference" : "systemGray2Color" }, "idiom" : "universal" }, @@ -20,12 +15,12 @@ } ], "color" : { - "color-space" : "display-p3", + "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0.866", - "green" : "0.866", - "red" : "0.866" + "blue" : "0.290", + "green" : "0.282", + "red" : "0.282" } }, "idiom" : "universal" diff --git a/Shared/Assets.xcassets/Colors/PlateBackground.colorset/Contents.json b/AutoCat2/Assets.xcassets/Colors/KeyBackground.colorset/Contents.json similarity index 78% rename from Shared/Assets.xcassets/Colors/PlateBackground.colorset/Contents.json rename to AutoCat2/Assets.xcassets/Colors/KeyBackground.colorset/Contents.json index 2257e90..eb0fccd 100644 --- a/Shared/Assets.xcassets/Colors/PlateBackground.colorset/Contents.json +++ b/AutoCat2/Assets.xcassets/Colors/KeyBackground.colorset/Contents.json @@ -20,12 +20,12 @@ } ], "color" : { - "color-space" : "display-p3", + "color-space" : "srgb", "components" : { - "alpha" : "1.000", - "blue" : "0.152", - "green" : "0.152", - "red" : "0.152" + "alpha" : "0.300", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" } }, "idiom" : "universal" diff --git a/Shared/Assets.xcassets/Contents.json b/AutoCat2/Assets.xcassets/Contents.json similarity index 100% rename from Shared/Assets.xcassets/Contents.json rename to AutoCat2/Assets.xcassets/Contents.json diff --git a/AutoCat2/Base.lproj/LaunchScreen.storyboard b/AutoCat2/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..865e932 --- /dev/null +++ b/AutoCat2/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AutoCat2/Base.lproj/Main.storyboard b/AutoCat2/Base.lproj/Main.storyboard new file mode 100644 index 0000000..25a7638 --- /dev/null +++ b/AutoCat2/Base.lproj/Main.storyboard @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AutoCat2/Components/ACButton.swift b/AutoCat2/Components/ACButton.swift new file mode 100644 index 0000000..2727e37 --- /dev/null +++ b/AutoCat2/Components/ACButton.swift @@ -0,0 +1,84 @@ +// +// Created by Selim Mustafaev on 06.03.2022. +// + +import UIKit + +enum ACButtonStyle { + case generic + case roundedBlue +} + +class ACButton: UIButton { + + private var style: ACButtonStyle = .generic + + convenience init(style: ACButtonStyle = .roundedBlue, title: String, onTap: @escaping () -> Void) { + self.init() + self.style(style) + self.onTap(onTap) + self.title(title) + translatesAutoresizingMaskIntoConstraints = false + } + + convenience init(style: ACButtonStyle = .roundedBlue, title: String, onTapAsync: @escaping () async -> Void) { + self.init() + self.style(style) + self.onTapAsync(onTapAsync) + self.title(title) + translatesAutoresizingMaskIntoConstraints = false + } + + override func layoutSubviews() { + super.layoutSubviews() + + if style == .roundedBlue { + self.layer.opacity = self.isEnabled ? 1 : 0.5 + } else { + self.layer.opacity = 1 + } + } + + @discardableResult + func style(_ style: ACButtonStyle) -> ACButton { + + self.style = style + + switch style { + case .generic: + break + case .roundedBlue: + backgroundColor = .systemBlue + layer.cornerRadius = 6 + } + + return self + } + + @discardableResult + func title(_ title: String) -> ACButton { + + setTitle(title, for: .normal) + return self + } + + @discardableResult + func enable(_ enabled: Bool) -> ACButton { + isEnabled = enabled + return self + } + + @discardableResult + func onTap(_ handler: @escaping () -> Void) -> ACButton { + + addAction(for: .touchUpInside, handler) + return self + } + + @discardableResult + func onTapAsync(_ handler: @escaping () async -> Void) -> ACButton { + + addActionAsync(for: .touchUpInside, handler) + return self + } +} diff --git a/AutoCat2/Components/ACTabBar/ACTabBarButton.swift b/AutoCat2/Components/ACTabBar/ACTabBarButton.swift new file mode 100644 index 0000000..59796aa --- /dev/null +++ b/AutoCat2/Components/ACTabBar/ACTabBarButton.swift @@ -0,0 +1,82 @@ +// +// ACTabBarButton.swift +// AutoCat2 +// +// Created by Selim Mustafaev on 20.03.2022. +// + +import UIKit + +class ACTabBarButton: UIView { + + enum State { + case normal + case selected + } + + private var state: State = .normal + private var tabBarItem: UITabBarItem? + + // MARK: - Views + + private lazy var imageView: UIImageView = { + let view = UIImageView() + view.contentMode = .scaleAspectFit + view.translatesAutoresizingMaskIntoConstraints = false + return view + }() + + private lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = UIFont.preferredFont(forTextStyle: .caption2) + label.textColor = tintColor + label.translatesAutoresizingMaskIntoConstraints = false + return label + }() + + private lazy var stackView: UIStackView = { + let stack = UIStackView(arrangedSubviews: [imageView, titleLabel]) + stack.axis = .vertical + stack.spacing = 0 + stack.distribution = .fill + stack.alignment = .center + stack.translatesAutoresizingMaskIntoConstraints = false + return stack + }() + + // MARK: - Lifecycle + + override init(frame: CGRect) { + super.init(frame: frame) + setup() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + convenience init(tabBarItem: UITabBarItem) { + self.init(frame: .zero) + apply(tabBarItem: tabBarItem) + } + + func setup() { + addSubview(stackView) + NSLayoutConstraint.activate([ + stackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 4), + stackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -4), + stackView.topAnchor.constraint(equalTo: topAnchor, constant: 4), + stackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -4), + imageView.widthAnchor.constraint(equalToConstant: 32), + imageView.heightAnchor.constraint(equalToConstant: 32) + ]) + } + + func apply(tabBarItem: UITabBarItem) { + self.tabBarItem = tabBarItem + + imageView.contentMode = .scaleAspectFit + imageView.image = state == .normal ? tabBarItem.image : tabBarItem.selectedImage + titleLabel.text = tabBarItem.title + } +} diff --git a/AutoCat2/Components/ACTabBar/ACTabBarController.swift b/AutoCat2/Components/ACTabBar/ACTabBarController.swift new file mode 100644 index 0000000..36a4fd1 --- /dev/null +++ b/AutoCat2/Components/ACTabBar/ACTabBarController.swift @@ -0,0 +1,67 @@ +// +// ACTabBarController.swift +// AutoCat2 +// +// Created by Selim Mustafaev on 20.03.2022. +// + +import UIKit + +class ACTabBarController: UIViewController { + + // MARK: - Properties + + var viewControllers: [UIViewController] = [] { + didSet { + setupControllers() + } + } + + // MARK: - Views + + private lazy var buttonsStackView: UIStackView = { + let stack = UIStackView() + stack.translatesAutoresizingMaskIntoConstraints = false + stack.axis = .horizontal + stack.spacing = 0 + stack.distribution = .fillEqually + return stack + }() + + private let tabBarView: UIView = { + let view = UIView() + view.translatesAutoresizingMaskIntoConstraints = false + return view + }() + + // MARK: - Lifecycle + + override func viewDidLoad() { + super.viewDidLoad() + +// tabBarView.backgroundColor = .green +// buttonsStackView.backgroundColor = .magenta + + view.addSubview(tabBarView) + tabBarView.addSubview(buttonsStackView) + + NSLayoutConstraint.activate([ + tabBarView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + tabBarView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + tabBarView.bottomAnchor.constraint(equalTo: view.bottomAnchor), + buttonsStackView.leadingAnchor.constraint(equalTo: tabBarView.leadingAnchor), + buttonsStackView.trailingAnchor.constraint(equalTo: tabBarView.trailingAnchor), + buttonsStackView.topAnchor.constraint(equalTo: tabBarView.topAnchor), + buttonsStackView.bottomAnchor.constraint(equalTo: tabBarView.safeAreaLayoutGuide.bottomAnchor) + ]) + } + + func setupControllers() { + buttonsStackView.removeAllArrangedSubviews() + + for controller in viewControllers { + let button = ACTabBarButton(tabBarItem: controller.tabBarItem) + buttonsStackView.addArrangedSubview(button) + } + } +} diff --git a/AutoCat2/Components/ACTextField.swift b/AutoCat2/Components/ACTextField.swift new file mode 100644 index 0000000..4156d27 --- /dev/null +++ b/AutoCat2/Components/ACTextField.swift @@ -0,0 +1,98 @@ +// +// Created by Selim Mustafaev on 06.03.2022. +// + +import UIKit + +enum ACTextFieldStyle { + + case generic + case roundBordered +} + +class ACTextField: UITextField, UITextFieldDelegate { + + private var style: ACTextFieldStyle = .generic + private var onTextChanged: ((String?) -> Void)? + + var editable: Bool = true + + // MARK: - Lifecycle + + convenience init(placeholder: String? = nil, style: ACTextFieldStyle = .roundBordered) { + self.init(frame: .zero) + + self.delegate = self + self.placeholderText(placeholder) + self.style(style) + + addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged) + + translatesAutoresizingMaskIntoConstraints = false + } + + override func layoutSubviews() { + super.layoutSubviews() + style(style) + } + + // MARK: - Setters + + @discardableResult + func placeholderText(_ placeholder: String?) -> ACTextField { + self.placeholder = placeholder + return self + } + + @discardableResult + func keyboardType(_ keyboardType: UIKeyboardType) -> ACTextField { + self.keyboardType = keyboardType + return self + } + + @discardableResult + func style(_ style: ACTextFieldStyle) -> ACTextField { + + self.style = style + borderStyle = .roundedRect + + switch style { + case .generic: + layer.borderWidth = 0 + layer.cornerRadius = 0 + layer.borderColor = nil + break + case .roundBordered: + layer.borderWidth = 1 + layer.cornerRadius = 6 + layer.borderColor = UIColor.secondaryLabel.cgColor + } + + return self + } + + @discardableResult + func secure(_ secure: Bool) -> ACTextField { + isSecureTextEntry = secure + return self + } + + @discardableResult + func onTextChanged(_ handler: @escaping (String?) -> Void) -> ACTextField { + onTextChanged = handler + return self + } + + // MARK: - UITextFieldDelegate + + func textField(_ textField: UITextField, + shouldChangeCharactersIn range: NSRange, + replacementString string: String) -> Bool { + editable + } + + @objc func textFieldDidChange(_ textField: UITextField) { + onTextChanged?(textField.text) + } + +} diff --git a/AutoCat2/Components/Extensions/UIControl.swift b/AutoCat2/Components/Extensions/UIControl.swift new file mode 100644 index 0000000..1f4114b --- /dev/null +++ b/AutoCat2/Components/Extensions/UIControl.swift @@ -0,0 +1,23 @@ +// +// Created by Selim Mustafaev on 06.03.2022. +// + +import UIKit + +extension UIControl { + + func addAction(for controlEvents: UIControl.Event = .touchUpInside, _ closure: @escaping () -> Void) { + + addAction(UIAction { _ in closure() }, for: controlEvents) + } + + func addActionAsync(for controlEvents: UIControl.Event = .touchUpInside, _ closure: @escaping () async -> Void) { + + addAction(UIAction { _ in + Task { + await closure() + } + }, for: controlEvents) + } + +} diff --git a/AutoCat2/Components/Extensions/UIStackView.swift b/AutoCat2/Components/Extensions/UIStackView.swift new file mode 100644 index 0000000..1f563ae --- /dev/null +++ b/AutoCat2/Components/Extensions/UIStackView.swift @@ -0,0 +1,26 @@ +// +// UIStackView.swift +// AutoCat2 +// +// Created by Selim Mustafaev on 20.03.2022. +// + +import UIKit + +extension UIStackView { + + func removeAllArrangedSubviews() { + + let removedSubviews = arrangedSubviews.reduce([]) { (allSubviews, subview) -> [UIView] in + self.removeArrangedSubview(subview) + return allSubviews + [subview] + } + + // Deactivate all constraints + NSLayoutConstraint.deactivate(removedSubviews.flatMap({ $0.constraints })) + + // Remove the views from self + removedSubviews.forEach({ $0.removeFromSuperview() }) + } + +} diff --git a/AutoCat2/Components/Extensions/UITextField.swift b/AutoCat2/Components/Extensions/UITextField.swift new file mode 100644 index 0000000..ae3f2fa --- /dev/null +++ b/AutoCat2/Components/Extensions/UITextField.swift @@ -0,0 +1,18 @@ +// +// Created by Selim Mustafaev on 06.03.2022. +// + +import UIKit +import Combine + +extension UITextField { + + var textPublisher: AnyPublisher { + + NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: self) + .map { ($0.object as? UITextField)?.text } + .eraseToAnyPublisher() + } + +} + diff --git a/AutoCat2/Components/Extensions/UIView.swift b/AutoCat2/Components/Extensions/UIView.swift new file mode 100644 index 0000000..e02ae0c --- /dev/null +++ b/AutoCat2/Components/Extensions/UIView.swift @@ -0,0 +1,25 @@ +// +// UIView.swift +// AutoCat2 +// +// Created by Selim Mustafaev on 20.03.2022. +// + +import UIKit + +extension UIView { + + func disableTranslatesAutoresizingMaskIntoConstraints() -> Self { + translatesAutoresizingMaskIntoConstraints = false + return self + } + + func pin(to view: UIView, insets: UIEdgeInsets = .zero) { + NSLayoutConstraint.activate([ + leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: insets.left), + trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -insets.right), + topAnchor.constraint(equalTo: view.topAnchor, constant: insets.top), + bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -insets.bottom) + ]) + } +} diff --git a/AutoCat2/Components/Extensions/UIViewController.swift b/AutoCat2/Components/Extensions/UIViewController.swift new file mode 100644 index 0000000..633ef26 --- /dev/null +++ b/AutoCat2/Components/Extensions/UIViewController.swift @@ -0,0 +1,16 @@ +// +// Created by Selim Mustafaev on 10.03.2022. +// + +import UIKit + +extension UIViewController { + + func show(error: Error) { + + let alert = UIAlertController(title: nil, message: error.localizedDescription, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "OK", style: .default)) + present(alert, animated: true) + } + +} \ No newline at end of file diff --git a/AutoCat2/Components/PNKeyboard.swift b/AutoCat2/Components/PNKeyboard.swift new file mode 100644 index 0000000..7b4dee1 --- /dev/null +++ b/AutoCat2/Components/PNKeyboard.swift @@ -0,0 +1,216 @@ +import UIKit +import AutoCatCore + +protocol PNKeyboardDelegate: AnyObject { + func returnClicked() +} + +protocol PNButtonDelegate: AnyObject { + func buttonTapped(_ button: PNButton) +} + +enum PNButtonType { + case symbol(String) + case backspace + case done +} + +class PNButton: UIButton { + private(set) var type: PNButtonType + private var timer: Timer? + private var waitCount = 0 + private var rectLayer = CAShapeLayer() + private var bgColor = UIColor(named: "KeyBackground") + + weak var delegate: PNButtonDelegate? + + init(letter: Character) { + self.type = .symbol(String(letter)) + super.init(frame: .zero) + self.setup() + self.titleLabel?.font = UIFont(name: "RoadNumbers", size: 36) + let title = String(Constants.pnLettersMap[letter] ?? letter) + self.setTitle(title, for: .normal) + self.setTitleColor(.label, for: .normal) + } + + init(digit: Int) { + self.type = .symbol(String(digit)) + super.init(frame: .zero) + self.setup() + self.titleLabel?.font = UIFont(name: "RoadNumbersCyr-Regular", size: 30) + let character = Character(String(digit)) + let title = String(Constants.pnLettersMap[character] ?? character) + self.setTitle(title, for: .normal) + self.setTitleColor(.label, for: .normal) + } + + init(imageName: String, type: PNButtonType) { + self.type = type + self.bgColor = UIColor(named: "DarkKeyBackground") + super.init(frame: .zero) + self.setup() + self.setImage(UIImage(systemName: imageName), for: .normal) + self.tintColor = .label + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func setup(_ radius: CGFloat = 0.5) { + self.rectLayer.cornerRadius = 6 + self.layer.addSublayer(self.rectLayer) + self.imageView?.layer.zPosition = 2 + + self.rectLayer.shadowColor = UIColor.black.cgColor + self.rectLayer.shadowOpacity = 0.4 + self.rectLayer.shadowOffset = CGSize(width: 0.0, height : 1.0) + self.rectLayer.shadowRadius = radius + + self.addTarget(self, action: #selector(buttonDown), for: .touchDown) + self.addTarget(self, action: #selector(buttonUp), for: [.touchUpInside, .touchUpOutside]) + self.addTarget(self, action: #selector(touchUpInside), for: .touchUpInside) + } + + override func layoutSubviews() { + super.layoutSubviews() + self.rectLayer.backgroundColor = self.bgColor?.cgColor + self.rectLayer.frame = self.layer.bounds.inset(by: UIEdgeInsets(top: 4, left: 4, bottom: 4, right: 4)) + } + + @objc func buttonDown(_ sender: PNButton) { + sender.layer.opacity = 0.3 + self.timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block: { _ in + self.waitCount += 1 + if self.waitCount > 5 { + self.delegate?.buttonTapped(self) + } + }) + } + + @objc func buttonUp(_ sender: PNButton) { + sender.layer.opacity = 1 + self.timer?.invalidate() + self.timer = nil + self.waitCount = 0 + } + + @objc func touchUpInside(_ sender: PNButton) { + self.delegate?.buttonTapped(self) + } + + func setBacgroundColor(color: UIColor) { + self.bgColor = color + self.setNeedsLayout() + } +} + +class PNKeyboard: UIView, UIInputViewAudioFeedback, PNButtonDelegate { + private weak var target: UIKeyInput? + weak var delegate: PNKeyboardDelegate? + + private let insets: UIEdgeInsets + + init(target: UIKeyInput?, insets: UIEdgeInsets) { + self.target = target + self.insets = insets + super.init(frame: .zero) + self.autoresizingMask = [.flexibleWidth, .flexibleHeight] + + let blurEffectView = UIVisualEffectView(effect: UIBlurEffect()) + blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + self.addSubview(blurEffectView) + + self.setupButtons() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func setupButtons() { + let letters: [PNButton] = Constants.pnLettersMap.keys.sorted().map { letter in + let button = PNButton(letter: letter) + button.delegate = self + return button + } + let digits: [PNButton] = [1,2,3,4,5,6,7,8,9,0].map { digit in + let button = PNButton(digit: digit) + button.delegate = self + return button + } + let backspace = PNButton(imageName: "delete.left", type: .backspace) + backspace.delegate = self + + let done = PNButton(imageName: "arrow.turn.down.left", type: .done) + done.delegate = self + done.setBacgroundColor(color: .systemBlue) + done.tintColor = .white + + let letterRows = [ + self.createLetterStack([letters[0], letters[1], letters[2]]), + self.createLetterStack([letters[3], letters[4], letters[5]]), + self.createLetterStack([letters[6], letters[7], letters[8]]), + self.createLetterStack([letters[9], letters[10], letters[11]]) + ] + + let lettersStack = UIStackView(arrangedSubviews: letterRows) + lettersStack.axis = .vertical + lettersStack.distribution = .fillEqually + //lettersStack.spacing = 8 + + let digitRows = [ + self.createLetterStack([digits[0], digits[1], digits[2]]), + self.createLetterStack([digits[3], digits[4], digits[5]]), + self.createLetterStack([digits[6], digits[7], digits[8]]), + self.createLetterStack([digits[9], backspace, done]) + ] + + let digitsStack = UIStackView(arrangedSubviews: digitRows) + digitsStack.axis = .vertical + digitsStack.distribution = .fillEqually + //digitsStack.spacing = 8 + + let mainStack = UIStackView(arrangedSubviews: [lettersStack, digitsStack]) + mainStack.spacing = 16 + mainStack.distribution = .fillEqually + mainStack.translatesAutoresizingMaskIntoConstraints = false + self.addSubview(mainStack) + + NSLayoutConstraint.activate([ + mainStack.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: insets.left), + mainStack.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -insets.right), + mainStack.topAnchor.constraint(equalTo: self.topAnchor, constant: insets.top), + mainStack.bottomAnchor.constraint(equalTo: self.safeAreaLayoutGuide.bottomAnchor, constant: -insets.bottom) + ]) + } + + func createLetterStack(_ views: [UIView]) -> UIStackView { + let stack = UIStackView(arrangedSubviews: views) + stack.distribution = .fillEqually + //stack.spacing = 8 + return stack + } + + // MARK: - PNButtonDelegate + + func buttonTapped(_ button: PNButton) { + UIDevice.current.playInputClick() + + switch button.type { + case .symbol(let s): + self.target?.insertText(s) + case .backspace: + self.target?.deleteBackward() + case .done: + self.delegate?.returnClicked() + } + } + + // MARK: - UIInputViewAudioFeedback + + var enableInputClicksWhenVisible: Bool { + return true + } +} diff --git a/AutoCat2/Components/PlateView/FlagLayer.swift b/AutoCat2/Components/PlateView/FlagLayer.swift new file mode 100644 index 0000000..2b87d06 --- /dev/null +++ b/AutoCat2/Components/PlateView/FlagLayer.swift @@ -0,0 +1,31 @@ +import UIKit + +class FlagLayer: CALayer { + let pixelWidth = 1/UIScreen.main.scale + + // Flag colors - https://ru.wikipedia.org/wiki/%D0%A4%D0%BB%D0%B0%D0%B3_%D0%A0%D0%BE%D1%81%D1%81%D0%B8%D0%B8 + let blue = CGColor(srgbRed: 0, green: 57/256.0, blue: 166/256.0, alpha: 1) + let red = CGColor(srgbRed: 213/256.0, green: 43/256.0, blue: 30/256.0, alpha: 1) + + override func draw(in ctx: CGContext) { + ctx.saveGState() + super.draw(in: ctx) + + ctx.setStrokeColor(UIColor.black.cgColor) + ctx.setLineWidth(0) + + ctx.setFillColor(UIColor.white.cgColor) + ctx.fill(bounds.inset(by: UIEdgeInsets(top: 0, left: 0, bottom: bounds.height*2/3, right: 0))) + + ctx.setFillColor(self.blue) + ctx.fill(bounds.insetBy(dx: 0, dy: bounds.height/3)) + + ctx.setFillColor(self.red) + ctx.fill(bounds.inset(by: UIEdgeInsets(top: bounds.height*2/3, left: 0, bottom: 0, right: 0))) + + ctx.setLineWidth(pixelWidth) + ctx.stroke(bounds.insetBy(dx: pixelWidth/2, dy: pixelWidth/2)) + + ctx.restoreGState() + } +} diff --git a/AutoCat2/Components/PlateView/PlateView.swift b/AutoCat2/Components/PlateView/PlateView.swift new file mode 100644 index 0000000..4181156 --- /dev/null +++ b/AutoCat2/Components/PlateView/PlateView.swift @@ -0,0 +1,138 @@ +import UIKit +import AutoCatCore + +class PlateView: UIView { + // Some driver plate parameters from "ГОСТ Р 50577-93" + // http://docs.cntd.ru/document/gost-r-50577-93 + private static let aspectRatio: CGFloat = 112.0/520.0 + private static let fontHeightCoeff: CGFloat = 58.0/76.0 + + private var bgLayer = CALayer() + private var mainBgLayer = CALayer() + private var regionBgLayer = CALayer() + private var numberLayer = CenterTextLayer(coeff: fontHeightCoeff) + private var regionLayer = CenterTextLayer(coeff: fontHeightCoeff) + private var countryLayer = CenterTextLayer() + private var flagLayer = FlagLayer() + + var number: PlateNumber? { + didSet { + self.layoutSubviews() + } + } + + var foreground: UIColor? { + didSet { + self.layoutSubviews() + } + } + + override init(frame: CGRect) { + super.init(frame: frame) + setup() + } + + required init?(coder: NSCoder) { + super.init(coder: coder) + setup() + } + + func setup() { + self.layer.backgroundColor = UIColor.clear.cgColor + + self.bgLayer.cornerRadius = 6 //self.bounds.height/8 + self.layer.addSublayer(self.bgLayer) + + self.mainBgLayer.cornerRadius = 4 + self.layer.addSublayer(self.mainBgLayer) + + self.regionBgLayer.cornerRadius = 4 + self.layer.addSublayer(self.regionBgLayer) + + self.numberLayer.alignmentMode = .center + self.numberLayer.contentsScale = UIScreen.main.scale + self.layer.addSublayer(self.numberLayer) + + self.regionLayer.alignmentMode = .center + self.regionLayer.contentsScale = UIScreen.main.scale + self.layer.addSublayer(self.regionLayer) + + self.countryLayer.contentsScale = UIScreen.main.scale + self.countryLayer.string = "RUS" + self.countryLayer.alignmentMode = .center + self.layer.addSublayer(self.countryLayer) + + self.flagLayer.contentsScale = UIScreen.main.scale + self.layer.addSublayer(self.flagLayer) + } + + private func pathForFlag(in rect: CGRect) -> CGPath { + let path = CGMutablePath() + let rect = CGPath(rect: rect, transform: nil) + path.addPath(rect) + return path + } + + override func layoutSubviews() { + super.layoutSubviews() + guard let number = self.number else { return } + guard let fgColorMain = UIColor(named: "PlateForeground")?.cgColor else { return } + guard let bgColor = UIColor(named: "PlateBackground")?.cgColor else { return } + + let fgColor = self.foreground?.cgColor ?? fgColorMain + + self.bgLayer.backgroundColor = fgColor + self.bgLayer.frame = self.bounds + + self.mainBgLayer.backgroundColor = bgColor + self.regionBgLayer.backgroundColor = bgColor + self.mainBgLayer.frame = self.bounds.inset(by: UIEdgeInsets(top: 2, left: 2, bottom: 2, right: self.bounds.width*0.27 + 1)) + self.regionBgLayer.frame = self.bounds.inset(by: UIEdgeInsets(top: 2, left: self.bounds.width*0.73 + 1, bottom: 2, right: 2)) + + self.numberLayer.frame = self.mainBgLayer.frame.insetBy(dx: 4, dy: 0) + let font = UIFont(name: "RoadNumbers", size: self.mainBgLayer.frame.height*1.1) ?? UIFont.boldSystemFont(ofSize: 24) + let attributes: [NSAttributedString.Key: Any] = [ + .kern: 3, + .font: font, + .foregroundColor: fgColor + ] + let attributed = NSAttributedString(string: number.mainPart(), attributes: attributes) + self.numberLayer.string = attributed + + let rbgSize = self.regionBgLayer.frame.size + + self.regionLayer.frame = self.regionBgLayer.frame.inset(by: UIEdgeInsets(top: 2, left: 2, bottom: rbgSize.height*0.35, right: 2)) + let regionFont = UIFont(name: "RoadNumbers", size: rbgSize.height*0.8) ?? UIFont.boldSystemFont(ofSize: 24) + let regionAttrs: [NSAttributedString.Key: Any] = [ + .kern: 1, + .font: regionFont, + .foregroundColor: fgColor + ] + let attributedRegion = NSAttributedString(string: number.region(), attributes: regionAttrs) + self.regionLayer.string = attributedRegion + + self.countryLayer.foregroundColor = fgColor + self.countryLayer.frame = self.regionBgLayer.frame.inset(by: UIEdgeInsets(top: rbgSize.height*0.64, left: rbgSize.width*0.08, bottom: rbgSize.height*0.07, right: rbgSize.width*0.42)) + self.countryLayer.fontSize = self.countryLayer.frame.size.height + + let top = (self.regionBgLayer.frame.origin.y + rbgSize.height*0.68).rounded(.toNearestOrAwayFromZero) + let left = (self.regionBgLayer.frame.origin.x + rbgSize.width*0.62).rounded(.toNearestOrAwayFromZero) + let w = (rbgSize.width*0.34).rounded(.toNearestOrAwayFromZero) + let h = (rbgSize.height*0.28).rounded(.toNearestOrAwayFromZero) + self.flagLayer.frame = CGRect(x: left, y: top, width: w, height: h) + self.flagLayer.setNeedsDisplay() + } + + override var intrinsicContentSize: CGSize { + guard self.bounds.width != 0 || self.bounds.height != 0 else { + return CGSize.zero + } + + let curAspectRatio = self.bounds.height/self.bounds.width + if curAspectRatio >= PlateView.aspectRatio { + return CGSize(width: self.bounds.width, height: self.bounds.width*PlateView.aspectRatio) + } else { + return CGSize(width: self.bounds.height/PlateView.aspectRatio, height: self.bounds.height) + } + } +} diff --git a/AutoCat2/Controllers/AuthController.swift b/AutoCat2/Controllers/AuthController.swift new file mode 100644 index 0000000..918862e --- /dev/null +++ b/AutoCat2/Controllers/AuthController.swift @@ -0,0 +1,99 @@ +// +// Created by Selim Mustafaev on 06.03.2022. +// + +import UIKit +import PKHUD +import AutoCatCore + +class AuthController: UIViewController { + + private lazy var emailField = ACTextField(placeholder: "Email") + .onTextChanged(textChanged) + .keyboardType(.emailAddress) + + private lazy var passwordField = ACTextField(placeholder: "Password") + .onTextChanged(textChanged) + .secure(true) + + private lazy var loginButton = ACButton(title: "Log in", onTapAsync: loginTapped) + .enable(false) + + private lazy var signupButton = ACButton(title: "Sign up", onTapAsync: signupTapped) + .enable(false) + + private lazy var stackView: UIStackView = { + let stack = UIStackView(arrangedSubviews: [ + emailField, + passwordField, + loginButton, + signupButton + ]) + + stack.axis = .vertical + stack.spacing = 16 + stack.translatesAutoresizingMaskIntoConstraints = false + + return stack + }() + + override func viewDidLoad() { + super.viewDidLoad() + + view.backgroundColor = .systemBackground + view.addSubview(stackView) + NSLayoutConstraint.activate([ + stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor), + stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor), + stackView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.7), + loginButton.heightAnchor.constraint(equalToConstant: 40), + signupButton.heightAnchor.constraint(equalToConstant: 40) + ]) + } + + func textChanged(_ text: String?) { + guard let email = emailField.text, let pass = passwordField.text else { + loginButton.isEnabled = false + signupButton.isEnabled = false + return + } + + let enabled = email.count >= 5 && pass.count >= 5 + loginButton.isEnabled = enabled + signupButton.isEnabled = enabled + } + + // MARK: - Button handlers + + func loginTapped() async { + guard let email = emailField.text, let password = passwordField.text else { + return + } + + do { + HUD.show(.progress) + Settings.shared.user = try await Api.shared.login(email: email, password: password) + view.window?.rootViewController = MainTabController() + HUD.hide() + } catch { + HUD.hide() + show(error: error) + } + } + + func signupTapped() async { + guard let email = emailField.text, let password = passwordField.text else { + return + } + + do { + HUD.show(.progress) + Settings.shared.user = try await Api.shared.signup(email: email, password: password) + view.window?.rootViewController = MainTabController() + HUD.hide() + } catch { + HUD.hide() + show(error: error) + } + } +} diff --git a/AutoCat2/Controllers/CheckController.swift b/AutoCat2/Controllers/CheckController.swift new file mode 100644 index 0000000..fcf88f7 --- /dev/null +++ b/AutoCat2/Controllers/CheckController.swift @@ -0,0 +1,112 @@ +// +// CheckController.swift +// AutoCat2 +// +// Created by Selim Mustafaev on 20.03.2022. +// + +import UIKit + +class CheckController: UIViewController { + + public var onCheck: ((String) -> Void)? + + private lazy var keyboardView: PNKeyboard = { + let keyboard = PNKeyboard(target: self.numberField, insets: .zero) + keyboard.delegate = self + return keyboard + }() + + private lazy var numberField: SwiftMaskTextfield = { + let textField = SwiftMaskTextfield() + textField.formatPattern = "@###@@###" + textField.placeholder = "A001AA 777" + textField.borderStyle = .roundedRect + textField.font = .preferredFont(forTextStyle: .largeTitle) + textField.textAlignment = .center + textField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged) + return textField + }() + + private let plateView = PlateView(frame: .zero) + + private lazy var checkButton = ACButton(title: "Check", onTap: check) + .enable(false) + + private lazy var stackView: UIStackView = { + let stack = UIStackView(arrangedSubviews: [plateView, checkButton]) + stack.axis = .horizontal + stack.spacing = 16 + stack.translatesAutoresizingMaskIntoConstraints = false + return stack + }() + + private let titleLabel: UILabel = { + let label = UILabel() + label.text = "Check new plate number" + label.font = UIFont.preferredFont(forTextStyle: .headline) + label.textAlignment = .center + label.translatesAutoresizingMaskIntoConstraints = false + return label + }() + + private lazy var mainStackView: UIStackView = { + let stack = UIStackView(arrangedSubviews: [titleLabel, stackView, keyboardView]) + stack.axis = .vertical + stack.spacing = 16 + stack.translatesAutoresizingMaskIntoConstraints = false + return stack + }() + + override func viewDidLoad() { + super.viewDidLoad() + + //view.backgroundColor = .systemBackground + checkButton.contentEdgeInsets = .init(top: 0, left: 8, bottom: 0, right: 8) + + view.addSubview(mainStackView) + mainStackView.pin(to: view, insets: .init(top: 16, left: 16, bottom: 16, right: 16)) + + NSLayoutConstraint.activate([ + plateView.heightAnchor.constraint(equalToConstant: 40) + ]) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + DispatchQueue.main.asyncAfter(deadline: .now() + 2) { + self.numberField.becomeFirstResponder() + } + } + + func check() { + guard let number = numberField.text else { + return + } + + let numberNormalized = number.filter { !$0.isWhitespace }.uppercased() + numberField.resignFirstResponder() + onCheck?(numberNormalized) + } +} + +extension CheckController: PNKeyboardDelegate { + + func returnClicked() { + check() + } +} + +extension CheckController: UITextFieldDelegate { + + @objc func textFieldDidChange(_ textField: UITextField) { + guard let text = textField.text else { + self.checkButton.isEnabled = false + return + } + + self.checkButton.isEnabled = text.count >= 8 + } + +} diff --git a/AutoCat2/Controllers/HistoryController.swift b/AutoCat2/Controllers/HistoryController.swift new file mode 100644 index 0000000..f45bc9e --- /dev/null +++ b/AutoCat2/Controllers/HistoryController.swift @@ -0,0 +1,37 @@ +// +// Created by Selim Mustafaev on 13.03.2022. +// + +import UIKit +import PKHUD +import AutoCatCore + +class HistoryController: UIViewController { + + + private lazy var tableView: UITableView = { + let table = UITableView() + table.translatesAutoresizingMaskIntoConstraints = false + return table + }() + + override func viewDidLoad() { + super.viewDidLoad() + + title = "Check history" + + view.addSubview(tableView) + tableView.pin(to: view) + } + + func checkPlateNumber(_ number: String) async { + print("Check plate number: ", number) + + do { + HUD.show(.progress) + try await VehicleService.shared.check(plateNumber: number, force: false) + } catch { + show(error: error) + } + } +} diff --git a/AutoCat2/Controllers/MainTabController.swift b/AutoCat2/Controllers/MainTabController.swift new file mode 100644 index 0000000..ee59ade --- /dev/null +++ b/AutoCat2/Controllers/MainTabController.swift @@ -0,0 +1,62 @@ +// +// Created by Selim Mustafaev on 06.03.2022. +// + +import UIKit +import SwiftEntryKit + +class MainTabController: UITabBarController, UITabBarControllerDelegate { + + // Dummy controller, tabbar item will be used as a button + private let addController = UIViewController() + + let historyController = HistoryController() + + override func viewDidLoad() { + super.viewDidLoad() + + self.view.backgroundColor = .systemBackground + self.delegate = self + + let historyNavController = UINavigationController(rootViewController: historyController) + historyNavController.tabBarItem = UITabBarItem(title: "Check", image: UIImage(systemName: "eye"), selectedImage: UIImage(systemName: "eye.fill")) + + let settingsController = SettingsController() + settingsController.tabBarItem = UITabBarItem(title: "Settings", image: UIImage(systemName: "gear"), selectedImage: nil) + + addController.tabBarItem = UITabBarItem(title: "New", image: UIImage(systemName: "plus"), selectedImage: nil) + + self.viewControllers = [historyNavController, addController, settingsController] + } + + func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool { + if viewController == addController { + showCheckPuller() + return false + } else { + return true + } + } + + func showCheckPuller() { + var attributes = EKAttributes.bottomToast + attributes.displayDuration = .infinity + attributes.positionConstraints.keyboardRelation = .bind(offset: .none) + attributes.entryBackground = .color(color: .standardBackground) + attributes.screenBackground = .color(color: EKColor(UIColor(white: 0.3, alpha: 0.5))) + attributes.roundCorners = .top(radius: 24) + attributes.screenInteraction = .dismiss + attributes.entryInteraction = .forward + //attributes.shadow = .active(with: .init(color: .black, opacity: 0.2, radius: 24, offset: .zero)) + + let checkController = CheckController() + checkController.onCheck = { number in + SwiftEntryKit.dismiss() + Task { + await self.historyController.checkPlateNumber(number) + } + } + + SwiftEntryKit.display(entry: checkController, using: attributes) + } +} diff --git a/AutoCat2/Controllers/SettingsController.swift b/AutoCat2/Controllers/SettingsController.swift new file mode 100644 index 0000000..30d0c48 --- /dev/null +++ b/AutoCat2/Controllers/SettingsController.swift @@ -0,0 +1,12 @@ +// +// Created by Selim Mustafaev on 13.03.2022. +// + +import UIKit + +class SettingsController: UIViewController { + + override func viewDidLoad() { + super.viewDidLoad() + } +} diff --git a/Shared/Fonts/RoadNumbers.otf b/AutoCat2/Fonts/RoadNumbers.otf similarity index 100% rename from Shared/Fonts/RoadNumbers.otf rename to AutoCat2/Fonts/RoadNumbers.otf diff --git a/Shared/Fonts/RoadNumbers2.0.otf b/AutoCat2/Fonts/RoadNumbers2.0.otf similarity index 100% rename from Shared/Fonts/RoadNumbers2.0.otf rename to AutoCat2/Fonts/RoadNumbers2.0.otf diff --git a/AutoCat2/Info.plist b/AutoCat2/Info.plist new file mode 100644 index 0000000..73daeb7 --- /dev/null +++ b/AutoCat2/Info.plist @@ -0,0 +1,30 @@ + + + + + UIAppFonts + + RoadNumbers.otf + RoadNumbers2.0.otf + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + UISceneStoryboardFile + Main + + + + + + diff --git a/AutoCat2/SceneDelegate.swift b/AutoCat2/SceneDelegate.swift new file mode 100644 index 0000000..db94ff6 --- /dev/null +++ b/AutoCat2/SceneDelegate.swift @@ -0,0 +1,63 @@ +// +// SceneDelegate.swift +// AutoCat2 +// +// Created by Selim Mustafaev on 05.03.2022. +// + +import UIKit +import AutoCatCore + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + // 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 scene = (scene as? UIWindowScene) else { return } + + self.window = UIWindow(windowScene: scene) + + if Settings.shared.user.token.isEmpty { + self.window?.rootViewController = AuthController() + } else { + self.window?.rootViewController = MainTabController() + } + + self.window?.makeKeyAndVisible() + } + + func sceneDidDisconnect(_ scene: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_ scene: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_ scene: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_ scene: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_ scene: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + } + + +} + diff --git a/AutoCat2/ThirdParty/CenterTextLayer.swift b/AutoCat2/ThirdParty/CenterTextLayer.swift new file mode 100644 index 0000000..8fd0a12 --- /dev/null +++ b/AutoCat2/ThirdParty/CenterTextLayer.swift @@ -0,0 +1,51 @@ +// +// CenterTextLayer.swift +// CenterTextLayer +// +// Created by Cem Olcay on 12/04/2017. +// +// http://stackoverflow.com/a/41518502/2048130 +// + +#if os(iOS) || os(tvOS) + import UIKit +#elseif os(OSX) + import AppKit +#endif + +public class CenterTextLayer: CATextLayer { + + var heightCoefficient: CGFloat = 1 + + public override init() { + super.init() + } + + public override init(layer: Any) { + super.init(layer: layer) + } + + public required init(coder aDecoder: NSCoder) { + super.init(layer: aDecoder) + } + + public init(coeff: CGFloat) { + super.init() + self.heightCoefficient = coeff + } + + public override func draw(in ctx: CGContext) { + #if os(iOS) || os(tvOS) + let multiplier = CGFloat(1) + #elseif os(OSX) + let multiplier = CGFloat(-1) + #endif + let h = ((string as? NSAttributedString)?.size().height ?? fontSize)*heightCoefficient; + let yDiff = (bounds.size.height - h) / 2 * multiplier + + ctx.saveGState() + ctx.translateBy(x: 0.0, y: yDiff) + super.draw(in: ctx) + ctx.restoreGState() + } +} diff --git a/AutoCat2/ThirdParty/SwiftMaskTextfield.swift b/AutoCat2/ThirdParty/SwiftMaskTextfield.swift new file mode 100644 index 0000000..706d47d --- /dev/null +++ b/AutoCat2/ThirdParty/SwiftMaskTextfield.swift @@ -0,0 +1,269 @@ +/* + * MIT License + * + * Copyright (c) 2016 Gabriel Maccori Kozma + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import UIKit + +//********************************************************************************************************** +// +// MARK: - Constants - +// +//********************************************************************************************************** + +//********************************************************************************************************** +// +// MARK: - Definitions - +// +//********************************************************************************************************** + +//********************************************************************************************************** +// +// MARK: - Class - +// +//********************************************************************************************************** + +open class SwiftMaskTextfield : UITextField { + +//************************************************** +// MARK: - Properties +//************************************************** + + public let lettersAndDigitsReplacementChar: String = "*" + public let anyLetterReplecementChar: String = "@" + public let lowerCaseLetterReplecementChar: String = "a" + public let upperCaseLetterReplecementChar: String = "A" + public let digitsReplecementChar: String = "#" + + /** + Var that holds the format pattern that you wish to apply + to some text + + If the pattern is set to "" no mask would be applied and + the textfield would behave like a normal one + */ + @IBInspectable open var formatPattern: String = "" + + /** + Var that holds the prefix to be added to the textfield + + If the prefix is set to "" no string will be added to the beggining + of the text + */ + @IBInspectable open var prefix: String = "" + + /** + Var that have the maximum length, based on the mask set + */ + open var maxLength: Int { + get { + return formatPattern.count + prefix.count + } + } + + /** + Overriding the var text from UITextField so if any text + is applied programmatically by calling formatText + */ + override open var text: String? { + set { + super.text = newValue + self.formatText() + } + + get { + return super.text + } + } + +//************************************************** +// MARK: - Constructors +//************************************************** + + public required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + self.setup() + } + + public override init(frame: CGRect) { + super.init(frame: frame) + self.setup() + } + + deinit { + self.deRegisterForNotifications() + } + +//************************************************** +// MARK: - Private Methods +//************************************************** + + fileprivate func setup() { + self.registerForNotifications() + } + + fileprivate func registerForNotifications() { + NotificationCenter.default.addObserver(self, + selector: #selector(textDidChange), + name: NSNotification.Name(rawValue: "UITextFieldTextDidChangeNotification"), + object: self) + } + + fileprivate func deRegisterForNotifications() { + NotificationCenter.default.removeObserver(self) + } + + @objc fileprivate func textDidChange() { + self.undoManager?.removeAllActions() + self.formatText() + } + + fileprivate func getOnlyDigitsString(_ string: String) -> String { + let charactersArray = string.components(separatedBy: CharacterSet.decimalDigits.inverted) + return charactersArray.joined(separator: "") + } + + fileprivate func getOnlyLettersString(_ string: String) -> String { + let charactersArray = string.components(separatedBy: CharacterSet.letters.inverted) + return charactersArray.joined(separator: "") + } + + fileprivate func getUppercaseLettersString(_ string: String) -> String { + let charactersArray = string.components(separatedBy: CharacterSet.uppercaseLetters.inverted) + return charactersArray.joined(separator: "") + } + + fileprivate func getLowercaseLettersString(_ string: String) -> String { + let charactersArray = string.components(separatedBy: CharacterSet.lowercaseLetters.inverted) + return charactersArray.joined(separator: "") + } + + fileprivate func getFilteredString(_ string: String) -> String { + let charactersArray = string.components(separatedBy: CharacterSet.alphanumerics.inverted) + return charactersArray.joined(separator: "") + } + + fileprivate func getStringWithoutPrefix(_ string: String) -> String { + if string.range(of: self.prefix) != nil { + if string.count > self.prefix.count { + let prefixIndex = string.index(string.endIndex, offsetBy: (string.count - self.prefix.count) * -1) + return String(string[prefixIndex...]) + } else if string.count == self.prefix.count { + return "" + } + + } + return string + } + +//************************************************** +// MARK: - Self Public Methods +//************************************************** + + /** + Func that formats the text based on formatPattern + + Override this function if you want to customize the behaviour of + the class + */ + open func formatText() { + var currentTextForFormatting = "" + + if let text = super.text { + if text.count > 0 { + currentTextForFormatting = self.getStringWithoutPrefix(text) + } + } + + if self.maxLength > 0 { + var formatterIndex = self.formatPattern.startIndex, currentTextForFormattingIndex = currentTextForFormatting.startIndex + var finalText = "" + + currentTextForFormatting = self.getFilteredString(currentTextForFormatting) + + if currentTextForFormatting.count > 0 { + while true { + let formatPatternRange = formatterIndex ..< formatPattern.index(after: formatterIndex) + let currentFormatCharacter = String(self.formatPattern[formatPatternRange]) + + let currentTextForFormattingPatterRange = currentTextForFormattingIndex ..< currentTextForFormatting.index(after: currentTextForFormattingIndex) + let currentTextForFormattingCharacter = String(currentTextForFormatting[currentTextForFormattingPatterRange]) + + switch currentFormatCharacter { + case self.lettersAndDigitsReplacementChar: + finalText += currentTextForFormattingCharacter + currentTextForFormattingIndex = currentTextForFormatting.index(after: currentTextForFormattingIndex) + formatterIndex = formatPattern.index(after: formatterIndex) + case self.anyLetterReplecementChar: + let filteredChar = self.getOnlyLettersString(currentTextForFormattingCharacter) + if !filteredChar.isEmpty { + finalText += filteredChar + formatterIndex = formatPattern.index(after: formatterIndex) + } + currentTextForFormattingIndex = currentTextForFormatting.index(after: currentTextForFormattingIndex) + case self.lowerCaseLetterReplecementChar: + let filteredChar = self.getLowercaseLettersString(currentTextForFormattingCharacter) + if !filteredChar.isEmpty { + finalText += filteredChar + formatterIndex = formatPattern.index(after: formatterIndex) + } + currentTextForFormattingIndex = currentTextForFormatting.index(after: currentTextForFormattingIndex) + case self.upperCaseLetterReplecementChar: + let filteredChar = self.getUppercaseLettersString(currentTextForFormattingCharacter) + if !filteredChar.isEmpty { + finalText += filteredChar + formatterIndex = formatPattern.index(after: formatterIndex) + } + currentTextForFormattingIndex = currentTextForFormatting.index(after: currentTextForFormattingIndex) + case self.digitsReplecementChar: + let filteredChar = self.getOnlyDigitsString(currentTextForFormattingCharacter) + if !filteredChar.isEmpty { + finalText += filteredChar + formatterIndex = formatPattern.index(after: formatterIndex) + } + currentTextForFormattingIndex = currentTextForFormatting.index(after: currentTextForFormattingIndex) + default: + finalText += currentFormatCharacter + formatterIndex = formatPattern.index(after: formatterIndex) + } + + if formatterIndex >= self.formatPattern.endIndex || + currentTextForFormattingIndex >= currentTextForFormatting.endIndex { + break + } + } + } + + if finalText.count > 0 { + super.text = "\(self.prefix)\(finalText)" + } else { + super.text = finalText + } + + if let text = self.text { + if text.count > self.maxLength { + super.text = String(text[text.index(text.startIndex, offsetBy: self.maxLength)]) + } + } + } + } +} diff --git a/AutoCat2/ViewController.swift b/AutoCat2/ViewController.swift new file mode 100644 index 0000000..14df048 --- /dev/null +++ b/AutoCat2/ViewController.swift @@ -0,0 +1,19 @@ +// +// ViewController.swift +// AutoCat2 +// +// Created by Selim Mustafaev on 05.03.2022. +// + +import UIKit + +class ViewController: UIViewController { + + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view. + } + + +} + diff --git a/AutoCat2Tests/Api/Lib/ApiMethodMockProtocol.swift b/AutoCat2Tests/Api/Lib/ApiMethodMockProtocol.swift deleted file mode 100644 index bf45947..0000000 --- a/AutoCat2Tests/Api/Lib/ApiMethodMockProtocol.swift +++ /dev/null @@ -1,7 +0,0 @@ -import Foundation - -protocol ApiMethodMockProtocol { - var path: String { get } - var httpMethod: String { get } - func response(headers: [String: String], params: [String: Any]) -> (status: Int, data: Data?) -} diff --git a/AutoCat2Tests/Api/Lib/MockURLProtocol.swift b/AutoCat2Tests/Api/Lib/MockURLProtocol.swift deleted file mode 100644 index b60320c..0000000 --- a/AutoCat2Tests/Api/Lib/MockURLProtocol.swift +++ /dev/null @@ -1,96 +0,0 @@ -import Foundation - -extension URL { - public var queryParameters: [String: String]? { - guard - let components = URLComponents(url: self, resolvingAgainstBaseURL: true), - let queryItems = components.queryItems else { return nil } - return queryItems.reduce(into: [String: String]()) { (result, item) in - result[item.name] = item.value - } - } -} - -extension URLRequest { - func bodySteamAsJSON() -> [String: Any]? { - guard let bodyStream = self.httpBodyStream else { return nil } - - bodyStream.open() - - // Will read 16 chars per iteration. Can use bigger buffer if needed - let bufferSize: Int = 16 - let buffer = UnsafeMutablePointer.allocate(capacity: bufferSize) - var dat = Data() - - while bodyStream.hasBytesAvailable { - let readDat = bodyStream.read(buffer, maxLength: bufferSize) - dat.append(buffer, count: readDat) - } - - buffer.deallocate() - bodyStream.close() - - return try? JSONSerialization.jsonObject(with: dat, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: Any] - } -} - -class MockURLProtocol: URLProtocol { - - static var baseUrl: String = "" - static var apiMethodMocks: [ApiMethodMockProtocol] = [] - - override class func canInit(with request: URLRequest) -> Bool { - return true - } - - override class func canonicalRequest(for request: URLRequest) -> URLRequest { - return request - } - - override func startLoading() { - guard let requestUrl = request.url else { return } - - let methodMock = MockURLProtocol.apiMethodMocks.first { - return request.url?.absoluteString == MockURLProtocol.baseUrl + $0.path - && request.httpMethod == $0.httpMethod - } - - guard let methodMock = methodMock else { - if let response = HTTPURLResponse(url: requestUrl, statusCode: 404, httpVersion: "HTTP/2", headerFields: [:]) { - client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed) - client?.urlProtocolDidFinishLoading(self) - } - - return - } - - // Assuming we use url parameters in GET requests and JSON-encoded body in everything else - var params: [String: Any] = [:] - if request.httpBodyStream != nil { - if let bodyDict = request.bodySteamAsJSON() { - params = bodyDict - } - } else { - if let urlParams = requestUrl.queryParameters { - params = urlParams - } - } - - let result = methodMock.response(headers: request.allHTTPHeaderFields ?? [:], params: params) - guard let response = HTTPURLResponse(url: requestUrl, statusCode: result.status, httpVersion: "HTTP/2", headerFields: [:]) else { return } - - client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed) - - if let data = result.data { - client?.urlProtocol(self, didLoad: data) - } - - client?.urlProtocolDidFinishLoading(self) - - //client?.urlProtocol(self, didFailWithError: error) - } - - override func stopLoading() { - - } -} diff --git a/AutoCat2Tests/Api/Mocks/ApiMethodMock.swift b/AutoCat2Tests/Api/Mocks/ApiMethodMock.swift deleted file mode 100644 index f4e82ff..0000000 --- a/AutoCat2Tests/Api/Mocks/ApiMethodMock.swift +++ /dev/null @@ -1,39 +0,0 @@ -import Foundation -import AutoCat2 - -open class ApiMethodMock: ApiMethodMockProtocol { - - private(set) var path: String - private(set) var httpMethod: String - - init(httpMethod: String, path: String) { - self.httpMethod = httpMethod - self.path = path - } - - func readData(from path: String) -> Data? { - guard let url = Bundle(for: type(of: self)).url(forResource: path, withExtension: "json") else { return nil } - return try? Data(contentsOf: url) - } - - func error(message: String, code: ApiErrorCode? = nil) -> Data? { - var errorData: [String: AnyEncodable] = [ - "success": false, - "error": AnyEncodable(message) - ] - - if let code = code { - errorData["errorCode"] = AnyEncodable(code) - } - - return try? JSONEncoder().encode(errorData) - } - - func notFoundResponse() -> (status: Int, data: Data?) { - return (status: 404, data: self.error(message: "Not found")) - } - - open func response(headers: [String : String], params: [String : Any]) -> (status: Int, data: Data?) { - return self.notFoundResponse() - } -} diff --git a/AutoCat2Tests/Api/Mocks/LoginMethodMock.swift b/AutoCat2Tests/Api/Mocks/LoginMethodMock.swift deleted file mode 100644 index 837f49b..0000000 --- a/AutoCat2Tests/Api/Mocks/LoginMethodMock.swift +++ /dev/null @@ -1,24 +0,0 @@ -import Foundation - -class LoginMethodMock: ApiMethodMock { - private var login: String - private var password: String - - init(httpMethod: String, path: String, login: String, password: String) { - self.login = login - self.password = password - super.init(httpMethod: httpMethod, path: path) - } - - override func response(headers: [String : String], params: [String : Any]) -> (status: Int, data: Data?) { - guard let login = params["email"] as? String, let password = params["password"] as? String else { - return (status: 400, data: self.error(message: "Invalid parameters")) - } - - if login != self.login || password != self.password { - return (status: 200, data: self.error(message: "Incorrect login or password", code: .invalidLoginOrPassword)) - } - - return (status: 200, data: readData(from: "login_success")) - } -} diff --git a/AutoCat2Tests/Api/Responses/login_success.json b/AutoCat2Tests/Api/Responses/login_success.json deleted file mode 100644 index 60c13de..0000000 --- a/AutoCat2Tests/Api/Responses/login_success.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "success": true, - "data": { - "_id": "832c1bd4-5caa-4c9d-b24c-4c000cd8a793", - "email": "selim@fastmail.fm", - "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InNlbGltQGZhc3RtYWlsLmZtIiwiaWF0IjoxNjI2MjgwNDgxLCJleHAiOjE2NTc4MTY0ODF9.eU6wpacgCSnhM4EiyMY2lUptsfSfHz9guuvOsAw4X90" - } -} diff --git a/AutoCat2Tests/ApiTests.swift b/AutoCat2Tests/ApiTests.swift deleted file mode 100644 index df060d4..0000000 --- a/AutoCat2Tests/ApiTests.swift +++ /dev/null @@ -1,45 +0,0 @@ -import XCTest -import AutoCat2 - -class ApiTests: XCTestCase { - - private var api: ApiProtocol! - - private let testLogin = "test@gmail.com" - private let testPassword = "12345" - - override func setUpWithError() throws { - MockURLProtocol.baseUrl = Constants.baseUrl - MockURLProtocol.apiMethodMocks = [ - LoginMethodMock(httpMethod: "POST", path: "user/login", login: self.testLogin, password: self.testPassword) - ] - - let sessionConfig = URLSessionConfiguration.default - sessionConfig.protocolClasses = [MockURLProtocol.self] - let session = URLSession(configuration: sessionConfig) - self.api = Api(session: session) - } - - override func tearDownWithError() throws { - MockURLProtocol.baseUrl = "" - MockURLProtocol.apiMethodMocks = [] - } - - func testLoginSuccess() async throws { - let user = try await self.api.login(email: self.testLogin, password: self.testPassword) - XCTAssertTrue(!user.token.isEmpty) - } - - func testLoginInvalidParams() async throws { - do { - _ = try await self.api.login(email: "", password: "") - } catch let error as ApiError { - XCTAssertTrue(error.code == .invalidLoginOrPassword) - return - } catch { - XCTFail("Wrong exception type") - } - - XCTFail("Exception expected") - } -} diff --git a/AutoCat2Tests/AutoCat2Tests.swift b/AutoCat2Tests/AutoCat2Tests.swift new file mode 100644 index 0000000..bfee03a --- /dev/null +++ b/AutoCat2Tests/AutoCat2Tests.swift @@ -0,0 +1,36 @@ +// +// AutoCat2Tests.swift +// AutoCat2Tests +// +// Created by Selim Mustafaev on 05.03.2022. +// + +import XCTest +@testable import AutoCat2 + +class AutoCat2Tests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + // Any test you write for XCTest can be annotated as throws and async. + // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. + // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. + } + + func testPerformanceExample() throws { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/AutoCat2Tests/SettingsTests.swift b/AutoCat2Tests/SettingsTests.swift deleted file mode 100644 index b290da7..0000000 --- a/AutoCat2Tests/SettingsTests.swift +++ /dev/null @@ -1,67 +0,0 @@ -import XCTest -import AutoCat2 - -class SettingsTests: XCTestCase { - - private var settings: SettingsProtocol! - - override func setUpWithError() throws { - guard let userDefaults = UserDefaults(suiteName: #file) else { - throw CocoaError.error("Failed to create UserDefaults") - } - userDefaults.removePersistentDomain(forName: #file) - self.settings = Settings(defaults: userDefaults) - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testAlternativeOrder() { - XCTAssert(self.settings.recognizeAlternativeOrder == false, "recognizeAlternativeOrder: wrong default value") - self.settings.recognizeAlternativeOrder = true - XCTAssert(self.settings.recognizeAlternativeOrder == true, "recognizeAlternativeOrder: failed to change value") - } - - func testShortenedNumbers() { - XCTAssert(self.settings.recognizeShortenedNumbers == false, "recognizeShortenedNumbers: wrong default value") - self.settings.recognizeShortenedNumbers = true - XCTAssert(self.settings.recognizeShortenedNumbers == true, "recognizeShortenedNumbers: failed to change value") - } - - func testDefaultRegion() { - XCTAssert(self.settings.defaultRegion == "161", "defaultRegion: wrong default value") - self.settings.defaultRegion = "761" - XCTAssert(self.settings.defaultRegion == "761", "defaultRegion: failed to change value") - } - - func testRecordBeep() { - XCTAssert(self.settings.recordBeep == false, "recordBeep: wrong default value") - self.settings.recordBeep = true - XCTAssert(self.settings.recordBeep == true, "recordBeep: failed to change value") - } - - func testDebugInfo() { - XCTAssert(self.settings.showDebugInfo == false, "showDebugInfo: wrong default value") - self.settings.showDebugInfo = true - XCTAssert(self.settings.showDebugInfo == true, "showDebugInfo: failed to change value") - } - - func testDefaultUser() { - XCTAssert(self.settings.user.email.isEmpty, "Default user email is not empty") - XCTAssert(self.settings.user.token.isEmpty, "Default user token is not empty") - XCTAssert(self.settings.user.firebaseIdToken == nil, "Default user firebase ID token is not nil") - XCTAssert(self.settings.user.firebaseRefreshToken == nil, "Default user firebase refresh token is not nil") - } - - func testSaveUser() { - self.settings.user.token = "TestToken" - XCTAssert(self.settings.user.token == "TestToken", "Failed to save user token to settings") - - self.settings.user.firebaseIdToken = "TestFirebaseToken" - XCTAssert(self.settings.user.firebaseIdToken == "TestFirebaseToken", "Failed to save user firebaseIdToken to settings") - - self.settings.user.firebaseRefreshToken = "TestResreshToken" - XCTAssert(self.settings.user.firebaseRefreshToken == "TestResreshToken", "Failed to save user firebaseRefreshToken to settings") - } -} diff --git a/AutoCat2Tests/SettingsTests.swift.plist b/AutoCat2Tests/SettingsTests.swift.plist deleted file mode 100644 index 4ac7f85..0000000 Binary files a/AutoCat2Tests/SettingsTests.swift.plist and /dev/null differ diff --git a/Tests macOS/Tests_macOS.swift b/AutoCat2UITests/AutoCat2UITests.swift similarity index 91% rename from Tests macOS/Tests_macOS.swift rename to AutoCat2UITests/AutoCat2UITests.swift index c8e41f7..4b40741 100644 --- a/Tests macOS/Tests_macOS.swift +++ b/AutoCat2UITests/AutoCat2UITests.swift @@ -1,13 +1,13 @@ // -// Tests_macOS.swift -// Tests macOS +// AutoCat2UITests.swift +// AutoCat2UITests // -// Created by Selim Mustafaev on 04.07.2021. +// Created by Selim Mustafaev on 05.03.2022. // import XCTest -class Tests_macOS: XCTestCase { +class AutoCat2UITests: XCTestCase { override func setUpWithError() throws { // Put setup code here. This method is called before the invocation of each test method in the class. diff --git a/AutoCat2UITests/AutoCat2UITestsLaunchTests.swift b/AutoCat2UITests/AutoCat2UITestsLaunchTests.swift new file mode 100644 index 0000000..4b30d7e --- /dev/null +++ b/AutoCat2UITests/AutoCat2UITestsLaunchTests.swift @@ -0,0 +1,32 @@ +// +// AutoCat2UITestsLaunchTests.swift +// AutoCat2UITests +// +// Created by Selim Mustafaev on 05.03.2022. +// + +import XCTest + +class AutoCat2UITestsLaunchTests: XCTestCase { + + override class var runsForEachTargetApplicationUIConfiguration: Bool { + true + } + + override func setUpWithError() throws { + continueAfterFailure = false + } + + func testLaunch() throws { + let app = XCUIApplication() + app.launch() + + // Insert steps here to perform after app launch but before taking a screenshot, + // such as logging into a test account or navigating somewhere in the app + + let attachment = XCTAttachment(screenshot: app.screenshot()) + attachment.name = "Launch Screen" + attachment.lifetime = .keepAlways + add(attachment) + } +} diff --git a/Shared/AutoCat2.xcdatamodeld/.xccurrentversion b/AutoCatCore/AutoCat2.xcdatamodeld/.xccurrentversion similarity index 100% rename from Shared/AutoCat2.xcdatamodeld/.xccurrentversion rename to AutoCatCore/AutoCat2.xcdatamodeld/.xccurrentversion diff --git a/Shared/AutoCat2.xcdatamodeld/Shared.xcdatamodel/contents b/AutoCatCore/AutoCat2.xcdatamodeld/Shared.xcdatamodel/contents similarity index 81% rename from Shared/AutoCat2.xcdatamodeld/Shared.xcdatamodel/contents rename to AutoCatCore/AutoCat2.xcdatamodeld/Shared.xcdatamodel/contents index ef04a39..480e6a4 100644 --- a/Shared/AutoCat2.xcdatamodeld/Shared.xcdatamodel/contents +++ b/AutoCatCore/AutoCat2.xcdatamodeld/Shared.xcdatamodel/contents @@ -1,16 +1,16 @@ - - + + - + - + diff --git a/AutoCatCore/AutoCatCore.docc/AutoCatCore.md b/AutoCatCore/AutoCatCore.docc/AutoCatCore.md new file mode 100755 index 0000000..6cff9e5 --- /dev/null +++ b/AutoCatCore/AutoCatCore.docc/AutoCatCore.md @@ -0,0 +1,13 @@ +# ``AutoCatCore`` + +Summary + +## Overview + +Text + +## Topics + +### Group + +- ``Symbol`` \ No newline at end of file diff --git a/AutoCatCore/AutoCatCore.h b/AutoCatCore/AutoCatCore.h new file mode 100644 index 0000000..c9cf052 --- /dev/null +++ b/AutoCatCore/AutoCatCore.h @@ -0,0 +1,18 @@ +// +// AutoCatCore.h +// AutoCatCore +// +// Created by Selim Mustafaev on 05.03.2022. +// + +#import + +//! Project version number for AutoCatCore. +FOUNDATION_EXPORT double AutoCatCoreVersionNumber; + +//! Project version string for AutoCatCore. +FOUNDATION_EXPORT const unsigned char AutoCatCoreVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/Shared/Models/PlateNumber.swift b/AutoCatCore/Models/PlateNumber.swift similarity index 100% rename from Shared/Models/PlateNumber.swift rename to AutoCatCore/Models/PlateNumber.swift diff --git a/Shared/Models/Response.swift b/AutoCatCore/Models/Response.swift similarity index 85% rename from Shared/Models/Response.swift rename to AutoCatCore/Models/Response.swift index be792a0..c499b71 100644 --- a/Shared/Models/Response.swift +++ b/AutoCatCore/Models/Response.swift @@ -4,7 +4,7 @@ class Response: Decodable where T: Decodable { let success: Bool let data: T? let error: String? - let errorCode: ApiErrorCode? + let errorCode: Int? enum CodingKeys: String, CodingKey { case success @@ -22,7 +22,7 @@ class Response: Decodable where T: Decodable { errorCode = nil } else { error = try container.decode(String.self, forKey: .error) - errorCode = try container.decodeIfPresent(ApiErrorCode.self, forKey: .errorCode) + errorCode = try container.decodeIfPresent(Int.self, forKey: .errorCode) data = nil } } diff --git a/Shared/Models/Settings.swift b/AutoCatCore/Models/Settings.swift similarity index 100% rename from Shared/Models/Settings.swift rename to AutoCatCore/Models/Settings.swift diff --git a/Shared/Models/User.swift b/AutoCatCore/Models/User.swift similarity index 100% rename from Shared/Models/User.swift rename to AutoCatCore/Models/User.swift diff --git a/Shared/Models/VBrand.swift b/AutoCatCore/Models/VBrand.swift similarity index 100% rename from Shared/Models/VBrand.swift rename to AutoCatCore/Models/VBrand.swift diff --git a/Shared/Models/VName.swift b/AutoCatCore/Models/VName.swift similarity index 100% rename from Shared/Models/VName.swift rename to AutoCatCore/Models/VName.swift diff --git a/Shared/Models/Vehicle.swift b/AutoCatCore/Models/Vehicle.swift similarity index 100% rename from Shared/Models/Vehicle.swift rename to AutoCatCore/Models/Vehicle.swift diff --git a/Shared/Services/StorageService.swift b/AutoCatCore/Services/StorageService.swift similarity index 53% rename from Shared/Services/StorageService.swift rename to AutoCatCore/Services/StorageService.swift index 884e213..d7b2740 100644 --- a/Shared/Services/StorageService.swift +++ b/AutoCatCore/Services/StorageService.swift @@ -11,21 +11,37 @@ class StorageService: StorageServiceProtocol { private let container: NSPersistentCloudKitContainer - static let shared = StorageService() + static var shared: StorageService { + get async throws { + print("!!!!!!!!!!!!!!!!!!!!!!!!! StorageService init") + let service = StorageService() + try await service.loadPersistentStores() + return service + } + } init(inMemory: Bool = false) { - container = NSPersistentCloudKitContainer(name: "AutoCat2") + let bundle = Bundle(for: Self.self) + let url = bundle.url(forResource: "AutoCat2", withExtension: "momd") + let mom = NSManagedObjectModel(contentsOf: url!) + + container = NSPersistentCloudKitContainer(name: "AutoCat2", managedObjectModel: mom!) if inMemory { container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null") } - - container.loadPersistentStores(completionHandler: { (storeDescription, error) in - if let error = error as NSError? { - // TODO: Handle error properly - fatalError("Unresolved error \(error), \(error.userInfo)") - } - }) + } + + private func loadPersistentStores() async throws { + try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in + container.loadPersistentStores(completionHandler: { (storeDescription, error) in + if let error = error as NSError? { + continuation.resume(throwing: error) + } else { + continuation.resume(returning: ()) + } + }) + } } private func save() throws { diff --git a/Shared/Services/VehicleService.swift b/AutoCatCore/Services/VehicleService.swift similarity index 58% rename from Shared/Services/VehicleService.swift rename to AutoCatCore/Services/VehicleService.swift index 502096a..9c46535 100644 --- a/Shared/Services/VehicleService.swift +++ b/AutoCatCore/Services/VehicleService.swift @@ -5,14 +5,19 @@ protocol VehicleServiceProtocol { func check(plateNumber: String, force: Bool) async throws -> CDVehicle } -class VehicleService: VehicleServiceProtocol { +public class VehicleService: VehicleServiceProtocol { private let api: ApiProtocol private let storage: StorageServiceProtocol - static let shared = VehicleService() + public static var shared: VehicleService { + get async throws { + let storageService = try await StorageService.shared + return VehicleService(api: Api.shared, storage: storageService) + } + } - init(api: ApiProtocol = Api.shared, storage: StorageServiceProtocol = StorageService.shared) { + init(api: ApiProtocol = Api.shared, storage: StorageServiceProtocol) { self.api = api self.storage = storage @@ -20,7 +25,8 @@ class VehicleService: VehicleServiceProtocol { // MARK: - VehicleServiceProtocol - func check(plateNumber: String, force: Bool) async throws -> CDVehicle { + @discardableResult + public func check(plateNumber: String, force: Bool) async throws -> CDVehicle { let vehicle = try await api.check(plateNumber: plateNumber, force: force) return try storage.store(vehicle: vehicle) diff --git a/Shared/ThirdParty/AnyEncodable.swift b/AutoCatCore/ThirdParty/AnyEncodable.swift similarity index 100% rename from Shared/ThirdParty/AnyEncodable.swift rename to AutoCatCore/ThirdParty/AnyEncodable.swift diff --git a/Shared/Utils/Api.swift b/AutoCatCore/Utils/Api.swift similarity index 62% rename from Shared/Utils/Api.swift rename to AutoCatCore/Utils/Api.swift index bd62446..4288f41 100644 --- a/Shared/Utils/Api.swift +++ b/AutoCatCore/Utils/Api.swift @@ -30,10 +30,6 @@ public class Api: ApiProtocol { // 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(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 } @@ -60,38 +56,54 @@ public class Api: ApiProtocol { private func makeRequest(api: String, method: String = "GET", body: B?, params: [String:P]? = nil) async throws -> T where T: Decodable, B: Encodable, P: LosslessStringConvertible { guard let request = self.createRequest(api: api, method: method, body: body, params: params) else { - throw self.genError("Error creating request", suggestion: "") + throw ApiError.message("Error creating request") } - let (data, response) = try await self.session.data(for: request) - guard let httpResponse = response as? HTTPURLResponse else { - throw self.genError("non-HTTP response received", suggestion: "") - } - - // let str = String(data: data, encoding: .utf8) - // print("================================") - // if let string = str?.replacingOccurrences(of: "\\\"", with: "\"") - // .replacingOccurrences(of: "\\'", with: "'") - // .replacingOccurrences(of: "\\n", with: "") { - // print(string) - // } - // print("================================") - - do { - if data.count > 0 { - let resp = try JSONDecoder().decode(Response.self, from: data) - if resp.success { - return resp.data! - } else { - throw ApiError(httpStatus: httpResponse.statusCode, message: resp.error, code: resp.errorCode) + return try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in + + let task = self.session.dataTask(with: request) { data, response, error in + + if let error = error { + continuation.resume(throwing: error) + return + } + + guard let data = data, data.count > 0 else { + continuation.resume(throwing: ApiError.message("No data in response")) + return + } + + + // let str = String(data: data, encoding: .utf8) + // print("================================") + // if let string = str?.replacingOccurrences(of: "\\\"", with: "\"") + // .replacingOccurrences(of: "\\'", with: "'") + // .replacingOccurrences(of: "\\n", with: "") { + // print(string) + // } + // print("================================") + + do { + let resp = try JSONDecoder().decode(Response.self, from: data) + if resp.success { + continuation.resume(returning: resp.data!) + } else { + if let code = resp.errorCode { + continuation.resume(throwing: ApiError(code: code)) + } else if let errorMessage = resp.error { + continuation.resume(throwing: ApiError.message(errorMessage)) + } else { + continuation.resume(throwing: ApiError.generic) + } + } + } catch let error as Swift.DecodingError { + continuation.resume(throwing: ApiError.message((error as CustomDebugStringConvertible).debugDescription)) + } catch { + continuation.resume(throwing: error) } - } else { - throw ApiError(httpStatus: httpResponse.statusCode) } - } catch let error as Swift.DecodingError { - throw CocoaError.error((error as CustomDebugStringConvertible).debugDescription) - } catch { - throw error + + task.resume() } } @@ -113,6 +125,7 @@ public class Api: ApiProtocol { // MARK: - AutoCat public API + @MainActor public func login(email: String, password: String) async throws -> User { let body = [ @@ -122,7 +135,19 @@ public class Api: ApiProtocol { return try await self.makeBodyRequest(api: "user/login", body: body) } + + @MainActor + public func signup(email: String, password: String) async throws -> User { + + let body = [ + "email": email, + "password": password + ] + + return try await self.makeBodyRequest(api: "user/signup", body: body) + } + @MainActor public func check(plateNumber: String, force: Bool = false) async throws -> Vehicle { var body = [ diff --git a/AutoCatCore/Utils/ApiError.swift b/AutoCatCore/Utils/ApiError.swift new file mode 100644 index 0000000..8de2cb9 --- /dev/null +++ b/AutoCatCore/Utils/ApiError.swift @@ -0,0 +1,31 @@ +import Foundation + +public enum ApiError: LocalizedError { + + case generic + case message(String) + case httpError(Int) + case invalidLoginOrPassword + + public var errorDescription: String? { + switch self { + case .generic: + return "Something bad happened" + case .message(let message): + return message + case .httpError(let status): + return "General http error (status \(status))" + case .invalidLoginOrPassword: + return "Invalid login or password" + } + } + + init(code: Int) { + switch code { + case 0: + self = .invalidLoginOrPassword + default: + self = .generic + } + } +} diff --git a/Shared/Utils/Constants.swift b/AutoCatCore/Utils/Constants.swift similarity index 100% rename from Shared/Utils/Constants.swift rename to AutoCatCore/Utils/Constants.swift diff --git a/AutoCatCoreTests/AutoCatCoreTests.swift b/AutoCatCoreTests/AutoCatCoreTests.swift new file mode 100644 index 0000000..95ed638 --- /dev/null +++ b/AutoCatCoreTests/AutoCatCoreTests.swift @@ -0,0 +1,36 @@ +// +// AutoCatCoreTests.swift +// AutoCatCoreTests +// +// Created by Selim Mustafaev on 05.03.2022. +// + +import XCTest +@testable import AutoCatCore + +class AutoCatCoreTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + // Any test you write for XCTest can be annotated as throws and async. + // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. + // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. + } + + func testPerformanceExample() throws { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/Shared/Assets.xcassets/Colors/PlateBackgroundError.colorset/Contents.json b/Shared/Assets.xcassets/Colors/PlateBackgroundError.colorset/Contents.json deleted file mode 100644 index dcf9455..0000000 --- a/Shared/Assets.xcassets/Colors/PlateBackgroundError.colorset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "display-p3", - "components" : { - "alpha" : "1.000", - "blue" : "0.000", - "green" : "0.000", - "red" : "1.000" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "display-p3", - "components" : { - "alpha" : "1.000", - "blue" : "0.182", - "green" : "0.225", - "red" : "1.000" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Shared/AutoCat2App.swift b/Shared/AutoCat2App.swift deleted file mode 100644 index af0a06e..0000000 --- a/Shared/AutoCat2App.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// AutoCat2App.swift -// Shared -// -// Created by Selim Mustafaev on 04.07.2021. -// - -import SwiftUI - -@main -struct AutoCat2App: App { - let storageService = StorageService.shared - - var body: some Scene { - WindowGroup { - ContentView() - .environment(\.managedObjectContext, storageService.context) - } - } -} diff --git a/Shared/Extensions/Alert.swift b/Shared/Extensions/Alert.swift deleted file mode 100644 index b6d95d2..0000000 --- a/Shared/Extensions/Alert.swift +++ /dev/null @@ -1,28 +0,0 @@ -import Foundation -import SwiftUI - -enum AlertMessage: Identifiable { - case info(title: String, body: String) - case error(error: Error) - - var id: Int { - switch self { - case .info(let title, let body): - return title.hashValue + body.hashValue - case .error(let error): - return error.localizedDescription.hashValue - } - } -} - -extension Alert { - init(_ message: AlertMessage) { - switch message { - case .info(let title, let body): - self.init(title: Text(title), message: Text(body)) - case .error(let error): - let msg = (error as NSError).displayMessage - self.init(title: Text(msg.title), message: Text(msg.body)) - } - } -} diff --git a/Shared/Extensions/CocoaError.swift b/Shared/Extensions/CocoaError.swift deleted file mode 100644 index 94f74b0..0000000 --- a/Shared/Extensions/CocoaError.swift +++ /dev/null @@ -1,68 +0,0 @@ -import CoreLocation - -extension NSError { - public var displayMessage: (title: String, body: String) { -// if let description = self.userInfo[NSLocalizedDescriptionKey] as? String { -// return (title: "Error", body: description) -// } else if let failure = self.userInfo[NSLocalizedFailureErrorKey] as? String, let reason = self.localizedFailureReason { -// if let recovery = self.localizedRecoverySuggestion { -// return (title: failure, body: reason + "\n" + recovery) -// } else { -// return (title: failure, body: reason) -// } -// } else { -// return (title: "Error", body: "") -// } - - if let recovery = self.localizedRecoverySuggestion { - return (title: "Error", body: self.localizedDescription + "\n" + recovery) - } else { - return (title: "Error", body: self.localizedDescription) - } - } -} - -extension CocoaError { - public static func error(_ description: String) -> NSError { - return error(Code(rawValue: 0), userInfo: [NSLocalizedDescriptionKey: description], url: nil) as NSError - } - - public static func error(_ error: String, reason: String, suggestion: String? = nil) -> NSError { - var info = [ - NSLocalizedFailureErrorKey: error, - NSLocalizedFailureReasonErrorKey: reason - ] - - if let suggestion = suggestion { - info[NSLocalizedRecoverySuggestionErrorKey] = suggestion - } - - return self.error(Code(rawValue: 0), userInfo: info, url: nil) as NSError - } -} - -extension CLError.Code: CustomStringConvertible { - public var description: String { - switch self { - case .locationUnknown: return "Location unknown" - case .denied: return "Access denied" - case .network: return "general, network-related error" - case .headingFailure: return "heading could not be determined" - case .regionMonitoringDenied: return "Location region monitoring has been denied" - case .regionMonitoringSetupDelayed: return "CL could not immediately initialize region monitoring" - case .regionMonitoringResponseDelayed: return "While events for this fence will be delivered, delivery will not occur immediately" - case .geocodeFoundNoResult: return "A geocode request yielded no result" - case .geocodeFoundPartialResult: return "A geocode request yielded a partial result" - case .geocodeCanceled: return "A geocode request was cancelled" - case .deferredFailed: return "Deferred mode failed" - case .deferredNotUpdatingLocation: return "Deferred mode failed because location updates disabled or paused" - case .deferredAccuracyTooLow: return "Deferred mode not supported for the requested accuracy" - case .deferredDistanceFiltered: return "Deferred mode does not support distance filters" - case .deferredCanceled: return "Deferred mode request canceled a previous request" - case .rangingUnavailable: return "Ranging cannot be performed" - case .rangingFailure: return "General ranging failure" - case .promptDeclined: return "Authorization request not presented to user" - default: return "Unknown location error (\(self.rawValue)" - } - } -} diff --git a/Shared/Persistence.swift b/Shared/Persistence.swift deleted file mode 100644 index 395345d..0000000 --- a/Shared/Persistence.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// Persistence.swift -// Shared -// -// Created by Selim Mustafaev on 04.07.2021. -// - -import CoreData - -struct PersistenceController { - static let shared = PersistenceController() - - static var preview: PersistenceController = { - let result = PersistenceController(inMemory: true) - let viewContext = result.container.viewContext -// for _ in 0..<10 { -// let newItem = Item(context: viewContext) -// newItem.timestamp = Date() -// } - do { - try viewContext.save() - } catch { - // Replace this implementation with code to handle the error appropriately. - // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. - let nsError = error as NSError - fatalError("Unresolved error \(nsError), \(nsError.userInfo)") - } - return result - }() - - let container: NSPersistentCloudKitContainer - - init(inMemory: Bool = false) { - container = NSPersistentCloudKitContainer(name: "AutoCat2") - if inMemory { - container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null") - } - container.loadPersistentStores(completionHandler: { (storeDescription, error) in - if let error = error as NSError? { - // Replace this implementation with code to handle the error appropriately. - // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. - - /* - Typical reasons for an error here include: - * The parent directory does not exist, cannot be created, or disallows writing. - * The persistent store is not accessible, due to permissions or data protection when the device is locked. - * The device is out of space. - * The store could not be migrated to the current model version. - Check the error message to determine what the actual problem was. - */ - fatalError("Unresolved error \(error), \(error.userInfo)") - } - }) - } -} diff --git a/Shared/Utils/ApiError.swift b/Shared/Utils/ApiError.swift deleted file mode 100644 index b1be874..0000000 --- a/Shared/Utils/ApiError.swift +++ /dev/null @@ -1,35 +0,0 @@ -import Foundation - -public enum ApiErrorCode: Int, CustomStringConvertible, Codable { - case invalidLoginOrPassword = 0 - - public var description: String { - switch self { - case .invalidLoginOrPassword: return "Invalid login or password" - } - } -} - -public class ApiError: LocalizedError { - public let httpStatus: Int - public let message: String? - public let code: ApiErrorCode? - - init(httpStatus: Int, message: String? = nil, code: ApiErrorCode? = nil) { - self.httpStatus = httpStatus - self.message = message - self.code = code - } - - public var errorDescription: String? { - if let code = code { - return code.description - } - - if let message = message { - return message - } - - return "General http error (status \(self.httpStatus))" - } -} diff --git a/Shared/ViewModels/AuthVM.swift b/Shared/ViewModels/AuthVM.swift deleted file mode 100644 index 4a8cdeb..0000000 --- a/Shared/ViewModels/AuthVM.swift +++ /dev/null @@ -1,17 +0,0 @@ -import Foundation - -public class AuthVM: ObservableObject { - - private let api: ApiProtocol - private var settings: SettingsProtocol - - init(api: ApiProtocol = Api.shared, settings: SettingsProtocol = Settings.shared) { - - self.api = api - self.settings = settings - } - - public func login(user: String, password: String) async throws { - settings.user = try await api.login(email: user, password: password) - } -} diff --git a/Shared/Views/ACProgressView.swift b/Shared/Views/ACProgressView.swift deleted file mode 100644 index 2eed1cc..0000000 --- a/Shared/Views/ACProgressView.swift +++ /dev/null @@ -1,68 +0,0 @@ -import SwiftUI - -struct ACProgressView: View { - @State var isDeterminate: Bool = false - @State var progress: CGFloat = 0 - @State var text: String? = nil - @State var isAnimating: Bool = false - - var body: some View { - ZStack(alignment: .center) { - Color.black.opacity(0.4) - VStack { - VStack(spacing: 24) { - if self.isDeterminate { - ZStack { - Circle() - .stroke(Color.secondary.opacity(0.2), style: StrokeStyle(lineWidth: 4)) - .frame(width: 100, height: 100) - Circle() - .trim(from: 0, to: self.progress) - .stroke(Color.blue, style: StrokeStyle(lineWidth: 4)) - .rotationEffect(.degrees(-90)) - .frame(width: 100, height: 100) - Text("\(Int(self.progress*100)) %") - } - } else { - let gradient = AngularGradient( - gradient: Gradient(colors: [Color.blue, Color.blue.opacity(0.01)]), - center: .center, - startAngle: .degrees(360), - endAngle: .degrees(0)) - Circle() - .stroke(gradient, style: StrokeStyle(lineWidth: 4)) - .rotationEffect(Angle(degrees: self.isAnimating ? 360 : 0)) - .onAppear { - withAnimation(.linear(duration: 1).repeatForever(autoreverses: false)) { - self.isAnimating = true - } - } - .onDisappear(perform: { - self.isAnimating = false - }) - .frame(width: 100, height: 100) - } - - if let msg = self.text { - Text(msg) - } - } - .padding(28) - } - //.frame(width: 160, height: 160) - .background(.regularMaterial) - .cornerRadius(20) - } - .edgesIgnoringSafeArea(.all) - } -} - -struct ACProgressView_Previews: PreviewProvider { - static var previews: some View { - Group { - ACProgressView() - ACProgressView(isDeterminate: true, progress: 0.3, text: "Loading...") - .preferredColorScheme(.dark) - } - } -} diff --git a/Shared/Views/AuthView.swift b/Shared/Views/AuthView.swift deleted file mode 100644 index ed0b556..0000000 --- a/Shared/Views/AuthView.swift +++ /dev/null @@ -1,71 +0,0 @@ -import SwiftUI - -struct AuthView: View { - - enum Field { - case email - case password - } - - @ObservedObject var viewModel = AuthVM() - - @State private var login: String = "" - @State private var password: String = "" - @State private var showProgress: Bool = false - @State private var alert: AlertMessage? = nil - @FocusState private var focus: Field? - - var body: some View { - ZStack { - VStack(alignment: .center, spacing: 16) { - Spacer() - #if os(iOS) - TextField("Login", text: $login) - .focused($focus, equals: .email) - .textContentType(.emailAddress) - .keyboardType(.emailAddress) - #else - TextField("Login", text: $login) - .focused($focus, equals: .email) - #endif - SecureField("Password", text: $password) - .focused($focus, equals: .password) - Button("Login") { - Task.init { - do { - self.focus = nil - self.showProgress = true - try await self.viewModel.login(user: self.login, password: self.password) - self.showProgress = false - } catch { - self.showProgress = false - self.alert = .error(error: error) - } - } - } - .alert(item: $alert, content: Alert.init) - Spacer() - } - .buttonStyle(.bordered) - .controlSize(.large) - .textFieldStyle(.roundedBorder) - .padding(20) - - if self.showProgress { - ACProgressView() - } - } - } -} - -struct AuthView_Previews: PreviewProvider { - static var previews: some View { - Group { - AuthView() - .previewDevice("iPhone 8") - AuthView() - .preferredColorScheme(.dark) - .previewDevice("iPhone 8") - } - } -} diff --git a/Shared/Views/CheckView.swift b/Shared/Views/CheckView.swift deleted file mode 100644 index 8a58a50..0000000 --- a/Shared/Views/CheckView.swift +++ /dev/null @@ -1,35 +0,0 @@ -import SwiftUI - -struct CheckView: View { - @State private var number: String = "" - @FetchRequest(entity: CDVehicle.entity(), sortDescriptors: []) var vehicles: FetchedResults - - var body: some View { - NavigationView { - VStack(alignment: .center, spacing: 16) { - TextField("A123AA777", text: $number) - Button("Check") { - - } - List(vehicles) { vehicle in - Text(vehicle.number ?? "") - } - } - .toolbar { - ToolbarItem(placement: .primaryAction) { - Button("New") {} - } - } - #if os(iOS) - .navigationBarTitle("Title", displayMode: .inline) - #endif - } - } -} - -struct CheckView_Previews: PreviewProvider { - static var previews: some View { - CheckView() - .preferredColorScheme(.dark) - } -} diff --git a/Shared/Views/ContentView.swift b/Shared/Views/ContentView.swift deleted file mode 100644 index f1a3363..0000000 --- a/Shared/Views/ContentView.swift +++ /dev/null @@ -1,20 +0,0 @@ -import SwiftUI -import CoreData - -struct ContentView: View { - @StateObject var settings = Settings.shared - - var body: some View { - if settings.user.token.isEmpty { - AuthView() - } else { - MainView() - } - } -} - -struct ContentView_Previews: PreviewProvider { - static var previews: some View { - ContentView() - } -} diff --git a/Shared/Views/MainView.swift b/Shared/Views/MainView.swift deleted file mode 100644 index 5abb5de..0000000 --- a/Shared/Views/MainView.swift +++ /dev/null @@ -1,17 +0,0 @@ -import SwiftUI - -struct MainView: View { - var body: some View { - #if os(iOS) - MainViewSmall() - #else - MainViewBig() - #endif - } -} - -struct MainView_Previews: PreviewProvider { - static var previews: some View { - MainView() - } -} diff --git a/Shared/Views/MainViewSmall.swift b/Shared/Views/MainViewSmall.swift deleted file mode 100644 index 0facd81..0000000 --- a/Shared/Views/MainViewSmall.swift +++ /dev/null @@ -1,45 +0,0 @@ -import SwiftUI - -struct MainViewSmall: View { - var body: some View { - NavigationView { - TabView { - CheckView() - .tabItem { - Image(systemName: "eye") - Text("Check") - } - RecordsView() - .tabItem { - Image(systemName: "recordingtape") - Text("Records") - } - SearchView() - .tabItem { - Image(systemName: "magnifyingglass") - Text("Search") - } - SettingsView() - .tabItem { - Image(systemName: "gear") - Text("Settings") - } - } - #if os(iOS) - .navigationBarHidden(true) - #endif - Text("detail") - } - } -} - -struct MainViewSmall_Previews: PreviewProvider { - static var previews: some View { - Group { - MainViewSmall() - MainViewSmall() - .previewInterfaceOrientation(.landscapeLeft) - .previewDevice("iPad Pro (9.7-inch)") - } - } -} diff --git a/Shared/Views/PlateNumberView.swift b/Shared/Views/PlateNumberView.swift deleted file mode 100644 index efacb01..0000000 --- a/Shared/Views/PlateNumberView.swift +++ /dev/null @@ -1,67 +0,0 @@ -// -// PlateNumberView.swift -// AutoCat2 -// -// Created by Selim Mustafaev on 21.11.2021. -// - -import SwiftUI - -struct PlateNumberView: View { - - let number: PlateNumber - let unrecognized: Bool - let outdated: Bool - - private var fgColor: Color { - if unrecognized { - return Color("PlateBackgroundError") - } else { - return Color("PlateForeground") - } - } - - var body: some View { - ZStack { - RoundedRectangle(cornerRadius: 6) - .fill(fgColor) - GeometryReader { geometry in - HStack(alignment: .center, spacing: 2) { - ZStack { - RoundedRectangle(cornerRadius: 4) - .fill(Color("PlateBackground")) - Text(number.mainPart()) - .font(Font.custom("RoadNumbers", size: geometry.size.height*0.9)) - } - .frame(width: geometry.size.width*0.73 - 1) - ZStack { - RoundedRectangle(cornerRadius: 4) - .fill(Color("PlateBackground")) - VStack(spacing: 0) { - Text(number.region()) - .frame(height: geometry.size.height*0.65, alignment: .center) - HStack { - - } - .frame(height: geometry.size.height*0.35, alignment: .center) - } - } - .frame(width: geometry.size.width*0.27 - 1) - } - } - .padding(2) - } - .aspectRatio(520.0/112.0, contentMode: .fit) - } -} - -struct PlateNumberView_Previews: PreviewProvider { - static var previews: some View { - Group { - PlateNumberView(number: PlateNumber("Е201АМ761"), unrecognized: false, outdated: false) - PlateNumberView(number: PlateNumber("Е201АМ761"), unrecognized: true, outdated: false) - PlateNumberView(number: PlateNumber("Е201АМ761"), unrecognized: false, outdated: true) - } - .previewLayout(.fixed(width: 200, height: 50)) - } -} diff --git a/Shared/Views/RecordsView.swift b/Shared/Views/RecordsView.swift deleted file mode 100644 index 6b9ade4..0000000 --- a/Shared/Views/RecordsView.swift +++ /dev/null @@ -1,13 +0,0 @@ -import SwiftUI - -struct RecordsView: View { - var body: some View { - Text("Records view") - } -} - -struct RecordsView_Previews: PreviewProvider { - static var previews: some View { - RecordsView() - } -} diff --git a/Shared/Views/ReportView.swift b/Shared/Views/ReportView.swift deleted file mode 100644 index 2808fdc..0000000 --- a/Shared/Views/ReportView.swift +++ /dev/null @@ -1,13 +0,0 @@ -import SwiftUI - -struct ReportView: View { - var body: some View { - Text("Report view") - } -} - -struct ReportView_Previews: PreviewProvider { - static var previews: some View { - ReportView() - } -} diff --git a/Shared/Views/SearchView.swift b/Shared/Views/SearchView.swift deleted file mode 100644 index 023ae7c..0000000 --- a/Shared/Views/SearchView.swift +++ /dev/null @@ -1,13 +0,0 @@ -import SwiftUI - -struct SearchView: View { - var body: some View { - Text("Search view") - } -} - -struct SearchView_Previews: PreviewProvider { - static var previews: some View { - SearchView() - } -} diff --git a/Shared/Views/SettingsView.swift b/Shared/Views/SettingsView.swift deleted file mode 100644 index 680d1a0..0000000 --- a/Shared/Views/SettingsView.swift +++ /dev/null @@ -1,13 +0,0 @@ -import SwiftUI - -struct SettingsView: View { - var body: some View { - Text("Search view") - } -} - -struct SettingsView_Previews: PreviewProvider { - static var previews: some View { - SettingsView() - } -} diff --git a/Shared/Views/macOS/CheckNumberPopup.swift b/Shared/Views/macOS/CheckNumberPopup.swift deleted file mode 100644 index d40d5fc..0000000 --- a/Shared/Views/macOS/CheckNumberPopup.swift +++ /dev/null @@ -1,56 +0,0 @@ -// -// CheckNumberPopup.swift -// AutoCat2 (macOS) -// -// Created by Selim Mustafaev on 07.11.2021. -// - -import SwiftUI - -struct CheckNumberPopup: View { - @Environment(\.presentationMode) var presentation - @State var plateNumber: String = "" - @State var progress = false - @State private var alert: AlertMessage? = nil - - var body: some View { - VStack(alignment: .center, spacing: 8) { - Text("Check new plate number") - TextField("qwe", text: $plateNumber, prompt: Text("asdf")) - .disabled(progress) - Spacer() - if progress { - ProgressView() - .progressViewStyle(CircularProgressViewStyle()) - } - Spacer() - HStack { - Button("Cancel") { - self.presentation.wrappedValue.dismiss() - } - .disabled(progress) - Button("Check") { - Task.init { - progress = true - do { - try await VehicleService.shared.check(plateNumber: plateNumber.uppercased(), force: false) - } catch { - alert = .error(error: error) - } - progress = false - } - } - .alert(item: $alert, content: Alert.init) - .disabled(progress) - } - } - .padding() - .frame(minWidth: 300, minHeight: 180) - } -} - -struct CheckNumberPopup_Previews: PreviewProvider { - static var previews: some View { - CheckNumberPopup() - } -} diff --git a/Shared/Views/macOS/MainViewBig.swift b/Shared/Views/macOS/MainViewBig.swift deleted file mode 100644 index 0e54a00..0000000 --- a/Shared/Views/macOS/MainViewBig.swift +++ /dev/null @@ -1,84 +0,0 @@ -import SwiftUI - -struct MainViewBig: View { - var body: some View { - NavigationView { - SidebarView() - Text("Master") - Text("Detail") - } - } -} - -struct SidebarView: View { - @State var selection: String? - @State private var showAddPlateNumberView = false - - @FetchRequest(entity: CDVehicle.entity(), sortDescriptors: []) var vehicles: FetchedResults - - var body: some View { - List(selection: $selection) { - Section("History") { - NavigationLink(destination: VehiclesListView(vehicles: vehicles)) { - Label("All", systemImage: "car.2") - .badge(vehicles.count) - } - NavigationLink(destination: VehiclesListView(vehicles: vehicles.filter(\.unrecognized))) { - Label("Unreconized", systemImage: "eye.slash") - .badge(vehicles.filter(\.unrecognized).count) - } - NavigationLink(destination: VehiclesListView(vehicles: vehicles.filter(\.outdated))) { - Label("Outdated", systemImage: "wind") - .badge(vehicles.filter(\.outdated).count) - } - } - .collapsible(false) - - Section("Other") { - Label("Recordings", systemImage: "waveform.path") - Label("Search", systemImage: "magnifyingglass") - } - .collapsible(false) - } - .frame(minWidth: 180) - .sheet(isPresented: $showAddPlateNumberView, onDismiss: { - print("Dismiss") - }, content: { - CheckNumberPopup() - }) - .toolbar { - ToolbarItem { - Spacer() - } - ToolbarItem { - Button { - self.showAddPlateNumberView = true - } label: { - Image(systemName: "plus") - } - - } - } - } -} - -struct VehiclesListView: View where VehicleCollection: RandomAccessCollection, VehicleCollection.Element == CDVehicle { - - var vehicles: VehicleCollection - @State var selection: CDVehicle? - - var body: some View { - List(selection: $selection) { - ForEach(vehicles, id: \.self) { vehicle in - let number = PlateNumber(vehicle.number ?? "") - PlateNumberView(number: number, unrecognized: vehicle.unrecognized, outdated: vehicle.outdated) - } - } - } -} - -struct MainViewBig_Previews: PreviewProvider { - static var previews: some View { - MainViewBig() - } -} diff --git a/Tests iOS/Tests_iOS.swift b/Tests iOS/Tests_iOS.swift deleted file mode 100644 index f46c9b9..0000000 --- a/Tests iOS/Tests_iOS.swift +++ /dev/null @@ -1,21 +0,0 @@ -import XCTest - -class Tests_iOS: XCTestCase { - - override func setUpWithError() throws { - continueAfterFailure = false - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testExample() throws { - // UI tests must launch the application that they test. - let app = XCUIApplication() - app.launch() - - // Use recording to get started writing UI tests. - // Use XCTAssert and related functions to verify your tests produce the correct results. - } -}