Adding tests for location picker
This commit is contained in:
parent
299ee23992
commit
9a1c05cb93
@ -39,6 +39,8 @@
|
|||||||
7A1CF80529A41C66007962DA /* RealmSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 7A1CF80429A41C66007962DA /* RealmSwift */; };
|
7A1CF80529A41C66007962DA /* RealmSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 7A1CF80429A41C66007962DA /* RealmSwift */; };
|
||||||
7A1CF81629A42117007962DA /* Realm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1CF81529A42117007962DA /* Realm.swift */; };
|
7A1CF81629A42117007962DA /* Realm.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1CF81529A42117007962DA /* Realm.swift */; };
|
||||||
7A1DC38E2517ED98002E9C99 /* BlockBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1DC38D2517ED98002E9C99 /* BlockBarButtonItem.swift */; };
|
7A1DC38E2517ED98002E9C99 /* BlockBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1DC38D2517ED98002E9C99 /* BlockBarButtonItem.swift */; };
|
||||||
|
7A22B6ED2C67FDEA00E60173 /* SwiftLocationMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A22B6EB2C67FDEA00E60173 /* SwiftLocationMock.swift */; };
|
||||||
|
7A22B6EE2C67FDEA00E60173 /* GeocoderMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A22B6EA2C67FDEA00E60173 /* GeocoderMock.swift */; };
|
||||||
7A27ADC7249D43210035F39E /* RegionsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A27ADC6249D43210035F39E /* RegionsController.swift */; };
|
7A27ADC7249D43210035F39E /* RegionsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A27ADC6249D43210035F39E /* RegionsController.swift */; };
|
||||||
7A27ADF3249F8B650035F39E /* RecordsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A27ADF2249F8B650035F39E /* RecordsController.swift */; };
|
7A27ADF3249F8B650035F39E /* RecordsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A27ADF2249F8B650035F39E /* RecordsController.swift */; };
|
||||||
7A27ADF5249FD2F90035F39E /* FileManagerExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A27ADF4249FD2F90035F39E /* FileManagerExt.swift */; };
|
7A27ADF5249FD2F90035F39E /* FileManagerExt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A27ADF4249FD2F90035F39E /* FileManagerExt.swift */; };
|
||||||
@ -130,6 +132,8 @@
|
|||||||
7AAAFADE2C4D23620050410D /* ACImageSliderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AAAFADD2C4D23620050410D /* ACImageSliderModel.swift */; };
|
7AAAFADE2C4D23620050410D /* ACImageSliderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AAAFADD2C4D23620050410D /* ACImageSliderModel.swift */; };
|
||||||
7AABB1F2267E9CC800D7AB32 /* SwiftDate in Frameworks */ = {isa = PBXBuildFile; productRef = 7AABB1F1267E9CC800D7AB32 /* SwiftDate */; };
|
7AABB1F2267E9CC800D7AB32 /* SwiftDate in Frameworks */ = {isa = PBXBuildFile; productRef = 7AABB1F1267E9CC800D7AB32 /* SwiftDate */; };
|
||||||
7AABDE26253350C30041AFC6 /* RxSectionedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AABDE25253350C30041AFC6 /* RxSectionedDataSource.swift */; };
|
7AABDE26253350C30041AFC6 /* RxSectionedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AABDE25253350C30041AFC6 /* RxSectionedDataSource.swift */; };
|
||||||
|
7AB0EF812C5CC0FE00291EE6 /* SwiftLocationProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB0EF802C5CC0FE00291EE6 /* SwiftLocationProtocol.swift */; };
|
||||||
|
7AB0EF892C5D307600291EE6 /* LocationServiceStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB0EF882C5D307600291EE6 /* LocationServiceStub.swift */; };
|
||||||
7AB5871D2C42C1CF00FA7B66 /* RealmSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 7AB5871C2C42C1CF00FA7B66 /* RealmSwift */; };
|
7AB5871D2C42C1CF00FA7B66 /* RealmSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 7AB5871C2C42C1CF00FA7B66 /* RealmSwift */; };
|
||||||
7AB587322C42D38E00FA7B66 /* StorageServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB587312C42D38E00FA7B66 /* StorageServiceProtocol.swift */; };
|
7AB587322C42D38E00FA7B66 /* StorageServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB587312C42D38E00FA7B66 /* StorageServiceProtocol.swift */; };
|
||||||
7AB587342C42D3FA00FA7B66 /* StorageService+Notes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB587332C42D3FA00FA7B66 /* StorageService+Notes.swift */; };
|
7AB587342C42D3FA00FA7B66 /* StorageService+Notes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AB587332C42D3FA00FA7B66 /* StorageService+Notes.swift */; };
|
||||||
@ -274,6 +278,8 @@
|
|||||||
7A17CE4B2A2E850200626A6E /* UISegmentedControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UISegmentedControl.swift; sourceTree = "<group>"; };
|
7A17CE4B2A2E850200626A6E /* UISegmentedControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UISegmentedControl.swift; sourceTree = "<group>"; };
|
||||||
7A1CF81529A42117007962DA /* Realm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Realm.swift; sourceTree = "<group>"; };
|
7A1CF81529A42117007962DA /* Realm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Realm.swift; sourceTree = "<group>"; };
|
||||||
7A1DC38D2517ED98002E9C99 /* BlockBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockBarButtonItem.swift; sourceTree = "<group>"; };
|
7A1DC38D2517ED98002E9C99 /* BlockBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockBarButtonItem.swift; sourceTree = "<group>"; };
|
||||||
|
7A22B6EA2C67FDEA00E60173 /* GeocoderMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeocoderMock.swift; sourceTree = "<group>"; };
|
||||||
|
7A22B6EB2C67FDEA00E60173 /* SwiftLocationMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftLocationMock.swift; sourceTree = "<group>"; };
|
||||||
7A27ADC6249D43210035F39E /* RegionsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegionsController.swift; sourceTree = "<group>"; };
|
7A27ADC6249D43210035F39E /* RegionsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegionsController.swift; sourceTree = "<group>"; };
|
||||||
7A27ADF2249F8B650035F39E /* RecordsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordsController.swift; sourceTree = "<group>"; };
|
7A27ADF2249F8B650035F39E /* RecordsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordsController.swift; sourceTree = "<group>"; };
|
||||||
7A27ADF4249FD2F90035F39E /* FileManagerExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManagerExt.swift; sourceTree = "<group>"; };
|
7A27ADF4249FD2F90035F39E /* FileManagerExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManagerExt.swift; sourceTree = "<group>"; };
|
||||||
@ -374,6 +380,8 @@
|
|||||||
7AAAFADD2C4D23620050410D /* ACImageSliderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ACImageSliderModel.swift; sourceTree = "<group>"; };
|
7AAAFADD2C4D23620050410D /* ACImageSliderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ACImageSliderModel.swift; sourceTree = "<group>"; };
|
||||||
7AABDE25253350C30041AFC6 /* RxSectionedDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RxSectionedDataSource.swift; sourceTree = "<group>"; };
|
7AABDE25253350C30041AFC6 /* RxSectionedDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RxSectionedDataSource.swift; sourceTree = "<group>"; };
|
||||||
7AAE6AD224CDDF950023860B /* VehicleEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleEvent.swift; sourceTree = "<group>"; };
|
7AAE6AD224CDDF950023860B /* VehicleEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleEvent.swift; sourceTree = "<group>"; };
|
||||||
|
7AB0EF802C5CC0FE00291EE6 /* SwiftLocationProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftLocationProtocol.swift; sourceTree = "<group>"; };
|
||||||
|
7AB0EF882C5D307600291EE6 /* LocationServiceStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationServiceStub.swift; sourceTree = "<group>"; };
|
||||||
7AB562B9249C9E9B00473D53 /* VehicleRegion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleRegion.swift; sourceTree = "<group>"; };
|
7AB562B9249C9E9B00473D53 /* VehicleRegion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleRegion.swift; sourceTree = "<group>"; };
|
||||||
7AB587222C42D27F00FA7B66 /* AutoCatTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AutoCatTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
7AB587222C42D27F00FA7B66 /* AutoCatTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AutoCatTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
7AB587312C42D38E00FA7B66 /* StorageServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageServiceProtocol.swift; sourceTree = "<group>"; };
|
7AB587312C42D38E00FA7B66 /* StorageServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageServiceProtocol.swift; sourceTree = "<group>"; };
|
||||||
@ -641,6 +649,15 @@
|
|||||||
path = NotesScreen;
|
path = NotesScreen;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
7A22B6EC2C67FDEA00E60173 /* Mocks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
7A22B6EA2C67FDEA00E60173 /* GeocoderMock.swift */,
|
||||||
|
7A22B6EB2C67FDEA00E60173 /* SwiftLocationMock.swift */,
|
||||||
|
);
|
||||||
|
path = Mocks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
7A3F07A924360D9100E59687 /* Extensions */ = {
|
7A3F07A924360D9100E59687 /* Extensions */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -718,6 +735,7 @@
|
|||||||
7A60D24C2C5A9D4900D13F7B /* LocationService.swift */,
|
7A60D24C2C5A9D4900D13F7B /* LocationService.swift */,
|
||||||
7A60D24E2C5A9DA800D13F7B /* LocationServiceProtocol.swift */,
|
7A60D24E2C5A9DA800D13F7B /* LocationServiceProtocol.swift */,
|
||||||
7A60D2502C5A9E4200D13F7B /* GeocoderProtocol.swift */,
|
7A60D2502C5A9E4200D13F7B /* GeocoderProtocol.swift */,
|
||||||
|
7AB0EF802C5CC0FE00291EE6 /* SwiftLocationProtocol.swift */,
|
||||||
);
|
);
|
||||||
path = LocationService;
|
path = LocationService;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -857,6 +875,7 @@
|
|||||||
children = (
|
children = (
|
||||||
7AB587362C42E3EC00FA7B66 /* StorageServiceStub.swift */,
|
7AB587362C42E3EC00FA7B66 /* StorageServiceStub.swift */,
|
||||||
7A176DB12C43071A00999D6B /* ApiServiceStub.swift */,
|
7A176DB12C43071A00999D6B /* ApiServiceStub.swift */,
|
||||||
|
7AB0EF882C5D307600291EE6 /* LocationServiceStub.swift */,
|
||||||
);
|
);
|
||||||
path = Preview;
|
path = Preview;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -910,6 +929,7 @@
|
|||||||
7AF6D2292677C3950086EA64 /* Extensions */,
|
7AF6D2292677C3950086EA64 /* Extensions */,
|
||||||
7A11474523FF2A9000B424AF /* Models */,
|
7A11474523FF2A9000B424AF /* Models */,
|
||||||
7AF6D20D2677C0C30086EA64 /* Utils */,
|
7AF6D20D2677C0C30086EA64 /* Utils */,
|
||||||
|
7A22B6EC2C67FDEA00E60173 /* Mocks */,
|
||||||
7AF6D1F12677C03B0086EA64 /* AutoCatCore.h */,
|
7AF6D1F12677C03B0086EA64 /* AutoCatCore.h */,
|
||||||
7AF6D1F22677C03B0086EA64 /* Info.plist */,
|
7AF6D1F22677C03B0086EA64 /* Info.plist */,
|
||||||
);
|
);
|
||||||
@ -1269,6 +1289,7 @@
|
|||||||
7A64AE732469DFB600ABE48E /* DismissAnimationController.swift in Sources */,
|
7A64AE732469DFB600ABE48E /* DismissAnimationController.swift in Sources */,
|
||||||
7ADF6C97250F41B000F237B2 /* PNKeyboard.swift in Sources */,
|
7ADF6C97250F41B000F237B2 /* PNKeyboard.swift in Sources */,
|
||||||
7A1022702C551EFD00B84627 /* LocationEditCoordinator.swift in Sources */,
|
7A1022702C551EFD00B84627 /* LocationEditCoordinator.swift in Sources */,
|
||||||
|
7AB0EF892C5D307600291EE6 /* LocationServiceStub.swift in Sources */,
|
||||||
7A7158042C43EAA200852088 /* OwnersCoordinator.swift in Sources */,
|
7A7158042C43EAA200852088 /* OwnersCoordinator.swift in Sources */,
|
||||||
7A7547E024032CB6004E8406 /* VehiclePhotoCell.swift in Sources */,
|
7A7547E024032CB6004E8406 /* VehiclePhotoCell.swift in Sources */,
|
||||||
7A17CE4C2A2E850200626A6E /* UISegmentedControl.swift in Sources */,
|
7A17CE4C2A2E850200626A6E /* UISegmentedControl.swift in Sources */,
|
||||||
@ -1294,11 +1315,14 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
7A5D84C22C1AE5C900C2209B /* VehicleModel.swift in Sources */,
|
7A5D84C22C1AE5C900C2209B /* VehicleModel.swift in Sources */,
|
||||||
|
7A22B6ED2C67FDEA00E60173 /* SwiftLocationMock.swift in Sources */,
|
||||||
|
7A22B6EE2C67FDEA00E60173 /* GeocoderMock.swift in Sources */,
|
||||||
7A5D84B92C1AD3C200C2209B /* DtoConvertible.swift in Sources */,
|
7A5D84B92C1AD3C200C2209B /* DtoConvertible.swift in Sources */,
|
||||||
7AF6D2182677C1680086EA64 /* VehicleAd.swift in Sources */,
|
7AF6D2182677C1680086EA64 /* VehicleAd.swift in Sources */,
|
||||||
7A761C08267E8EA20005F28F /* JWT.swift in Sources */,
|
7A761C08267E8EA20005F28F /* JWT.swift in Sources */,
|
||||||
7A64A2242C1A07EA00284124 /* Formatters.swift in Sources */,
|
7A64A2242C1A07EA00284124 /* Formatters.swift in Sources */,
|
||||||
7A5D84C02C1AE4DC00C2209B /* VehicleEngine.swift in Sources */,
|
7A5D84C02C1AE4DC00C2209B /* VehicleEngine.swift in Sources */,
|
||||||
|
7AB0EF812C5CC0FE00291EE6 /* SwiftLocationProtocol.swift in Sources */,
|
||||||
7AF6D2282677C2DC0086EA64 /* Constants.swift in Sources */,
|
7AF6D2282677C2DC0086EA64 /* Constants.swift in Sources */,
|
||||||
7A64A2182C19E64800284124 /* VehicleOwnershipPeriodDto.swift in Sources */,
|
7A64A2182C19E64800284124 /* VehicleOwnershipPeriodDto.swift in Sources */,
|
||||||
7A599C3B2C18B36A00D47C18 /* FbVerifyTokenModel.swift in Sources */,
|
7A599C3B2C18B36A00D47C18 /* FbVerifyTokenModel.swift in Sources */,
|
||||||
@ -1543,16 +1567,17 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements;
|
CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 132;
|
CURRENT_PROJECT_VERSION = 133;
|
||||||
DEVELOPMENT_TEAM = 46DTTB8X4S;
|
DEVELOPMENT_TEAM = 46DTTB8X4S;
|
||||||
INFOPLIST_FILE = AutoCat/Info.plist;
|
INFOPLIST_FILE = AutoCat/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = AutoCat;
|
INFOPLIST_KEY_CFBundleDisplayName = AutoCat;
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 17.6;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 14.6;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCat;
|
PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCat;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
@ -1569,16 +1594,17 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements;
|
CODE_SIGN_ENTITLEMENTS = AutoCat/AutoCat.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 132;
|
CURRENT_PROJECT_VERSION = 133;
|
||||||
DEVELOPMENT_TEAM = 46DTTB8X4S;
|
DEVELOPMENT_TEAM = 46DTTB8X4S;
|
||||||
INFOPLIST_FILE = AutoCat/Info.plist;
|
INFOPLIST_FILE = AutoCat/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = AutoCat;
|
INFOPLIST_KEY_CFBundleDisplayName = AutoCat;
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 17.6;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 14.6;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCat;
|
PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCat;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
@ -1601,8 +1627,9 @@
|
|||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 17.6;
|
||||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 14.6;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCatCoreTests;
|
PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCatCoreTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
@ -1625,8 +1652,9 @@
|
|||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 17.6;
|
||||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 14.6;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCatCoreTests;
|
PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCatCoreTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
@ -1649,8 +1677,9 @@
|
|||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 17.6;
|
||||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 14.6;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCatTests;
|
PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCatTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
@ -1675,8 +1704,9 @@
|
|||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 17.6;
|
||||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 14.6;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCatTests;
|
PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCatTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
@ -1700,12 +1730,13 @@
|
|||||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||||
INFOPLIST_FILE = AutoCatCore/Info.plist;
|
INFOPLIST_FILE = AutoCatCore/Info.plist;
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 17.6;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@loader_path/Frameworks",
|
"@loader_path/Frameworks",
|
||||||
);
|
);
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 14.6;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCatCore;
|
PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCatCore;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
@ -1730,12 +1761,13 @@
|
|||||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||||
INFOPLIST_FILE = AutoCatCore/Info.plist;
|
INFOPLIST_FILE = AutoCatCore/Info.plist;
|
||||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 17.6;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
"@loader_path/Frameworks",
|
"@loader_path/Frameworks",
|
||||||
);
|
);
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 14.6;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCatCore;
|
PRODUCT_BUNDLE_IDENTIFIER = pro.aliencat.AutoCatCore;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
|
|||||||
@ -33,8 +33,8 @@
|
|||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/Kolos65/Mockable",
|
"location" : "https://github.com/Kolos65/Mockable",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "81ccaead99a3c038c09345caa2888ae74b644ee9",
|
"revision" : "da977ecb20974c4b1cf185f5fd38771b2d4674fb",
|
||||||
"version" : "0.0.9"
|
"version" : "0.0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -51,8 +51,8 @@
|
|||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/realm/realm-core.git",
|
"location" : "https://github.com/realm/realm-core.git",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "f3d7ae5f9f31d90b327a64536bb7801cc69fd85b",
|
"revision" : "c2552e1d36867cb42b28130e894a81fc17081062",
|
||||||
"version" : "14.9.0"
|
"version" : "14.12.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -60,17 +60,17 @@
|
|||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/realm/realm-swift.git",
|
"location" : "https://github.com/realm/realm-swift.git",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "4c4413abd0cd2221f59318f800960fe5bddc1494",
|
"revision" : "5221a83dc720823e3017493394d00d49ccf13446",
|
||||||
"version" : "10.51.0"
|
"version" : "10.52.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"identity" : "swift-syntax",
|
"identity" : "swift-syntax",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/apple/swift-syntax.git",
|
"location" : "https://github.com/swiftlang/swift-syntax.git",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "303e5c5c36d6a558407d364878df131c3546fad8",
|
"revision" : "2bc86522d115234d1f588efe2bcb4ce4be8f8b82",
|
||||||
"version" : "510.0.2"
|
"version" : "510.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -25,8 +25,10 @@ class AudioRecordCell: UITableViewCell, ConfigurableCell {
|
|||||||
self.componentsFormatter.allowedUnits = [.minute, .second]
|
self.componentsFormatter.allowedUnits = [.minute, .second]
|
||||||
self.componentsFormatter.zeroFormattingBehavior = .pad
|
self.componentsFormatter.zeroFormattingBehavior = .pad
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
self.progressView.progress = 0
|
self.progressView.progress = 0
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override func prepareForReuse() {
|
override func prepareForReuse() {
|
||||||
super.prepareForReuse()
|
super.prepareForReuse()
|
||||||
|
|||||||
@ -10,9 +10,12 @@ class VehicleNoteCell: UITableViewCell {
|
|||||||
|
|
||||||
override func awakeFromNib() {
|
override func awakeFromNib() {
|
||||||
super.awakeFromNib()
|
super.awakeFromNib()
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
self.dateFormatter.dateStyle = .medium
|
self.dateFormatter.dateStyle = .medium
|
||||||
self.dateFormatter.timeStyle = .medium
|
self.dateFormatter.timeStyle = .medium
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func configure(with note: VehicleNoteDto) {
|
func configure(with note: VehicleNoteDto) {
|
||||||
self.noteText.text = note.text
|
self.noteText.text = note.text
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import UIKit
|
|||||||
import Kingfisher
|
import Kingfisher
|
||||||
import AutoCatCore
|
import AutoCatCore
|
||||||
|
|
||||||
|
@MainActor
|
||||||
class VehiclePhotoCell: UICollectionViewCell {
|
class VehiclePhotoCell: UICollectionViewCell {
|
||||||
@IBOutlet weak var photo: UIImageView!
|
@IBOutlet weak var photo: UIImageView!
|
||||||
@IBOutlet weak var model: UILabel!
|
@IBOutlet weak var model: UILabel!
|
||||||
@ -11,9 +12,12 @@ class VehiclePhotoCell: UICollectionViewCell {
|
|||||||
|
|
||||||
override func awakeFromNib() {
|
override func awakeFromNib() {
|
||||||
super.awakeFromNib()
|
super.awakeFromNib()
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
self.layer.cornerRadius = 8
|
self.layer.cornerRadius = 8
|
||||||
formatter.timeStyle = .none
|
self.formatter.timeStyle = .none
|
||||||
formatter.dateStyle = .medium
|
self.formatter.dateStyle = .medium
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func prepareForReuse() {
|
override func prepareForReuse() {
|
||||||
|
|||||||
27
AutoCat/Preview/LocationServiceStub.swift
Normal file
27
AutoCat/Preview/LocationServiceStub.swift
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
//
|
||||||
|
// LocationServiceStub.swift
|
||||||
|
// AutoCat
|
||||||
|
//
|
||||||
|
// Created by Selim Mustafaev on 02.08.2024.
|
||||||
|
// Copyright © 2024 Selim Mustafaev. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import AutoCatCore
|
||||||
|
|
||||||
|
final class LocationServiceStub: LocationServiceProtocol {
|
||||||
|
|
||||||
|
let event: VehicleEventDto
|
||||||
|
|
||||||
|
init(event: VehicleEventDto) {
|
||||||
|
self.event = event
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAddressForLocation(latitude: Double, longitude: Double) async throws -> String {
|
||||||
|
event.address ?? ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func requestCurrentLocation() async throws -> AutoCatCore.VehicleEventDto {
|
||||||
|
event
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -23,7 +23,7 @@ final class LocationPickerCoordinator: Coordinator {
|
|||||||
|
|
||||||
func start() async throws -> VehicleEventDto? {
|
func start() async throws -> VehicleEventDto? {
|
||||||
|
|
||||||
let viewModel = LocationPickerViewModel(event: event)
|
let viewModel = LocationPickerViewModel(event: event, locationService: LocationService.shared)
|
||||||
let screen = LocationPickerScreen(viewModel: viewModel)
|
let screen = LocationPickerScreen(viewModel: viewModel)
|
||||||
let controller = CustomHostingController(rootView: screen)
|
let controller = CustomHostingController(rootView: screen)
|
||||||
viewController?.pushViewController(controller, animated: true)
|
viewController?.pushViewController(controller, animated: true)
|
||||||
|
|||||||
@ -18,15 +18,22 @@ struct LocationPickerScreen: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
Map(coordinateRegion: $viewModel.region)
|
Map(initialPosition: viewModel.position)
|
||||||
|
.mapControls {
|
||||||
|
MapUserLocationButton()
|
||||||
|
}
|
||||||
|
.onMapCameraChange(frequency: .onEnd) { context in
|
||||||
|
Task { await viewModel.updateEvent(center: context.region.center) }
|
||||||
|
}
|
||||||
|
|
||||||
Image(systemName: "mappin.and.ellipse")
|
Image(systemName: "mappin.and.ellipse")
|
||||||
.resizable()
|
.resizable()
|
||||||
.aspectRatio(contentMode: .fit)
|
.aspectRatio(contentMode: .fit)
|
||||||
.frame(height: 48)
|
.frame(height: 48)
|
||||||
.offset(.init(width: 0, height: -10))
|
.offset(.init(width: 0, height: -16))
|
||||||
.foregroundColor(.blue)
|
.foregroundColor(.blue)
|
||||||
}
|
}
|
||||||
.ignoresSafeArea()
|
//.ignoresSafeArea()
|
||||||
.navigationTitle(viewModel.event.location)
|
.navigationTitle(viewModel.event.location)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItem(placement: .primaryAction) {
|
ToolbarItem(placement: .primaryAction) {
|
||||||
@ -44,5 +51,9 @@ struct LocationPickerScreen: View {
|
|||||||
var event = VehicleEventDto(lat: 47.250049, lon: 39.711821)
|
var event = VehicleEventDto(lat: 47.250049, lon: 39.711821)
|
||||||
event.address = "Ул. Ленина, 123"
|
event.address = "Ул. Ленина, 123"
|
||||||
|
|
||||||
return LocationPickerScreen(viewModel: .init(event: event))
|
let locationService = LocationServiceStub(event: event)
|
||||||
|
let viewModel = LocationPickerViewModel(event: event,
|
||||||
|
locationService: locationService)
|
||||||
|
|
||||||
|
return LocationPickerScreen(viewModel: viewModel)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,58 +14,30 @@ import SwiftUI
|
|||||||
@MainActor
|
@MainActor
|
||||||
final class LocationPickerViewModel: ObservableObject {
|
final class LocationPickerViewModel: ObservableObject {
|
||||||
|
|
||||||
|
let locationService: LocationServiceProtocol
|
||||||
|
|
||||||
@Published var event: VehicleEventDto
|
@Published var event: VehicleEventDto
|
||||||
@Published var region: MKCoordinateRegion {
|
@Published var position: MapCameraPosition
|
||||||
didSet {
|
|
||||||
Task { await updateEvent(region: region) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var result: VehicleEventDto?
|
var result: VehicleEventDto?
|
||||||
|
|
||||||
var geocodingTask: Task<Void, Error>?
|
init(event: VehicleEventDto, locationService: LocationServiceProtocol) {
|
||||||
|
|
||||||
init(event: VehicleEventDto) {
|
|
||||||
self.event = event
|
self.event = event
|
||||||
|
self.locationService = locationService
|
||||||
let center = CLLocationCoordinate2D(latitude: event.latitude, longitude: event.longitude)
|
|
||||||
self.region = MKCoordinateRegion(center: center,
|
|
||||||
latitudinalMeters: 1000,
|
|
||||||
longitudinalMeters: 1000)
|
|
||||||
|
|
||||||
if event.latitude == 0 && event.longitude == 0 {
|
if event.latitude == 0 && event.longitude == 0 {
|
||||||
Task { await moveToCurrentLocation() }
|
self.position = .userLocation(fallback: .automatic)
|
||||||
|
} else {
|
||||||
|
let center = CLLocationCoordinate2D(latitude: event.latitude, longitude: event.longitude)
|
||||||
|
self.position = .region(.init(center: center, latitudinalMeters: 1000, longitudinalMeters: 1000))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func moveToCurrentLocation() async {
|
func updateEvent(center: CLLocationCoordinate2D) async {
|
||||||
do {
|
event.latitude = center.latitude
|
||||||
let currentEvent = try await RxLocationManager.requestCurrentLocation()
|
event.longitude = center.longitude
|
||||||
let center = CLLocationCoordinate2D(latitude: currentEvent.latitude,
|
event.address = try? await locationService.getAddressForLocation(latitude: center.latitude,
|
||||||
longitude: currentEvent.longitude)
|
longitude: center.longitude)
|
||||||
self.region = MKCoordinateRegion(center: center,
|
|
||||||
latitudinalMeters: 1000,
|
|
||||||
longitudinalMeters: 1000)
|
|
||||||
|
|
||||||
event.latitude = currentEvent.latitude
|
|
||||||
event.longitude = currentEvent.longitude
|
|
||||||
event.address = try? await RxLocationManager.getAddressForLocation(latitude: region.center.latitude,
|
|
||||||
longitude: region.center.longitude)
|
|
||||||
} catch {
|
|
||||||
print(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateEvent(region: MKCoordinateRegion) async {
|
|
||||||
geocodingTask?.cancel()
|
|
||||||
geocodingTask = Task {
|
|
||||||
event.latitude = region.center.latitude
|
|
||||||
event.longitude = region.center.longitude
|
|
||||||
try await Task.sleep(nanoseconds: 500_000_000)
|
|
||||||
event.address = try? await RxLocationManager.getAddressForLocation(latitude: region.center.latitude,
|
|
||||||
longitude: region.center.longitude)
|
|
||||||
geocodingTask = nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func done() {
|
func done() {
|
||||||
|
|||||||
45
AutoCatCore/Mocks/SwiftLocationMock.swift
Normal file
45
AutoCatCore/Mocks/SwiftLocationMock.swift
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
//
|
||||||
|
// SwiftLocationMock.swift
|
||||||
|
// AutoCatCoreTests
|
||||||
|
//
|
||||||
|
// Created by Selim Mustafaev on 02.08.2024.
|
||||||
|
// Copyright © 2024 Selim Mustafaev. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import AutoCatCore
|
||||||
|
import CoreLocation
|
||||||
|
import SwiftLocation
|
||||||
|
|
||||||
|
final class SwiftLocationMock {
|
||||||
|
|
||||||
|
var authorizationStatus: CLAuthorizationStatus = .notDetermined
|
||||||
|
var requestedStatus: CLAuthorizationStatus = .notDetermined
|
||||||
|
var location: CLLocation?
|
||||||
|
|
||||||
|
var requestLocationTime: TimeInterval = 0
|
||||||
|
var requestLocationCount = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SwiftLocationMock: SwiftLocationProtocol {
|
||||||
|
|
||||||
|
func requestPermission(_ permission: LocationPermission) async throws -> CLAuthorizationStatus {
|
||||||
|
authorizationStatus = requestedStatus
|
||||||
|
return requestedStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
func requestLocation(accuracy filters: AccuracyFilters?,
|
||||||
|
timeout: TimeInterval?) async throws -> Tasks.ContinuousUpdateLocation.StreamEvent {
|
||||||
|
|
||||||
|
requestLocationCount += 1
|
||||||
|
|
||||||
|
if requestLocationTime > 0 {
|
||||||
|
try await Task.sleep(nanoseconds: UInt64(requestLocationTime*1_000_000_000))
|
||||||
|
}
|
||||||
|
|
||||||
|
if let location {
|
||||||
|
return .didUpdateLocations([location])
|
||||||
|
} else {
|
||||||
|
return .didUpdateLocations([])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,16 +7,53 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import CoreLocation
|
import CoreLocation
|
||||||
|
import SwiftLocation
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
public final class LocationService: LocationServiceProtocol {
|
public final class LocationService {
|
||||||
|
|
||||||
private var geocoder: GeocoderProtocol
|
public static let shared = LocationService(geocoder: CLGeocoder(),
|
||||||
|
locationManager: Location())
|
||||||
|
|
||||||
public init(geocoder: GeocoderProtocol) {
|
private let geocoder: GeocoderProtocol
|
||||||
|
private var locationManager: SwiftLocationProtocol
|
||||||
|
|
||||||
|
private var eventTask: Task<VehicleEventDto,Error>?
|
||||||
|
|
||||||
|
public init(geocoder: GeocoderProtocol, locationManager: SwiftLocationProtocol) {
|
||||||
self.geocoder = geocoder
|
self.geocoder = geocoder
|
||||||
|
self.locationManager = locationManager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func checkPermissions() async throws {
|
||||||
|
|
||||||
|
switch locationManager.authorizationStatus {
|
||||||
|
case .authorizedWhenInUse, .authorizedAlways:
|
||||||
|
break
|
||||||
|
case .notDetermined:
|
||||||
|
_ = try await locationManager.requestPermission(.always)
|
||||||
|
try await checkPermissions()
|
||||||
|
case .denied:
|
||||||
|
throw CLError(.denied)
|
||||||
|
default:
|
||||||
|
throw LocationError.permission
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func requestLocation() async throws -> VehicleEventDto {
|
||||||
|
try await checkPermissions()
|
||||||
|
let locationEvent = try await locationManager.requestLocation(accuracy: nil, timeout: 20)
|
||||||
|
|
||||||
|
guard let coordinate = locationEvent.location?.coordinate else {
|
||||||
|
throw LocationError.generic
|
||||||
|
}
|
||||||
|
|
||||||
|
return VehicleEventDto(lat: coordinate.latitude, lon: coordinate.longitude)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension LocationService: LocationServiceProtocol {
|
||||||
|
|
||||||
public func getAddressForLocation(latitude: Double, longitude: Double) async throws -> String {
|
public func getAddressForLocation(latitude: Double, longitude: Double) async throws -> String {
|
||||||
|
|
||||||
let location = CLLocation(latitude: latitude, longitude: longitude)
|
let location = CLLocation(latitude: latitude, longitude: longitude)
|
||||||
@ -28,4 +65,20 @@ public final class LocationService: LocationServiceProtocol {
|
|||||||
throw LocationError.reverseGeocode
|
throw LocationError.reverseGeocode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
public func requestCurrentLocation() async throws -> VehicleEventDto {
|
||||||
|
|
||||||
|
if let eventTask {
|
||||||
|
return try await eventTask.value
|
||||||
|
} else {
|
||||||
|
let task = Task {
|
||||||
|
let location = try await requestLocation()
|
||||||
|
eventTask = nil
|
||||||
|
return location
|
||||||
|
}
|
||||||
|
eventTask = task
|
||||||
|
return try await task.value
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,9 +6,9 @@
|
|||||||
// Copyright © 2024 Selim Mustafaev. All rights reserved.
|
// Copyright © 2024 Selim Mustafaev. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
@MainActor
|
||||||
|
|
||||||
public protocol LocationServiceProtocol {
|
public protocol LocationServiceProtocol {
|
||||||
|
|
||||||
func getAddressForLocation(latitude: Double, longitude: Double) async throws -> String
|
func getAddressForLocation(latitude: Double, longitude: Double) async throws -> String
|
||||||
|
func requestCurrentLocation() async throws -> VehicleEventDto
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,21 @@
|
|||||||
|
//
|
||||||
|
// SwiftLocationProtocol.swift
|
||||||
|
// AutoCatCore
|
||||||
|
//
|
||||||
|
// Created by Selim Mustafaev on 02.08.2024.
|
||||||
|
// Copyright © 2024 Selim Mustafaev. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftLocation
|
||||||
|
import CoreLocation
|
||||||
|
|
||||||
|
public protocol SwiftLocationProtocol {
|
||||||
|
|
||||||
|
var authorizationStatus: CLAuthorizationStatus { get }
|
||||||
|
|
||||||
|
func requestPermission(_ permission: LocationPermission) async throws -> CLAuthorizationStatus
|
||||||
|
func requestLocation(accuracy filters: AccuracyFilters?,
|
||||||
|
timeout: TimeInterval?) async throws -> Tasks.ContinuousUpdateLocation.StreamEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Location: SwiftLocationProtocol { }
|
||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import Testing
|
import Testing
|
||||||
import CoreLocation
|
import CoreLocation
|
||||||
import AutoCatCore
|
@testable import AutoCatCore
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
struct LocationServiceTests {
|
struct LocationServiceTests {
|
||||||
@ -18,10 +18,12 @@ struct LocationServiceTests {
|
|||||||
let address = "Test Address"
|
let address = "Test Address"
|
||||||
|
|
||||||
let geocoder = GeocoderMock()
|
let geocoder = GeocoderMock()
|
||||||
|
let locationManager = SwiftLocationMock()
|
||||||
let locationService: LocationService
|
let locationService: LocationService
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
self.locationService = LocationService(geocoder: geocoder)
|
self.locationService = LocationService(geocoder: geocoder,
|
||||||
|
locationManager: locationManager)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -58,4 +60,85 @@ struct LocationServiceTests {
|
|||||||
longitude: longitude)
|
longitude: longitude)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test("Get location: denied")
|
||||||
|
func getLocationDenied() async throws {
|
||||||
|
|
||||||
|
locationManager.authorizationStatus = .denied
|
||||||
|
|
||||||
|
await #expect(throws: CLError(.denied)) {
|
||||||
|
_ = try await locationService.requestCurrentLocation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Get location: not determined -> denied")
|
||||||
|
func getLocationNotDeterminedDenied() async throws {
|
||||||
|
|
||||||
|
locationManager.authorizationStatus = .notDetermined
|
||||||
|
locationManager.requestedStatus = .denied
|
||||||
|
|
||||||
|
await #expect(throws: CLError(.denied)) {
|
||||||
|
_ = try await locationService.requestCurrentLocation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Get location: not determined -> allow")
|
||||||
|
func getLocationNotDeterminedAllow() async throws {
|
||||||
|
|
||||||
|
locationManager.authorizationStatus = .notDetermined
|
||||||
|
locationManager.requestedStatus = .authorizedWhenInUse
|
||||||
|
locationManager.location = CLLocation(latitude: latitude, longitude: longitude)
|
||||||
|
|
||||||
|
let event = try await locationService.requestCurrentLocation()
|
||||||
|
|
||||||
|
#expect(event.latitude == latitude)
|
||||||
|
#expect(event.longitude == longitude)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Get location: normal")
|
||||||
|
func getLocationNormal() async throws {
|
||||||
|
|
||||||
|
locationManager.authorizationStatus = .authorizedWhenInUse
|
||||||
|
locationManager.location = CLLocation(latitude: latitude, longitude: longitude)
|
||||||
|
|
||||||
|
let event = try await locationService.requestCurrentLocation()
|
||||||
|
|
||||||
|
#expect(event.latitude == latitude)
|
||||||
|
#expect(event.longitude == longitude)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Get location: no location")
|
||||||
|
func getLocationNone() async throws {
|
||||||
|
|
||||||
|
locationManager.authorizationStatus = .authorizedWhenInUse
|
||||||
|
|
||||||
|
await #expect(throws: LocationError.generic) {
|
||||||
|
_ = try await locationService.requestCurrentLocation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Get location: parallel requests")
|
||||||
|
func getLocationParallel() async throws {
|
||||||
|
|
||||||
|
locationManager.authorizationStatus = .authorizedWhenInUse
|
||||||
|
locationManager.location = CLLocation(latitude: latitude, longitude: longitude)
|
||||||
|
locationManager.requestLocationTime = 1
|
||||||
|
|
||||||
|
async let task1 = locationService.requestCurrentLocation()
|
||||||
|
async let task2 = locationService.requestCurrentLocation()
|
||||||
|
|
||||||
|
try await Task.sleep(nanoseconds: 1_500_000_000)
|
||||||
|
async let task3 = locationService.requestCurrentLocation()
|
||||||
|
|
||||||
|
let (event1, event2, event3) = try await (task1, task2, task3)
|
||||||
|
|
||||||
|
#expect(locationManager.requestLocationCount == 2)
|
||||||
|
|
||||||
|
#expect(event1.latitude == latitude)
|
||||||
|
#expect(event1.longitude == longitude)
|
||||||
|
#expect(event2.latitude == latitude)
|
||||||
|
#expect(event2.longitude == longitude)
|
||||||
|
#expect(event3.latitude == latitude)
|
||||||
|
#expect(event3.longitude == longitude)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
66
AutoCatTests/LocationPickerTests.swift
Normal file
66
AutoCatTests/LocationPickerTests.swift
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
//
|
||||||
|
// LocationPickerTests.swift
|
||||||
|
// AutoCatTests
|
||||||
|
//
|
||||||
|
// Created by Selim Mustafaev on 10.08.2024.
|
||||||
|
// Copyright © 2024 Selim Mustafaev. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Testing
|
||||||
|
import CoreLocation
|
||||||
|
|
||||||
|
@testable import AutoCat
|
||||||
|
@testable import AutoCatCore
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
struct LocationPickerTests {
|
||||||
|
|
||||||
|
let latitude: CLLocationDegrees = 10
|
||||||
|
let longitude: CLLocationDegrees = 10
|
||||||
|
let address = "Test Address"
|
||||||
|
|
||||||
|
let geocoder = GeocoderMock()
|
||||||
|
let locationManager = SwiftLocationMock()
|
||||||
|
let locationService: LocationService
|
||||||
|
|
||||||
|
init() {
|
||||||
|
self.locationService = LocationService(geocoder: geocoder,
|
||||||
|
locationManager: locationManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Set initial location (user)")
|
||||||
|
func setInitialLocationUser() async throws {
|
||||||
|
|
||||||
|
let viewModel = LocationPickerViewModel(event: .init(lat: 0, lon: 0),
|
||||||
|
locationService: locationService)
|
||||||
|
|
||||||
|
#expect(viewModel.position == .userLocation(fallback: .automatic))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Set initial location (custom)")
|
||||||
|
func setInitialLocationCustom() async throws {
|
||||||
|
|
||||||
|
let viewModel = LocationPickerViewModel(event: .init(lat: latitude, lon: longitude),
|
||||||
|
locationService: locationService)
|
||||||
|
|
||||||
|
#expect(viewModel.position.region?.center.latitude == latitude)
|
||||||
|
#expect(viewModel.position.region?.center.longitude == longitude)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test("Update event")
|
||||||
|
func updateEvent() async throws {
|
||||||
|
|
||||||
|
let viewModel = LocationPickerViewModel(event: .init(lat: 0, lon: 0),
|
||||||
|
locationService: locationService)
|
||||||
|
|
||||||
|
geocoder.addLocation(latitude: latitude,
|
||||||
|
longitude: longitude,
|
||||||
|
address: address)
|
||||||
|
|
||||||
|
await viewModel.updateEvent(center: .init(latitude: latitude, longitude: longitude))
|
||||||
|
|
||||||
|
#expect(viewModel.event.latitude == latitude)
|
||||||
|
#expect(viewModel.event.longitude == longitude)
|
||||||
|
#expect(viewModel.event.address == address)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user