Latest changes

This commit is contained in:
Selim Mustafaev 2022-03-05 17:20:15 +03:00
parent c80f073f03
commit 1752d89b78
31 changed files with 631 additions and 429 deletions

View File

@ -9,7 +9,6 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
7A2329B926AF122400264CFA /* MainViewSmall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2329B826AF122400264CFA /* MainViewSmall.swift */; }; 7A2329B926AF122400264CFA /* MainViewSmall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2329B826AF122400264CFA /* MainViewSmall.swift */; };
7A2329BA26AF122400264CFA /* MainViewSmall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2329B826AF122400264CFA /* MainViewSmall.swift */; }; 7A2329BA26AF122400264CFA /* MainViewSmall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2329B826AF122400264CFA /* MainViewSmall.swift */; };
7A2329BC26AF123300264CFA /* MainViewBig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2329BB26AF123200264CFA /* MainViewBig.swift */; };
7A2329BD26AF123300264CFA /* MainViewBig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2329BB26AF123200264CFA /* MainViewBig.swift */; }; 7A2329BD26AF123300264CFA /* MainViewBig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2329BB26AF123200264CFA /* MainViewBig.swift */; };
7A2329BF26AF140F00264CFA /* CheckView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2329BE26AF140F00264CFA /* CheckView.swift */; }; 7A2329BF26AF140F00264CFA /* CheckView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2329BE26AF140F00264CFA /* CheckView.swift */; };
7A2329C026AF140F00264CFA /* CheckView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2329BE26AF140F00264CFA /* CheckView.swift */; }; 7A2329C026AF140F00264CFA /* CheckView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2329BE26AF140F00264CFA /* CheckView.swift */; };
@ -57,25 +56,34 @@
7A971F0A26AD74FD007E527B /* ApiMethodMockProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A971F0926AD74FD007E527B /* ApiMethodMockProtocol.swift */; }; 7A971F0A26AD74FD007E527B /* ApiMethodMockProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A971F0926AD74FD007E527B /* ApiMethodMockProtocol.swift */; };
7A971F0D26AD7D4C007E527B /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A971F0C26AD7D4C007E527B /* AnyEncodable.swift */; }; 7A971F0D26AD7D4C007E527B /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A971F0C26AD7D4C007E527B /* AnyEncodable.swift */; };
7A971F0E26AD7D4C007E527B /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A971F0C26AD7D4C007E527B /* AnyEncodable.swift */; }; 7A971F0E26AD7D4C007E527B /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A971F0C26AD7D4C007E527B /* AnyEncodable.swift */; };
7A971F1526AD8AEB007E527B /* Initialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A971F1026AD8AEB007E527B /* Initialization.swift */; };
7A971F1626AD8AEB007E527B /* Initialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A971F1026AD8AEB007E527B /* Initialization.swift */; };
7A971F1726AD8AEB007E527B /* Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A971F1126AD8AEB007E527B /* Querying.swift */; };
7A971F1826AD8AEB007E527B /* Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A971F1126AD8AEB007E527B /* Querying.swift */; };
7A971F1926AD8AEB007E527B /* JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A971F1226AD8AEB007E527B /* JSON.swift */; };
7A971F1A26AD8AEB007E527B /* JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A971F1226AD8AEB007E527B /* JSON.swift */; };
7A971F1D26AD8AEB007E527B /* Merging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A971F1426AD8AEB007E527B /* Merging.swift */; };
7A971F1E26AD8AEB007E527B /* Merging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A971F1426AD8AEB007E527B /* Merging.swift */; };
7A971F2026ADC351007E527B /* ApiError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A971F1F26ADC351007E527B /* ApiError.swift */; }; 7A971F2026ADC351007E527B /* ApiError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A971F1F26ADC351007E527B /* ApiError.swift */; };
7A971F2126ADC351007E527B /* 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 */; }; 7A971F2326ADF74B007E527B /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A971F2226ADF74B007E527B /* MainView.swift */; };
7A971F2426ADF74B007E527B /* 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 */; }; 7ABC05A826B05AF9004BB5A7 /* Vehicle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ABC05A726B05AF9004BB5A7 /* Vehicle.swift */; };
7ABC05A926B05AF9004BB5A7 /* 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 */; }; 7ACD05D72695C08A00557667 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ACD05D62695C08A00557667 /* Constants.swift */; };
7ACD05D82695C08A00557667 /* 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 */; }; 7AEFAEED26985A3400ED2C85 /* ACProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AEFAEEC26985A3400ED2C85 /* ACProgressView.swift */; };
7AEFAEEE26985A3400ED2C85 /* 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 */; }; 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 */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@ -160,17 +168,24 @@
7A971F0726AD7084007E527B /* LoginMethodMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginMethodMock.swift; sourceTree = "<group>"; }; 7A971F0726AD7084007E527B /* LoginMethodMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginMethodMock.swift; sourceTree = "<group>"; };
7A971F0926AD74FD007E527B /* ApiMethodMockProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiMethodMockProtocol.swift; sourceTree = "<group>"; }; 7A971F0926AD74FD007E527B /* ApiMethodMockProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiMethodMockProtocol.swift; sourceTree = "<group>"; };
7A971F0C26AD7D4C007E527B /* AnyEncodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnyEncodable.swift; sourceTree = "<group>"; }; 7A971F0C26AD7D4C007E527B /* AnyEncodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnyEncodable.swift; sourceTree = "<group>"; };
7A971F1026AD8AEB007E527B /* Initialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Initialization.swift; sourceTree = "<group>"; };
7A971F1126AD8AEB007E527B /* Querying.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Querying.swift; sourceTree = "<group>"; };
7A971F1226AD8AEB007E527B /* JSON.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSON.swift; sourceTree = "<group>"; };
7A971F1426AD8AEB007E527B /* Merging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Merging.swift; sourceTree = "<group>"; };
7A971F1F26ADC351007E527B /* ApiError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiError.swift; sourceTree = "<group>"; }; 7A971F1F26ADC351007E527B /* ApiError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiError.swift; sourceTree = "<group>"; };
7A971F2226ADF74B007E527B /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; }; 7A971F2226ADF74B007E527B /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; };
7A971F2526ADFD35007E527B /* AutoCat2 (macOS).entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "AutoCat2 (macOS).entitlements"; sourceTree = "<group>"; }; 7A971F2526ADFD35007E527B /* AutoCat2 (macOS).entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "AutoCat2 (macOS).entitlements"; sourceTree = "<group>"; };
7AAEBD34274A47C500230C28 /* PlateNumberView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlateNumberView.swift; sourceTree = "<group>"; };
7ABC05A726B05AF9004BB5A7 /* Vehicle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Vehicle.swift; sourceTree = "<group>"; }; 7ABC05A726B05AF9004BB5A7 /* Vehicle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Vehicle.swift; sourceTree = "<group>"; };
7ACD05D62695C08A00557667 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; }; 7ACD05D62695C08A00557667 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
7ADE63702735E1FF00A152FE /* VName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VName.swift; sourceTree = "<group>"; };
7ADE63722735E27200A152FE /* VBrand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VBrand.swift; sourceTree = "<group>"; };
7ADE637727368C4600A152FE /* VehicleService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleService.swift; sourceTree = "<group>"; };
7ADE637A273692F300A152FE /* StorageService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageService.swift; sourceTree = "<group>"; };
7ADE637E2738143000A152FE /* CheckNumberPopup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckNumberPopup.swift; sourceTree = "<group>"; };
7AEFAEEC26985A3400ED2C85 /* ACProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ACProgressView.swift; sourceTree = "<group>"; }; 7AEFAEEC26985A3400ED2C85 /* ACProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ACProgressView.swift; sourceTree = "<group>"; };
7AF552D82696E5C100578083 /* ApiTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiTests.swift; sourceTree = "<group>"; }; 7AF552D82696E5C100578083 /* ApiTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiTests.swift; sourceTree = "<group>"; };
7AFADDA6274A49DB00D53EFC /* PlateNumber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlateNumber.swift; sourceTree = "<group>"; };
7AFADDAD274A64DD00D53EFC /* RoadNumbers.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = RoadNumbers.otf; sourceTree = "<group>"; };
7AFADDAE274A64DD00D53EFC /* RoadNumbers2.0.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = RoadNumbers2.0.otf; sourceTree = "<group>"; };
7AFADDB3274A65BB00D53EFC /* AutoCat2--macOS--Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "AutoCat2--macOS--Info.plist"; sourceTree = "<group>"; };
7AFADDB4274A678D00D53EFC /* AutoCat2--iOS--Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "AutoCat2--iOS--Info.plist"; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@ -215,19 +230,22 @@
7A40D5732691C6D6009B0BC4 = { 7A40D5732691C6D6009B0BC4 = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
7AFADDB4274A678D00D53EFC /* AutoCat2--iOS--Info.plist */,
7AFADDB3274A65BB00D53EFC /* AutoCat2--macOS--Info.plist */,
7A971F2526ADFD35007E527B /* AutoCat2 (macOS).entitlements */, 7A971F2526ADFD35007E527B /* AutoCat2 (macOS).entitlements */,
7A40D5782691C6D7009B0BC4 /* Shared */, 7A40D5782691C6D7009B0BC4 /* Shared */,
7A40D5912691C6D8009B0BC4 /* Tests iOS */, 7A40D5912691C6D8009B0BC4 /* Tests iOS */,
7A40D59B2691C6D8009B0BC4 /* Tests macOS */, 7A40D59B2691C6D8009B0BC4 /* Tests macOS */,
7A40D5F42693A63A009B0BC4 /* AutoCat2Tests */, 7A40D5F42693A63A009B0BC4 /* AutoCat2Tests */,
7A40D5842691C6D8009B0BC4 /* Products */, 7A40D5842691C6D8009B0BC4 /* Products */,
7A40D5D92691C7EB009B0BC4 /* Frameworks */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
}; };
7A40D5782691C6D7009B0BC4 /* Shared */ = { 7A40D5782691C6D7009B0BC4 /* Shared */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
7AFADDAC274A64DD00D53EFC /* Fonts */,
7ADE637627368C2A00A152FE /* Services */,
7A971F0B26AD7D27007E527B /* ThirdParty */, 7A971F0B26AD7D27007E527B /* ThirdParty */,
7A40D6002694FF4C009B0BC4 /* Utils */, 7A40D6002694FF4C009B0BC4 /* Utils */,
7A40D5FC2693A90F009B0BC4 /* Extensions */, 7A40D5FC2693A90F009B0BC4 /* Extensions */,
@ -271,13 +289,6 @@
path = "Tests macOS"; path = "Tests macOS";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
7A40D5D92691C7EB009B0BC4 /* Frameworks */ = {
isa = PBXGroup;
children = (
);
name = Frameworks;
sourceTree = "<group>";
};
7A40D5DF26924ADE009B0BC4 /* Models */ = { 7A40D5DF26924ADE009B0BC4 /* Models */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -285,6 +296,9 @@
7A40D5E226924B09009B0BC4 /* Settings.swift */, 7A40D5E226924B09009B0BC4 /* Settings.swift */,
7A683998269612EA00B2188A /* Response.swift */, 7A683998269612EA00B2188A /* Response.swift */,
7ABC05A726B05AF9004BB5A7 /* Vehicle.swift */, 7ABC05A726B05AF9004BB5A7 /* Vehicle.swift */,
7ADE63702735E1FF00A152FE /* VName.swift */,
7ADE63722735E27200A152FE /* VBrand.swift */,
7AFADDA6274A49DB00D53EFC /* PlateNumber.swift */,
); );
path = Models; path = Models;
sourceTree = "<group>"; sourceTree = "<group>";
@ -299,17 +313,18 @@
7A40D5E726938BC8009B0BC4 /* Views */ = { 7A40D5E726938BC8009B0BC4 /* Views */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
7ADE637D2738139D00A152FE /* macOS */,
7AEFAEEC26985A3400ED2C85 /* ACProgressView.swift */, 7AEFAEEC26985A3400ED2C85 /* ACProgressView.swift */,
7A2329BE26AF140F00264CFA /* CheckView.swift */,
7A40D57C2691C6D7009B0BC4 /* ContentView.swift */, 7A40D57C2691C6D7009B0BC4 /* ContentView.swift */,
7A40D5E826938BEC009B0BC4 /* AuthView.swift */, 7A40D5E826938BEC009B0BC4 /* AuthView.swift */,
7A971F2226ADF74B007E527B /* MainView.swift */, 7A971F2226ADF74B007E527B /* MainView.swift */,
7A2329B826AF122400264CFA /* MainViewSmall.swift */, 7A2329B826AF122400264CFA /* MainViewSmall.swift */,
7A2329BB26AF123200264CFA /* MainViewBig.swift */,
7A2329BE26AF140F00264CFA /* CheckView.swift */,
7A2329C126AF141E00264CFA /* SearchView.swift */, 7A2329C126AF141E00264CFA /* SearchView.swift */,
7A2329C426AF142900264CFA /* SettingsView.swift */, 7A2329C426AF142900264CFA /* SettingsView.swift */,
7A2329C726AF143F00264CFA /* RecordsView.swift */, 7A2329C726AF143F00264CFA /* RecordsView.swift */,
7A2329CA26AF15AC00264CFA /* ReportView.swift */, 7A2329CA26AF15AC00264CFA /* ReportView.swift */,
7AAEBD34274A47C500230C28 /* PlateNumberView.swift */,
); );
path = Views; path = Views;
sourceTree = "<group>"; sourceTree = "<group>";
@ -390,21 +405,36 @@
7A971F0B26AD7D27007E527B /* ThirdParty */ = { 7A971F0B26AD7D27007E527B /* ThirdParty */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
7A971F0F26AD8AEB007E527B /* GenericJSON */,
7A971F0C26AD7D4C007E527B /* AnyEncodable.swift */, 7A971F0C26AD7D4C007E527B /* AnyEncodable.swift */,
); );
path = ThirdParty; path = ThirdParty;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
7A971F0F26AD8AEB007E527B /* GenericJSON */ = { 7ADE637627368C2A00A152FE /* Services */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
7A971F1026AD8AEB007E527B /* Initialization.swift */, 7ADE637727368C4600A152FE /* VehicleService.swift */,
7A971F1126AD8AEB007E527B /* Querying.swift */, 7ADE637A273692F300A152FE /* StorageService.swift */,
7A971F1226AD8AEB007E527B /* JSON.swift */,
7A971F1426AD8AEB007E527B /* Merging.swift */,
); );
path = GenericJSON; path = Services;
sourceTree = "<group>";
};
7ADE637D2738139D00A152FE /* macOS */ = {
isa = PBXGroup;
children = (
7A2329BB26AF123200264CFA /* MainViewBig.swift */,
7ADE637E2738143000A152FE /* CheckNumberPopup.swift */,
);
path = macOS;
sourceTree = "<group>";
};
7AFADDAC274A64DD00D53EFC /* Fonts */ = {
isa = PBXGroup;
children = (
7AFADDAD274A64DD00D53EFC /* RoadNumbers.otf */,
7AFADDAE274A64DD00D53EFC /* RoadNumbers2.0.otf */,
);
path = Fonts;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
/* End PBXGroup section */ /* End PBXGroup section */
@ -424,6 +454,8 @@
dependencies = ( dependencies = (
); );
name = "AutoCat2 (iOS)"; name = "AutoCat2 (iOS)";
packageProductDependencies = (
);
productName = "AutoCat2 (iOS)"; productName = "AutoCat2 (iOS)";
productReference = 7A40D5832691C6D8009B0BC4 /* AutoCat2.app */; productReference = 7A40D5832691C6D8009B0BC4 /* AutoCat2.app */;
productType = "com.apple.product-type.application"; productType = "com.apple.product-type.application";
@ -442,6 +474,8 @@
dependencies = ( dependencies = (
); );
name = "AutoCat2 (macOS)"; name = "AutoCat2 (macOS)";
packageProductDependencies = (
);
productName = "AutoCat2 (macOS)"; productName = "AutoCat2 (macOS)";
productReference = 7A40D5892691C6D8009B0BC4 /* AutoCat2.app */; productReference = 7A40D5892691C6D8009B0BC4 /* AutoCat2.app */;
productType = "com.apple.product-type.application"; productType = "com.apple.product-type.application";
@ -508,7 +542,7 @@
attributes = { attributes = {
BuildIndependentTargetsInParallel = 1; BuildIndependentTargetsInParallel = 1;
LastSwiftUpdateCheck = 1300; LastSwiftUpdateCheck = 1300;
LastUpgradeCheck = 1300; LastUpgradeCheck = 1310;
TargetAttributes = { TargetAttributes = {
7A40D5822691C6D8009B0BC4 = { 7A40D5822691C6D8009B0BC4 = {
CreatedOnToolsVersion = 13.0; CreatedOnToolsVersion = 13.0;
@ -539,6 +573,8 @@
Base, Base,
); );
mainGroup = 7A40D5732691C6D6009B0BC4; mainGroup = 7A40D5732691C6D6009B0BC4;
packageReferences = (
);
productRefGroup = 7A40D5842691C6D8009B0BC4 /* Products */; productRefGroup = 7A40D5842691C6D8009B0BC4 /* Products */;
projectDirPath = ""; projectDirPath = "";
projectRoot = ""; projectRoot = "";
@ -557,7 +593,9 @@
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
7AFADDAF274A64DD00D53EFC /* RoadNumbers.otf in Resources */,
7A40D5A62691C6D8009B0BC4 /* Assets.xcassets in Resources */, 7A40D5A62691C6D8009B0BC4 /* Assets.xcassets in Resources */,
7AFADDB1274A64DD00D53EFC /* RoadNumbers2.0.otf in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -565,7 +603,9 @@
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
7AFADDB0274A64DD00D53EFC /* RoadNumbers.otf in Resources */,
7A40D5A72691C6D8009B0BC4 /* Assets.xcassets in Resources */, 7A40D5A72691C6D8009B0BC4 /* Assets.xcassets in Resources */,
7AFADDB2274A64DD00D53EFC /* RoadNumbers2.0.otf in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -600,25 +640,26 @@
files = ( files = (
7A40D5E926938BEC009B0BC4 /* AuthView.swift in Sources */, 7A40D5E926938BEC009B0BC4 /* AuthView.swift in Sources */,
7A971F2326ADF74B007E527B /* MainView.swift in Sources */, 7A971F2326ADF74B007E527B /* MainView.swift in Sources */,
7ADE63732735E27200A152FE /* VBrand.swift in Sources */,
7A971F0D26AD7D4C007E527B /* AnyEncodable.swift in Sources */, 7A971F0D26AD7D4C007E527B /* AnyEncodable.swift in Sources */,
7ACD05D72695C08A00557667 /* Constants.swift in Sources */, 7ACD05D72695C08A00557667 /* Constants.swift in Sources */,
7A971F1926AD8AEB007E527B /* JSON.swift in Sources */,
7A2329BC26AF123300264CFA /* MainViewBig.swift in Sources */,
7A971F1D26AD8AEB007E527B /* Merging.swift in Sources */,
7A40D5E326924B09009B0BC4 /* Settings.swift in Sources */, 7A40D5E326924B09009B0BC4 /* Settings.swift in Sources */,
7ADE637827368C4600A152FE /* VehicleService.swift in Sources */,
7AEFAEED26985A3400ED2C85 /* ACProgressView.swift in Sources */, 7AEFAEED26985A3400ED2C85 /* ACProgressView.swift in Sources */,
7A40D5A02691C6D8009B0BC4 /* AutoCat2App.swift in Sources */, 7A40D5A02691C6D8009B0BC4 /* AutoCat2App.swift in Sources */,
7A683999269612EA00B2188A /* Response.swift in Sources */, 7A683999269612EA00B2188A /* Response.swift in Sources */,
7A2329BF26AF140F00264CFA /* CheckView.swift in Sources */, 7A2329BF26AF140F00264CFA /* CheckView.swift in Sources */,
7A971F1526AD8AEB007E527B /* Initialization.swift in Sources */,
7A971F1726AD8AEB007E527B /* Querying.swift in Sources */,
7A40D60826998DCF009B0BC4 /* Alert.swift in Sources */, 7A40D60826998DCF009B0BC4 /* Alert.swift in Sources */,
7A2329C226AF141E00264CFA /* SearchView.swift in Sources */, 7A2329C226AF141E00264CFA /* SearchView.swift in Sources */,
7ADE637B273692F300A152FE /* StorageService.swift in Sources */,
7ADE63712735E1FF00A152FE /* VName.swift in Sources */,
7A40D5A42691C6D8009B0BC4 /* Persistence.swift in Sources */, 7A40D5A42691C6D8009B0BC4 /* Persistence.swift in Sources */,
7A971F2026ADC351007E527B /* ApiError.swift in Sources */, 7A971F2026ADC351007E527B /* ApiError.swift in Sources */,
7A2329CB26AF15AC00264CFA /* ReportView.swift in Sources */, 7A2329CB26AF15AC00264CFA /* ReportView.swift in Sources */,
7AAEBD35274A47C500230C28 /* PlateNumberView.swift in Sources */,
7A40D5ED2693A1EA009B0BC4 /* AuthVM.swift in Sources */, 7A40D5ED2693A1EA009B0BC4 /* AuthVM.swift in Sources */,
7A40D59E2691C6D8009B0BC4 /* AutoCat2.xcdatamodeld in Sources */, 7A40D59E2691C6D8009B0BC4 /* AutoCat2.xcdatamodeld in Sources */,
7AFADDA7274A49DB00D53EFC /* PlateNumber.swift in Sources */,
7ABC05A826B05AF9004BB5A7 /* Vehicle.swift in Sources */, 7ABC05A826B05AF9004BB5A7 /* Vehicle.swift in Sources */,
7A2329B926AF122400264CFA /* MainViewSmall.swift in Sources */, 7A2329B926AF122400264CFA /* MainViewSmall.swift in Sources */,
7A2329C826AF143F00264CFA /* RecordsView.swift in Sources */, 7A2329C826AF143F00264CFA /* RecordsView.swift in Sources */,
@ -634,31 +675,34 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
7AAEBD36274A47C600230C28 /* PlateNumberView.swift in Sources */,
7ADE637927368C4600A152FE /* VehicleService.swift in Sources */,
7A40D5EA26938BEC009B0BC4 /* AuthView.swift in Sources */, 7A40D5EA26938BEC009B0BC4 /* AuthView.swift in Sources */,
7A971F2426ADF74B007E527B /* MainView.swift in Sources */, 7A971F2426ADF74B007E527B /* MainView.swift in Sources */,
7ADE63742735E27200A152FE /* VBrand.swift in Sources */,
7A971F0E26AD7D4C007E527B /* AnyEncodable.swift in Sources */, 7A971F0E26AD7D4C007E527B /* AnyEncodable.swift in Sources */,
7ACD05D82695C08A00557667 /* Constants.swift in Sources */, 7ACD05D82695C08A00557667 /* Constants.swift in Sources */,
7A971F1A26AD8AEB007E527B /* JSON.swift in Sources */,
7A2329BD26AF123300264CFA /* MainViewBig.swift in Sources */, 7A2329BD26AF123300264CFA /* MainViewBig.swift in Sources */,
7A971F1E26AD8AEB007E527B /* Merging.swift in Sources */,
7A40D5A12691C6D8009B0BC4 /* AutoCat2App.swift in Sources */, 7A40D5A12691C6D8009B0BC4 /* AutoCat2App.swift in Sources */,
7AEFAEEE26985A3400ED2C85 /* ACProgressView.swift in Sources */, 7AEFAEEE26985A3400ED2C85 /* ACProgressView.swift in Sources */,
7A40D5A52691C6D8009B0BC4 /* Persistence.swift in Sources */, 7A40D5A52691C6D8009B0BC4 /* Persistence.swift in Sources */,
7AFADDA8274A49DB00D53EFC /* PlateNumber.swift in Sources */,
7A68399A269612EA00B2188A /* Response.swift in Sources */, 7A68399A269612EA00B2188A /* Response.swift in Sources */,
7A2329C026AF140F00264CFA /* CheckView.swift in Sources */, 7A2329C026AF140F00264CFA /* CheckView.swift in Sources */,
7A971F1626AD8AEB007E527B /* Initialization.swift in Sources */,
7A971F1826AD8AEB007E527B /* Querying.swift in Sources */,
7A40D60926998DCF009B0BC4 /* Alert.swift in Sources */, 7A40D60926998DCF009B0BC4 /* Alert.swift in Sources */,
7A2329C326AF141E00264CFA /* SearchView.swift in Sources */, 7A2329C326AF141E00264CFA /* SearchView.swift in Sources */,
7A40D5E526924B0C009B0BC4 /* User.swift in Sources */, 7A40D5E526924B0C009B0BC4 /* User.swift in Sources */,
7A971F2126ADC351007E527B /* ApiError.swift in Sources */, 7A971F2126ADC351007E527B /* ApiError.swift in Sources */,
7A2329CC26AF15AC00264CFA /* ReportView.swift in Sources */, 7A2329CC26AF15AC00264CFA /* ReportView.swift in Sources */,
7ADE637C273692F300A152FE /* StorageService.swift in Sources */,
7ADE637F2738143000A152FE /* CheckNumberPopup.swift in Sources */,
7A40D5EE2693A1EA009B0BC4 /* AuthVM.swift in Sources */, 7A40D5EE2693A1EA009B0BC4 /* AuthVM.swift in Sources */,
7A40D59F2691C6D8009B0BC4 /* AutoCat2.xcdatamodeld in Sources */, 7A40D59F2691C6D8009B0BC4 /* AutoCat2.xcdatamodeld in Sources */,
7ABC05A926B05AF9004BB5A7 /* Vehicle.swift in Sources */, 7ABC05A926B05AF9004BB5A7 /* Vehicle.swift in Sources */,
7A2329BA26AF122400264CFA /* MainViewSmall.swift in Sources */, 7A2329BA26AF122400264CFA /* MainViewSmall.swift in Sources */,
7A2329C926AF143F00264CFA /* RecordsView.swift in Sources */, 7A2329C926AF143F00264CFA /* RecordsView.swift in Sources */,
7A40D5A32691C6D8009B0BC4 /* ContentView.swift in Sources */, 7A40D5A32691C6D8009B0BC4 /* ContentView.swift in Sources */,
7ADE63752735E27D00A152FE /* VName.swift in Sources */,
7A40D5FF2693A91F009B0BC4 /* CocoaError.swift in Sources */, 7A40D5FF2693A91F009B0BC4 /* CocoaError.swift in Sources */,
7A40D6032694FF5D009B0BC4 /* Api.swift in Sources */, 7A40D6032694FF5D009B0BC4 /* Api.swift in Sources */,
7A40D5E426924B09009B0BC4 /* Settings.swift in Sources */, 7A40D5E426924B09009B0BC4 /* Settings.swift in Sources */,
@ -841,6 +885,7 @@
DEVELOPMENT_TEAM = 46DTTB8X4S; DEVELOPMENT_TEAM = 46DTTB8X4S;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "AutoCat2--iOS--Info.plist";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES;
@ -871,6 +916,7 @@
DEVELOPMENT_TEAM = 46DTTB8X4S; DEVELOPMENT_TEAM = 46DTTB8X4S;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "AutoCat2--iOS--Info.plist";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES;
@ -898,6 +944,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = "AutoCat2 (macOS).entitlements"; CODE_SIGN_ENTITLEMENTS = "AutoCat2 (macOS).entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
@ -907,6 +954,7 @@
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
ENABLE_USER_SELECTED_FILES = readonly; ENABLE_USER_SELECTED_FILES = readonly;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "AutoCat2--macOS--Info.plist";
INFOPLIST_KEY_NSHumanReadableCopyright = ""; INFOPLIST_KEY_NSHumanReadableCopyright = "";
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
@ -928,6 +976,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = "AutoCat2 (macOS).entitlements"; CODE_SIGN_ENTITLEMENTS = "AutoCat2 (macOS).entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
@ -937,6 +986,7 @@
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
ENABLE_USER_SELECTED_FILES = readonly; ENABLE_USER_SELECTED_FILES = readonly;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "AutoCat2--macOS--Info.plist";
INFOPLIST_KEY_NSHumanReadableCopyright = ""; INFOPLIST_KEY_NSHumanReadableCopyright = "";
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",

View File

@ -20,5 +20,23 @@
<integer>2</integer> <integer>2</integer>
</dict> </dict>
</dict> </dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>7A40D5822691C6D8009B0BC4</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>7A40D58D2691C6D8009B0BC4</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>7A40D5F22693A63A009B0BC4</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict> </dict>
</plist> </plist>

View File

@ -3,7 +3,7 @@ import AutoCat2
class ApiTests: XCTestCase { class ApiTests: XCTestCase {
private var api: Api! private var api: ApiProtocol!
private let testLogin = "test@gmail.com" private let testLogin = "test@gmail.com"
private let testPassword = "12345" private let testPassword = "12345"

View File

@ -3,7 +3,7 @@ import AutoCat2
class SettingsTests: XCTestCase { class SettingsTests: XCTestCase {
private var settings: Settings! private var settings: SettingsProtocol!
override func setUpWithError() throws { override func setUpWithError() throws {
guard let userDefaults = UserDefaults(suiteName: #file) else { guard let userDefaults = UserDefaults(suiteName: #file) else {

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "1.000",
"green" : "1.000",
"red" : "1.000"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.152",
"green" : "0.152",
"red" : "0.152"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,38 @@
{
"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
}
}

View File

@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.000",
"green" : "0.000",
"red" : "0.000"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.866",
"green" : "0.866",
"red" : "0.866"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19186" systemVersion="21A5294g" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="YES" userDefinedModelVersionIdentifier=""> <model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19461" systemVersion="21A559" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="YES" userDefinedModelVersionIdentifier="">
<entity name="VBrand" representedClassName="VBrand" syncable="YES" codeGenerationType="class"> <entity name="VBrand" representedClassName="CDVBrand" syncable="YES" codeGenerationType="class">
<attribute name="logo" optional="YES" attributeType="String"/> <attribute name="logo" optional="YES" attributeType="String"/>
<relationship name="name" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="VName" inverseName="brand" inverseEntity="VName"/> <relationship name="name" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="VName" inverseName="brand" inverseEntity="VName"/>
<relationship name="vehicle" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Vehicle" inverseName="brand" inverseEntity="Vehicle"/> <relationship name="vehicle" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Vehicle" inverseName="brand" inverseEntity="Vehicle"/>
</entity> </entity>
<entity name="Vehicle" representedClassName="Vehicle" syncable="YES" codeGenerationType="class"> <entity name="Vehicle" representedClassName="CDVehicle" syncable="YES" codeGenerationType="class">
<attribute name="currentNumber" optional="YES" attributeType="String"/> <attribute name="currentNumber" optional="YES" attributeType="String"/>
<attribute name="number" optional="YES" attributeType="String"/> <attribute name="number" optional="YES" attributeType="String"/>
<relationship name="brand" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="VBrand" inverseName="vehicle" inverseEntity="VBrand"/> <relationship name="brand" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="VBrand" inverseName="vehicle" inverseEntity="VBrand"/>
</entity> </entity>
<entity name="VName" representedClassName="VName" syncable="YES" codeGenerationType="class"> <entity name="VName" representedClassName="CDVName" syncable="YES" codeGenerationType="class">
<attribute name="normalized" optional="YES" attributeType="String"/> <attribute name="normalized" optional="YES" attributeType="String"/>
<attribute name="original" optional="YES" attributeType="String"/> <attribute name="original" optional="YES" attributeType="String"/>
<relationship name="brand" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="VBrand" inverseName="name" inverseEntity="VBrand"/> <relationship name="brand" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="VBrand" inverseName="name" inverseEntity="VBrand"/>
</entity> </entity>
<elements> <elements>
<element name="Vehicle" positionX="-145.9427490234375" positionY="-363.4805297851562" width="128" height="88"/> <element name="VBrand" positionX="118.7528686523438" positionY="-209.1094360351562" width="128" height="74"/>
<element name="VName" positionX="-54" positionY="9" width="128" height="88"/> <element name="Vehicle" positionX="-145.9427490234375" positionY="-363.4805297851562" width="128" height="74"/>
<element name="VBrand" positionX="118.7528686523438" positionY="-209.1094360351562" width="128" height="88"/> <element name="VName" positionX="-54" positionY="9" width="128" height="74"/>
</elements> </elements>
</model> </model>

View File

@ -9,12 +9,12 @@ import SwiftUI
@main @main
struct AutoCat2App: App { struct AutoCat2App: App {
let persistenceController = PersistenceController.shared let storageService = StorageService.shared
var body: some Scene { var body: some Scene {
WindowGroup { WindowGroup {
ContentView() ContentView()
.environment(\.managedObjectContext, persistenceController.container.viewContext) .environment(\.managedObjectContext, storageService.context)
} }
} }
} }

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,25 @@
import Foundation
public class PlateNumber {
private var number: String
private var numberEnglish: String
public init(_ string: String) {
self.number = string
self.numberEnglish = String(self.number.map { Constants.pnLettersMap[$0] ?? $0 })
}
public func asString() -> String {
return self.number
}
public func mainPart() -> String {
let index = self.numberEnglish.index(self.numberEnglish.startIndex, offsetBy: 6)
return String(self.numberEnglish[..<index])
}
public func region() -> String {
let index = self.numberEnglish.index(self.numberEnglish.startIndex, offsetBy: 6)
return String(self.numberEnglish[index...])
}
}

View File

@ -1,7 +1,18 @@
import Foundation import Foundation
public class Settings: ObservableObject { public protocol SettingsProtocol {
private var defaults: UserDefaults
var user: User { get set }
var recognizeAlternativeOrder: Bool { get set }
var recognizeShortenedNumbers: Bool { get set }
var defaultRegion: String { get set }
var recordBeep: Bool { get set }
var showDebugInfo: Bool { get set }
}
public class Settings: ObservableObject, SettingsProtocol {
private let defaults: UserDefaults
public static let shared = Settings() public static let shared = Settings()
@Published @Published

View File

@ -0,0 +1,22 @@
import Foundation
import CoreData
public struct VBrand: Decodable {
let logo: String?
let name: VName?
}
extension CDVBrand {
convenience init(vbrand: VBrand, context: NSManagedObjectContext) {
self.init(context: context)
self.logo = vbrand.logo
if let vname = vbrand.name {
self.name = CDVName(vname: vname, context: context)
}
}
}

19
Shared/Models/VName.swift Normal file
View File

@ -0,0 +1,19 @@
import Foundation
import CoreData
public struct VName: Decodable {
let normalized: String?
let original: String?
}
extension CDVName {
convenience init(vname: VName, context: NSManagedObjectContext) {
self.init(context: context)
self.normalized = vname.normalized
self.original = vname.original
}
}

View File

@ -1,7 +1,13 @@
import Foundation import Foundation
import CoreData import CoreData
extension Vehicle { public struct Vehicle: Decodable {
let number: String
let currentNumber: String?
let brand: VBrand?
// TODO: Remove code duplication
public var unrecognized: Bool { public var unrecognized: Bool {
return self.brand == nil return self.brand == nil
} }
@ -14,3 +20,30 @@ extension Vehicle {
} }
} }
} }
extension CDVehicle {
convenience init(vehicle: Vehicle, context: NSManagedObjectContext) {
self.init(context: context)
self.number = vehicle.number
self.currentNumber = vehicle.currentNumber
if let vbrand = vehicle.brand {
self.brand = CDVBrand(vbrand: vbrand, context: context)
}
}
public var unrecognized: Bool {
return self.brand == nil
}
public var outdated: Bool {
if let current = self.currentNumber {
return current != self.number
} else {
return false
}
}
}

View File

@ -0,0 +1,56 @@
import Foundation
import CoreData
protocol StorageServiceProtocol {
var context: NSManagedObjectContext { get }
func store(vehicle: Vehicle) throws -> CDVehicle
}
class StorageService: StorageServiceProtocol {
private let container: NSPersistentCloudKitContainer
static let shared = StorageService()
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? {
// TODO: Handle error properly
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
}
private func save() throws {
guard context.hasChanges else {
return
}
do {
try context.save()
} catch {
context.rollback()
throw error
}
}
// MARK: - StorageServiceProtocol
var context: NSManagedObjectContext {
container.viewContext
}
func store(vehicle: Vehicle) throws -> CDVehicle {
let cdVehicle = CDVehicle(vehicle: vehicle, context: context)
try save()
return cdVehicle
}
}

View File

@ -0,0 +1,29 @@
import Foundation
protocol VehicleServiceProtocol {
func check(plateNumber: String, force: Bool) async throws -> CDVehicle
}
class VehicleService: VehicleServiceProtocol {
private let api: ApiProtocol
private let storage: StorageServiceProtocol
static let shared = VehicleService()
init(api: ApiProtocol = Api.shared, storage: StorageServiceProtocol = StorageService.shared) {
self.api = api
self.storage = storage
}
// MARK: - VehicleServiceProtocol
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)
}
}

View File

@ -1,124 +0,0 @@
import Foundation
private struct InitializationError: Error {}
extension JSON {
/// Create a JSON value from anything.
///
/// Argument has to be a valid JSON structure: A `Double`, `Int`, `String`,
/// `Bool`, an `Array` of those types or a `Dictionary` of those types.
///
/// You can also pass `nil` or `NSNull`, both will be treated as `.null`.
public init(_ value: Any) throws {
switch value {
case _ as NSNull:
self = .null
case let opt as Optional<Any> where opt == nil:
self = .null
case let num as NSNumber:
if num.isBool {
self = .bool(num.boolValue)
} else {
self = .number(num.doubleValue)
}
case let str as String:
self = .string(str)
case let bool as Bool:
self = .bool(bool)
case let array as [Any]:
self = .array(try array.map(JSON.init))
case let dict as [String:Any]:
self = .object(try dict.mapValues(JSON.init))
default:
throw InitializationError()
}
}
}
extension JSON {
/// Create a JSON value from an `Encodable`. This will give you access to the raw
/// encoded JSON value the `Encodable` is serialized into.
public init<T: Encodable>(encodable: T) throws {
let encoded = try JSONEncoder().encode(encodable)
self = try JSONDecoder().decode(JSON.self, from: encoded)
}
}
extension JSON: ExpressibleByBooleanLiteral {
public init(booleanLiteral value: Bool) {
self = .bool(value)
}
}
extension JSON: ExpressibleByNilLiteral {
public init(nilLiteral: ()) {
self = .null
}
}
extension JSON: ExpressibleByArrayLiteral {
public init(arrayLiteral elements: JSON...) {
self = .array(elements)
}
}
extension JSON: ExpressibleByDictionaryLiteral {
public init(dictionaryLiteral elements: (String, JSON)...) {
var object: [String:JSON] = [:]
for (k, v) in elements {
object[k] = v
}
self = .object(object)
}
}
extension JSON: ExpressibleByFloatLiteral {
public init(floatLiteral value: Double) {
self = .number(value)
}
}
extension JSON: ExpressibleByIntegerLiteral {
public init(integerLiteral value: Int) {
self = .number(Double(value))
}
}
extension JSON: ExpressibleByStringLiteral {
public init(stringLiteral value: String) {
self = .string(value)
}
}
// MARK: - NSNumber
extension NSNumber {
/// Boolean value indicating whether this `NSNumber` wraps a boolean.
///
/// For example, when using `NSJSONSerialization` Bool values are converted into `NSNumber` instances.
///
/// - seealso: https://stackoverflow.com/a/49641315/3589408
fileprivate var isBool: Bool {
let objCType = String(cString: self.objCType)
if (self.compare(trueNumber) == .orderedSame && objCType == trueObjCType) || (self.compare(falseNumber) == .orderedSame && objCType == falseObjCType) {
return true
} else {
return false
}
}
}
private let trueNumber = NSNumber(value: true)
private let falseNumber = NSNumber(value: false)
private let trueObjCType = String(cString: trueNumber.objCType)
private let falseObjCType = String(cString: falseNumber.objCType)

View File

@ -1,82 +0,0 @@
import Foundation
/// A JSON value representation. This is a bit more useful than the naïve `[String:Any]` type
/// for JSON values, since it makes sure only valid JSON values are present & supports `Equatable`
/// and `Codable`, so that you can compare values for equality and code and decode them into data
/// or strings.
@dynamicMemberLookup public enum JSON: Equatable {
case string(String)
case number(Double)
case object([String:JSON])
case array([JSON])
case bool(Bool)
case null
}
extension JSON: Codable {
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case let .array(array):
try container.encode(array)
case let .object(object):
try container.encode(object)
case let .string(string):
try container.encode(string)
case let .number(number):
try container.encode(number)
case let .bool(bool):
try container.encode(bool)
case .null:
try container.encodeNil()
}
}
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let object = try? container.decode([String: JSON].self) {
self = .object(object)
} else if let array = try? container.decode([JSON].self) {
self = .array(array)
} else if let string = try? container.decode(String.self) {
self = .string(string)
} else if let bool = try? container.decode(Bool.self) {
self = .bool(bool)
} else if let number = try? container.decode(Double.self) {
self = .number(number)
} else if container.decodeNil() {
self = .null
} else {
throw DecodingError.dataCorrupted(
.init(codingPath: decoder.codingPath, debugDescription: "Invalid JSON value.")
)
}
}
}
extension JSON: CustomDebugStringConvertible {
public var debugDescription: String {
switch self {
case .string(let str):
return str.debugDescription
case .number(let num):
return num.debugDescription
case .bool(let bool):
return bool.description
case .null:
return "null"
default:
let encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted]
return try! String(data: encoder.encode(self), encoding: .utf8)!
}
}
}
extension JSON: Hashable {}

View File

@ -1,41 +0,0 @@
import Foundation
extension JSON {
/// Return a new JSON value by merging two other ones
///
/// If we call the current JSON value `old` and the incoming JSON value
/// `new`, the precise merging rules are:
///
/// 1. If `old` or `new` are anything but an object, return `new`.
/// 2. If both `old` and `new` are objects, create a merged object like this:
/// 1. Add keys from `old` not present in `new` (no change case).
/// 2. Add keys from `new` not present in `old` (create case).
/// 3. For keys present in both `old` and `new`, apply merge recursively to their values (update case).
public func merging(with new: JSON) -> JSON {
// If old or new are anything but an object, return new.
guard case .object(let lhs) = self, case .object(let rhs) = new else {
return new
}
var merged: [String: JSON] = [:]
// Add keys from old not present in new (no change case).
for (key, val) in lhs where rhs[key] == nil {
merged[key] = val
}
// Add keys from new not present in old (create case).
for (key, val) in rhs where lhs[key] == nil {
merged[key] = val
}
// For keys present in both old and new, apply merge recursively to their values.
for key in lhs.keys where rhs[key] != nil {
merged[key] = lhs[key]?.merging(with: rhs[key]!)
}
return JSON.object(merged)
}
}

View File

@ -1,107 +0,0 @@
import Foundation
public extension JSON {
/// Return the string value if this is a `.string`, otherwise `nil`
var stringValue: String? {
if case .string(let value) = self {
return value
}
return nil
}
/// Return the double value if this is a `.number`, otherwise `nil`
var doubleValue: Double? {
if case .number(let value) = self {
return value
}
return nil
}
/// Return the bool value if this is a `.bool`, otherwise `nil`
var boolValue: Bool? {
if case .bool(let value) = self {
return value
}
return nil
}
/// Return the object value if this is an `.object`, otherwise `nil`
var objectValue: [String: JSON]? {
if case .object(let value) = self {
return value
}
return nil
}
/// Return the array value if this is an `.array`, otherwise `nil`
var arrayValue: [JSON]? {
if case .array(let value) = self {
return value
}
return nil
}
/// Return `true` iff this is `.null`
var isNull: Bool {
if case .null = self {
return true
}
return false
}
/// If this is an `.array`, return item at index
///
/// If this is not an `.array` or the index is out of bounds, returns `nil`.
subscript(index: Int) -> JSON? {
if case .array(let arr) = self, arr.indices.contains(index) {
return arr[index]
}
return nil
}
/// If this is an `.object`, return item at key
subscript(key: String) -> JSON? {
if case .object(let dict) = self {
return dict[key]
}
return nil
}
/// Dynamic member lookup sugar for string subscripts
///
/// This lets you write `json.foo` instead of `json["foo"]`.
subscript(dynamicMember member: String) -> JSON? {
return self[member]
}
/// Return the JSON type at the keypath if this is an `.object`, otherwise `nil`
///
/// This lets you write `json[keyPath: "foo.bar.jar"]`.
subscript(keyPath keyPath: String) -> JSON? {
return queryKeyPath(keyPath.components(separatedBy: "."))
}
func queryKeyPath<T>(_ path: T) -> JSON? where T: Collection, T.Element == String {
// Only object values may be subscripted
guard case .object(let object) = self else {
return nil
}
// Is the path non-empty?
guard let head = path.first else {
return nil
}
// Do we have a value at the required key?
guard let value = object[head] else {
return nil
}
let tail = path.dropFirst()
return tail.isEmpty ? value : value.queryKeyPath(tail)
}
}

View File

@ -1,12 +1,23 @@
import Foundation import Foundation
public class Api { public protocol ApiProtocol {
func login(email: String, password: String) async throws -> User
func check(plateNumber: String, force: Bool) async throws -> Vehicle
}
private var session: URLSession public class Api: ApiProtocol {
private let session: URLSession
private let settings: SettingsProtocol
public static let shared = Api() public static let shared = Api()
public init(session: URLSession? = nil) { public init(session: URLSession? = nil, settings: SettingsProtocol = Settings.shared) {
self.settings = settings
if let session = session { if let session = session {
self.session = session self.session = session
} else { } else {
@ -34,7 +45,7 @@ public class Api {
request.httpMethod = method request.httpMethod = method
request.addValue("application/json", forHTTPHeaderField: "Content-Type") request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept") request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("Bearer " + Settings.shared.user.token, forHTTPHeaderField: "Authorization") request.addValue("Bearer " + settings.user.token, forHTTPHeaderField: "Authorization")
if let body = body, method.uppercased() != "GET" { if let body = body, method.uppercased() != "GET" {
let encoder = JSONEncoder() let encoder = JSONEncoder()
@ -103,10 +114,26 @@ public class Api {
// MARK: - AutoCat public API // MARK: - AutoCat public API
public func login(email: String, password: String) async throws -> User { public func login(email: String, password: String) async throws -> User {
let body = [ let body = [
"email": email, "email": email,
"password": password "password": password
] ]
return try await self.makeBodyRequest(api: "user/login", body: body) return try await self.makeBodyRequest(api: "user/login", body: body)
} }
public func check(plateNumber: String, force: Bool = false) async throws -> Vehicle {
var body = [
"number": AnyEncodable(plateNumber),
"forceUpdate": AnyEncodable(force)
]
if let token = settings.user.firebaseIdToken {
body["googleIdToken"] = AnyEncodable(token)
}
return try await self.makeBodyRequest(api: "vehicles/check", body: body)
}
} }

View File

@ -3,11 +3,15 @@ import Foundation
public struct Constants { public struct Constants {
public static var baseUrl: String { public static var baseUrl: String {
#if DEBUG #if DEBUG
//return "http://127.0.0.1:3000/" return "http://127.0.0.1:3000/"
//return "http://192.168.1.67:3000/" //return "http://192.168.1.67:3000/"
return "https://vps.aliencat.pro:8443/" //return "https://vps.aliencat.pro:8443/"
#else #else
return "https://vps.aliencat.pro:8443/" return "https://vps.aliencat.pro:8443/"
#endif #endif
} }
public static let pnLettersMap: [Character: Character] = [
"А": "A", "В": "B", "Е": "E", "К": "K", "М": "M", "Н": "H", "О": "O", "Р": "P", "С": "C", "Т": "T", "У": "Y", "Х": "X"
]
} }

View File

@ -1,7 +1,17 @@
import Foundation import Foundation
public class AuthVM: ObservableObject { 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 { public func login(user: String, password: String) async throws {
Settings.shared.user = try await Api.shared.login(email: user, password: password) settings.user = try await api.login(email: user, password: password)
} }
} }

View File

@ -47,6 +47,7 @@ struct AuthView: View {
Spacer() Spacer()
} }
.buttonStyle(.bordered) .buttonStyle(.bordered)
.controlSize(.large)
.textFieldStyle(.roundedBorder) .textFieldStyle(.roundedBorder)
.padding(20) .padding(20)

View File

@ -2,7 +2,7 @@ import SwiftUI
struct CheckView: View { struct CheckView: View {
@State private var number: String = "" @State private var number: String = ""
@FetchRequest(entity: Vehicle.entity(), sortDescriptors: []) var vehicles: FetchedResults<Vehicle> @FetchRequest(entity: CDVehicle.entity(), sortDescriptors: []) var vehicles: FetchedResults<CDVehicle>
var body: some View { var body: some View {
NavigationView { NavigationView {

View File

@ -0,0 +1,67 @@
//
// 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))
}
}

View File

@ -0,0 +1,56 @@
//
// 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()
}
}

View File

@ -12,21 +12,22 @@ struct MainViewBig: View {
struct SidebarView: View { struct SidebarView: View {
@State var selection: String? @State var selection: String?
@State private var showAddPlateNumberView = false
@FetchRequest(entity: Vehicle.entity(), sortDescriptors: []) var vehicles: FetchedResults<Vehicle> @FetchRequest(entity: CDVehicle.entity(), sortDescriptors: []) var vehicles: FetchedResults<CDVehicle>
var body: some View { var body: some View {
List(selection: $selection) { List(selection: $selection) {
Section("History") { Section("History") {
NavigationLink(destination: VehiclesListView()) { NavigationLink(destination: VehiclesListView(vehicles: vehicles)) {
Label("All", systemImage: "car.2") Label("All", systemImage: "car.2")
.badge(vehicles.count) .badge(vehicles.count)
} }
NavigationLink(destination: VehiclesListView()) { NavigationLink(destination: VehiclesListView(vehicles: vehicles.filter(\.unrecognized))) {
Label("Unreconized", systemImage: "eye.slash") Label("Unreconized", systemImage: "eye.slash")
.badge(vehicles.filter(\.unrecognized).count) .badge(vehicles.filter(\.unrecognized).count)
} }
NavigationLink(destination: VehiclesListView()) { NavigationLink(destination: VehiclesListView(vehicles: vehicles.filter(\.outdated))) {
Label("Outdated", systemImage: "wind") Label("Outdated", systemImage: "wind")
.badge(vehicles.filter(\.outdated).count) .badge(vehicles.filter(\.outdated).count)
} }
@ -40,13 +41,18 @@ struct SidebarView: View {
.collapsible(false) .collapsible(false)
} }
.frame(minWidth: 180) .frame(minWidth: 180)
.sheet(isPresented: $showAddPlateNumberView, onDismiss: {
print("Dismiss")
}, content: {
CheckNumberPopup()
})
.toolbar { .toolbar {
ToolbarItem { ToolbarItem {
Spacer() Spacer()
} }
ToolbarItem { ToolbarItem {
Button { Button {
self.showAddPlateNumberView = true
} label: { } label: {
Image(systemName: "plus") Image(systemName: "plus")
} }
@ -56,16 +62,18 @@ struct SidebarView: View {
} }
} }
struct VehiclesListView: View { struct VehiclesListView<VehicleCollection>: View where VehicleCollection: RandomAccessCollection, VehicleCollection.Element == CDVehicle {
var vehicles: VehicleCollection
@State var selection: CDVehicle?
var body: some View { var body: some View {
Text("Vehicles list") List(selection: $selection) {
.toolbar { ForEach(vehicles, id: \.self) { vehicle in
ToolbarItem(placement: .primaryAction) { let number = PlateNumber(vehicle.number ?? "")
Button("xxx") { PlateNumberView(number: number, unrecognized: vehicle.unrecognized, outdated: vehicle.outdated)
}
}
} }
}
} }
} }