diff --git a/ImageFeed.xcodeproj/project.pbxproj b/ImageFeed.xcodeproj/project.pbxproj index 3597a50..ca5e1a4 100644 --- a/ImageFeed.xcodeproj/project.pbxproj +++ b/ImageFeed.xcodeproj/project.pbxproj @@ -21,6 +21,18 @@ 8447A6F42A9A849F00579B97 /* ProfileService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8447A6F32A9A849F00579B97 /* ProfileService.swift */; }; 8447A6F62A9A8C8600579B97 /* ProfileImageService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8447A6F52A9A8C8600579B97 /* ProfileImageService.swift */; }; 844D31F92A8176E000FEDD16 /* SingleImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844D31F82A8176E000FEDD16 /* SingleImageViewController.swift */; }; + 845BCF2C2AB50ACA0012FCA0 /* AuthHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845BCF2B2AB50ACA0012FCA0 /* AuthHelper.swift */; }; + 845BCF2E2AB50ADB0012FCA0 /* WebViewPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845BCF2D2AB50ADB0012FCA0 /* WebViewPresenter.swift */; }; + 845BCF302AB50AE90012FCA0 /* WebViewPresenterSpy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845BCF2F2AB50AE90012FCA0 /* WebViewPresenterSpy.swift */; }; + 845BCF322AB50AF90012FCA0 /* WebViewViewControllerSpy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845BCF312AB50AF90012FCA0 /* WebViewViewControllerSpy.swift */; }; + 845BCF342AB50BC80012FCA0 /* ProfileViewPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845BCF332AB50BC80012FCA0 /* ProfileViewPresenter.swift */; }; + 845BCF362AB50BDC0012FCA0 /* ProfileViewPresenterSpy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845BCF352AB50BDC0012FCA0 /* ProfileViewPresenterSpy.swift */; }; + 845BCF382AB50BEA0012FCA0 /* ProfileViewControllerSpy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845BCF372AB50BEA0012FCA0 /* ProfileViewControllerSpy.swift */; }; + 845BCF3A2AB50CED0012FCA0 /* ImagesListViewPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845BCF392AB50CED0012FCA0 /* ImagesListViewPresenter.swift */; }; + 845BCF422AB50DA20012FCA0 /* ImageFeedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845BCF412AB50DA20012FCA0 /* ImageFeedTests.swift */; }; + 845BCF492AB50DE80012FCA0 /* ProfileViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845BCF482AB50DE80012FCA0 /* ProfileViewTests.swift */; }; + 845BCF512AB50E210012FCA0 /* ImageFeedUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845BCF502AB50E210012FCA0 /* ImageFeedUITests.swift */; }; + 845BCF532AB50E210012FCA0 /* ImageFeedUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845BCF522AB50E210012FCA0 /* ImageFeedUITestsLaunchTests.swift */; }; 8463DE2C2A87EF220069C738 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8463DE2B2A87EF220069C738 /* Constants.swift */; }; 8463DE2F2A87FC480069C738 /* AuthViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8463DE2E2A87FC480069C738 /* AuthViewController.swift */; }; 8463DE332A87FF110069C738 /* WebViewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8463DE322A87FF110069C738 /* WebViewViewController.swift */; }; @@ -37,6 +49,23 @@ 84C8B7132A9BBE2700D4626C /* NetworkError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84C8B7122A9BBE2700D4626C /* NetworkError.swift */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + 845BCF432AB50DA20012FCA0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 840780652A65CAB2006461B3 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8407806C2A65CAB2006461B3; + remoteInfo = ImageFeed; + }; + 845BCF542AB50E210012FCA0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 840780652A65CAB2006461B3 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8407806C2A65CAB2006461B3; + remoteInfo = ImageFeed; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXFileReference section */ 840307032A7ADE1300238325 /* ProfileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewController.swift; sourceTree = ""; }; 8407806D2A65CAB2006461B3 /* ImageFeed.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ImageFeed.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -53,6 +82,20 @@ 8447A6F32A9A849F00579B97 /* ProfileService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileService.swift; sourceTree = ""; }; 8447A6F52A9A8C8600579B97 /* ProfileImageService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileImageService.swift; sourceTree = ""; }; 844D31F82A8176E000FEDD16 /* SingleImageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleImageViewController.swift; sourceTree = ""; }; + 845BCF2B2AB50ACA0012FCA0 /* AuthHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthHelper.swift; sourceTree = ""; }; + 845BCF2D2AB50ADB0012FCA0 /* WebViewPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewPresenter.swift; sourceTree = ""; }; + 845BCF2F2AB50AE90012FCA0 /* WebViewPresenterSpy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewPresenterSpy.swift; sourceTree = ""; }; + 845BCF312AB50AF90012FCA0 /* WebViewViewControllerSpy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewViewControllerSpy.swift; sourceTree = ""; }; + 845BCF332AB50BC80012FCA0 /* ProfileViewPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewPresenter.swift; sourceTree = ""; }; + 845BCF352AB50BDC0012FCA0 /* ProfileViewPresenterSpy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewPresenterSpy.swift; sourceTree = ""; }; + 845BCF372AB50BEA0012FCA0 /* ProfileViewControllerSpy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewControllerSpy.swift; sourceTree = ""; }; + 845BCF392AB50CED0012FCA0 /* ImagesListViewPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagesListViewPresenter.swift; sourceTree = ""; }; + 845BCF3F2AB50DA20012FCA0 /* ImageFeedTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ImageFeedTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 845BCF412AB50DA20012FCA0 /* ImageFeedTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageFeedTests.swift; sourceTree = ""; }; + 845BCF482AB50DE80012FCA0 /* ProfileViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewTests.swift; sourceTree = ""; }; + 845BCF4E2AB50E210012FCA0 /* ImageFeedUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ImageFeedUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 845BCF502AB50E210012FCA0 /* ImageFeedUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageFeedUITests.swift; sourceTree = ""; }; + 845BCF522AB50E210012FCA0 /* ImageFeedUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageFeedUITestsLaunchTests.swift; sourceTree = ""; }; 8463DE2B2A87EF220069C738 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; 8463DE2E2A87FC480069C738 /* AuthViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthViewController.swift; sourceTree = ""; }; 8463DE322A87FF110069C738 /* WebViewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = " WebViewViewController.swift"; sourceTree = ""; }; @@ -78,6 +121,20 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 845BCF3C2AB50DA20012FCA0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 845BCF4B2AB50E210012FCA0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -85,6 +142,8 @@ isa = PBXGroup; children = ( 8407806F2A65CAB2006461B3 /* ImageFeed */, + 845BCF402AB50DA20012FCA0 /* ImageFeedTests */, + 845BCF4F2AB50E210012FCA0 /* ImageFeedUITests */, 8407806E2A65CAB2006461B3 /* Products */, ); sourceTree = ""; @@ -93,6 +152,8 @@ isa = PBXGroup; children = ( 8407806D2A65CAB2006461B3 /* ImageFeed.app */, + 845BCF3F2AB50DA20012FCA0 /* ImageFeedTests.xctest */, + 845BCF4E2AB50E210012FCA0 /* ImageFeedUITests.xctest */, ); name = Products; sourceTree = ""; @@ -129,15 +190,38 @@ children = ( 840CA7472A6EF0A8005D49EE /* ImagesListCell.swift */, 840780742A65CAB2006461B3 /* ImagesListViewController.swift */, + 845BCF392AB50CED0012FCA0 /* ImagesListViewPresenter.swift */, ); path = ImagesList; sourceTree = ""; }; + 845BCF402AB50DA20012FCA0 /* ImageFeedTests */ = { + isa = PBXGroup; + children = ( + 845BCF412AB50DA20012FCA0 /* ImageFeedTests.swift */, + 845BCF482AB50DE80012FCA0 /* ProfileViewTests.swift */, + ); + path = ImageFeedTests; + sourceTree = ""; + }; + 845BCF4F2AB50E210012FCA0 /* ImageFeedUITests */ = { + isa = PBXGroup; + children = ( + 845BCF502AB50E210012FCA0 /* ImageFeedUITests.swift */, + 845BCF522AB50E210012FCA0 /* ImageFeedUITestsLaunchTests.swift */, + ); + path = ImageFeedUITests; + sourceTree = ""; + }; 8463DE2D2A87FC350069C738 /* Auth */ = { isa = PBXGroup; children = ( 8463DE2E2A87FC480069C738 /* AuthViewController.swift */, 8463DE322A87FF110069C738 /* WebViewViewController.swift */, + 845BCF2B2AB50ACA0012FCA0 /* AuthHelper.swift */, + 845BCF2D2AB50ADB0012FCA0 /* WebViewPresenter.swift */, + 845BCF2F2AB50AE90012FCA0 /* WebViewPresenterSpy.swift */, + 845BCF312AB50AF90012FCA0 /* WebViewViewControllerSpy.swift */, ); path = Auth; sourceTree = ""; @@ -156,6 +240,9 @@ isa = PBXGroup; children = ( 840307032A7ADE1300238325 /* ProfileViewController.swift */, + 845BCF332AB50BC80012FCA0 /* ProfileViewPresenter.swift */, + 845BCF352AB50BDC0012FCA0 /* ProfileViewPresenterSpy.swift */, + 845BCF372AB50BEA0012FCA0 /* ProfileViewControllerSpy.swift */, ); path = Profile; sourceTree = ""; @@ -219,6 +306,42 @@ productReference = 8407806D2A65CAB2006461B3 /* ImageFeed.app */; productType = "com.apple.product-type.application"; }; + 845BCF3E2AB50DA20012FCA0 /* ImageFeedTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 845BCF452AB50DA20012FCA0 /* Build configuration list for PBXNativeTarget "ImageFeedTests" */; + buildPhases = ( + 845BCF3B2AB50DA20012FCA0 /* Sources */, + 845BCF3C2AB50DA20012FCA0 /* Frameworks */, + 845BCF3D2AB50DA20012FCA0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 845BCF442AB50DA20012FCA0 /* PBXTargetDependency */, + ); + name = ImageFeedTests; + productName = ImageFeedTests; + productReference = 845BCF3F2AB50DA20012FCA0 /* ImageFeedTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 845BCF4D2AB50E210012FCA0 /* ImageFeedUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 845BCF562AB50E210012FCA0 /* Build configuration list for PBXNativeTarget "ImageFeedUITests" */; + buildPhases = ( + 845BCF4A2AB50E210012FCA0 /* Sources */, + 845BCF4B2AB50E210012FCA0 /* Frameworks */, + 845BCF4C2AB50E210012FCA0 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 845BCF552AB50E210012FCA0 /* PBXTargetDependency */, + ); + name = ImageFeedUITests; + productName = ImageFeedUITests; + productReference = 845BCF4E2AB50E210012FCA0 /* ImageFeedUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -232,6 +355,14 @@ 8407806C2A65CAB2006461B3 = { CreatedOnToolsVersion = 14.2; }; + 845BCF3E2AB50DA20012FCA0 = { + CreatedOnToolsVersion = 14.2; + TestTargetID = 8407806C2A65CAB2006461B3; + }; + 845BCF4D2AB50E210012FCA0 = { + CreatedOnToolsVersion = 14.2; + TestTargetID = 8407806C2A65CAB2006461B3; + }; }; }; buildConfigurationList = 840780682A65CAB2006461B3 /* Build configuration list for PBXProject "ImageFeed" */; @@ -253,6 +384,8 @@ projectRoot = ""; targets = ( 8407806C2A65CAB2006461B3 /* ImageFeed */, + 845BCF3E2AB50DA20012FCA0 /* ImageFeedTests */, + 845BCF4D2AB50E210012FCA0 /* ImageFeedUITests */, ); }; /* End PBXProject section */ @@ -268,6 +401,20 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 845BCF3D2AB50DA20012FCA0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 845BCF4C2AB50E210012FCA0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -278,21 +425,29 @@ 8474924D2A9183EE00CA01AC /* OAuth2TokenStorage.swift in Sources */, 840780752A65CAB2006461B3 /* ImagesListViewController.swift in Sources */, 8463DE332A87FF110069C738 /* WebViewViewController.swift in Sources */, + 845BCF3A2AB50CED0012FCA0 /* ImagesListViewPresenter.swift in Sources */, 84C8B7112A9BB98800D4626C /* TabBarController.swift in Sources */, + 845BCF362AB50BDC0012FCA0 /* ProfileViewPresenterSpy.swift in Sources */, 840CA7482A6EF0A8005D49EE /* ImagesListCell.swift in Sources */, 8491BCF02AA4E4DB00BC2E95 /* ImageListService.swift in Sources */, 840307042A7ADE1300238325 /* ProfileViewController.swift in Sources */, 84C8B7092A9BA84A00D4626C /* URLRequestExtension.swift in Sources */, 8491BCF22AA4E72300BC2E95 /* ImageListServiceModel.swift in Sources */, 8474924B2A9183DE00CA01AC /* OAuth2Service.swift in Sources */, + 845BCF342AB50BC80012FCA0 /* ProfileViewPresenter.swift in Sources */, + 845BCF2E2AB50ADB0012FCA0 /* WebViewPresenter.swift in Sources */, + 845BCF382AB50BEA0012FCA0 /* ProfileViewControllerSpy.swift in Sources */, 8447A6F02A9A78B200579B97 /* UIBlockingProgressHUD.swift in Sources */, 840780712A65CAB2006461B3 /* AppDelegate.swift in Sources */, + 845BCF322AB50AF90012FCA0 /* WebViewViewControllerSpy.swift in Sources */, 84C8B7072A9BA80400D4626C /* URLSessionExtension.swift in Sources */, 8463DE2F2A87FC480069C738 /* AuthViewController.swift in Sources */, 8463DE2C2A87EF220069C738 /* Constants.swift in Sources */, 844D31F92A8176E000FEDD16 /* SingleImageViewController.swift in Sources */, 840780732A65CAB2006461B3 /* SceneDelegate.swift in Sources */, 8447A6F62A9A8C8600579B97 /* ProfileImageService.swift in Sources */, + 845BCF2C2AB50ACA0012FCA0 /* AuthHelper.swift in Sources */, + 845BCF302AB50AE90012FCA0 /* WebViewPresenterSpy.swift in Sources */, 8493F5A22A8EA2820018FDA6 /* SplashViewController.swift in Sources */, 8447A6F42A9A849F00579B97 /* ProfileService.swift in Sources */, 84C8B7132A9BBE2700D4626C /* NetworkError.swift in Sources */, @@ -300,8 +455,39 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 845BCF3B2AB50DA20012FCA0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 845BCF492AB50DE80012FCA0 /* ProfileViewTests.swift in Sources */, + 845BCF422AB50DA20012FCA0 /* ImageFeedTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 845BCF4A2AB50E210012FCA0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 845BCF532AB50E210012FCA0 /* ImageFeedUITestsLaunchTests.swift in Sources */, + 845BCF512AB50E210012FCA0 /* ImageFeedUITests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 845BCF442AB50DA20012FCA0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8407806C2A65CAB2006461B3 /* ImageFeed */; + targetProxy = 845BCF432AB50DA20012FCA0 /* PBXContainerItemProxy */; + }; + 845BCF552AB50E210012FCA0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8407806C2A65CAB2006461B3 /* ImageFeed */; + targetProxy = 845BCF542AB50E210012FCA0 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin PBXVariantGroup section */ 840780762A65CAB2006461B3 /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -500,6 +686,76 @@ }; name = Release; }; + 845BCF462AB50DA20012FCA0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = HG22Y54574; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = ru.yandex.practicum.ImageFeedTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ImageFeed.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/ImageFeed"; + }; + name = Debug; + }; + 845BCF472AB50DA20012FCA0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = HG22Y54574; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = ru.yandex.practicum.ImageFeedTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ImageFeed.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/ImageFeed"; + }; + name = Release; + }; + 845BCF572AB50E210012FCA0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = HG22Y54574; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = ru.yandex.practicum.ImageFeedUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = ImageFeed; + }; + name = Debug; + }; + 845BCF582AB50E210012FCA0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = HG22Y54574; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = ru.yandex.practicum.ImageFeedUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = ImageFeed; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -521,6 +777,24 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 845BCF452AB50DA20012FCA0 /* Build configuration list for PBXNativeTarget "ImageFeedTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 845BCF462AB50DA20012FCA0 /* Debug */, + 845BCF472AB50DA20012FCA0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 845BCF562AB50E210012FCA0 /* Build configuration list for PBXNativeTarget "ImageFeedUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 845BCF572AB50E210012FCA0 /* Debug */, + 845BCF582AB50E210012FCA0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ diff --git a/ImageFeed/AppDelegate.swift b/ImageFeed/AppDelegate.swift index ca66666..7b9d6b5 100644 --- a/ImageFeed/AppDelegate.swift +++ b/ImageFeed/AppDelegate.swift @@ -6,13 +6,11 @@ // import UIKit -import CoreData @main class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - // Override point for customization after application launch. return true } @@ -21,60 +19,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { let sceneConfiguration = UISceneConfiguration(name: "Main", sessionRole: connectingSceneSession.role) sceneConfiguration.delegateClass = SceneDelegate.self - // Called when a new scene session is being created. - // Use this method to select a configuration to create the new scene with. return sceneConfiguration } func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { - // Called when the user discards a scene session. - // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. - // Use this method to release any resources that were specific to the discarded scenes, as they will not return. - } - - // MARK: - Core Data stack - - lazy var persistentContainer: NSPersistentContainer = { - /* - The persistent container for the application. This implementation - creates and returns a container, having loaded the store for the - application to it. This property is optional since there are legitimate - error conditions that could cause the creation of the store to fail. - */ - let container = NSPersistentContainer(name: "ImageFeed") - container.loadPersistentStores(completionHandler: { (storeDescription, error) in - if let error = error as NSError? { - // Replace this implementation with code to handle the error appropriately. - // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. - - /* - Typical reasons for an error here include: - * The parent directory does not exist, cannot be created, or disallows writing. - * The persistent store is not accessible, due to permissions or data protection when the device is locked. - * The device is out of space. - * The store could not be migrated to the current model version. - Check the error message to determine what the actual problem was. - */ - fatalError("Unresolved error \(error), \(error.userInfo)") - } - }) - return container - }() - - // MARK: - Core Data Saving support - - func saveContext () { - let context = persistentContainer.viewContext - if context.hasChanges { - do { - try context.save() - } catch { - // Replace this implementation with code to handle the error appropriately. - // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. - let nserror = error as NSError - fatalError("Unresolved error \(nserror), \(nserror.userInfo)") - } - } } } - diff --git a/ImageFeed/Auth/ WebViewViewController.swift b/ImageFeed/Auth/ WebViewViewController.swift index 9dcb789..72778e4 100644 --- a/ImageFeed/Auth/ WebViewViewController.swift +++ b/ImageFeed/Auth/ WebViewViewController.swift @@ -8,10 +8,11 @@ import UIKit import WebKit -private struct APIConstants { - static let authorizeURLString = "https://unsplash.com/oauth/authorize" - static let code = "code" - static let authorizathionPath = "/oauth/authorize/native" +public protocol WebViewViewControllerProtocol: AnyObject { + var presenter: WebViewPresenterProtocol? { get set } + func load(request: URLRequest) + func setProgressValue(_ newValue: Float) + func setProgressHidden(_ isHidden: Bool) } protocol WebViewViewControllerDelegate: AnyObject { @@ -19,76 +20,66 @@ protocol WebViewViewControllerDelegate: AnyObject { func webViewViewControllerDidCancel(_ vc:WebViewViewController) } -final class WebViewViewController: UIViewController { +final class WebViewViewController: UIViewController, WebViewViewControllerProtocol { @IBOutlet private var progressView: UIProgressView! @IBOutlet private var webView: WKWebView! - weak var delegate: WebViewViewControllerDelegate? + var presenter: WebViewPresenterProtocol? private var estimatedProgressObservation: NSKeyValueObservation? + weak var delegate: WebViewViewControllerDelegate? + override func viewDidLoad() { super.viewDidLoad() webView.navigationDelegate = self - - loadWebView() + presenter?.viewDidLoad() + webView.accessibilityIdentifier = "UnsplashWebView" estimatedProgressObservation = webView.observe( \.estimatedProgress, options: [], changeHandler: { [weak self] _, _ in guard let self = self else { return } - self.updateProgress() + self.presenter?.didUpdateProgressValue(self.webView.estimatedProgress) }) } - @IBAction private func didTapBackButton(_ sender: Any) { + @IBAction private func didTapBackButton(_ sender: Any?) { delegate?.webViewViewControllerDidCancel(self) print("Press button") } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - updateProgress() } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) } - private func updateProgress() { - progressView.progress = Float(webView.estimatedProgress) - progressView.isHidden = fabs(webView.estimatedProgress - 1.0) <= 0.0001 + func load(request: URLRequest) { + webView.load(request) + } + + func setProgressValue(_ newValue: Float) { + progressView.progress = newValue + } + + func setProgressHidden(_ isHidden: Bool) { + progressView.isHidden = isHidden } } private extension WebViewViewController { - func loadWebView() { - var urlComponents = URLComponents(string: APIConstants.authorizeURLString) - urlComponents?.queryItems = [ - URLQueryItem(name: "client_id", value: AccessKey), - URLQueryItem(name: "redirect_uri", value: RedirectURI), - URLQueryItem(name: "response_type", value: "code"), - URLQueryItem(name: "scope", value: AccessScope) - ] - guard let url = urlComponents?.url else { return } - let request = URLRequest(url: url) - webView.load(request) - updateProgress() - } func fetchCode(from navigationAction: WKNavigationAction) -> String? { - if let url = navigationAction.request.url, - let components = URLComponents(string: url.absoluteString), - components.path == APIConstants.authorizathionPath, - let items = components.queryItems, - let codeItem = items.first(where: { $0.name == APIConstants.code }) { - return codeItem.value - } else { - return nil + if let url = navigationAction.request.url { + return presenter?.code(from: url) } + return nil } } diff --git a/ImageFeed/Auth/AuthHelper.swift b/ImageFeed/Auth/AuthHelper.swift new file mode 100644 index 0000000..b6e0135 --- /dev/null +++ b/ImageFeed/Auth/AuthHelper.swift @@ -0,0 +1,48 @@ +// +// AuthHelper.swift +// ImageFeed +// +// Created by admin on 16.09.2023. +// + +import Foundation + +protocol AuthHelperProtocol { + func authRequest() -> URLRequest + func code(from url: URL) -> String? +} + +class AuthHelper: AuthHelperProtocol { + let configuration: AuthConfiguration + + init(configuration: AuthConfiguration = .standart) { + self.configuration = configuration + } + + func authRequest() -> URLRequest { + let url = authURL() + return URLRequest(url: url) + } + + func authURL() -> URL { + var urlComponents = URLComponents(string: configuration.authURLString)! + urlComponents.queryItems = [ + URLQueryItem(name: "client_id", value: configuration.accessKey), + URLQueryItem(name: "redirect_uri", value: configuration.redirectURI), + URLQueryItem(name: "response_type", value: "code"), + URLQueryItem(name: "scope", value: configuration.accessScope) + ] + return urlComponents.url! + } + + func code(from url: URL) -> String? { + if let components = URLComponents(string: url.absoluteString), + components.path == "/oauth/authorize/native", + let items = components.queryItems, + let codeItem = items.first(where: { $0.name == "code" }) { + return codeItem.value + } else { + return nil + } + } +} diff --git a/ImageFeed/Auth/AuthViewController.swift b/ImageFeed/Auth/AuthViewController.swift index 5a3a0bf..a32e519 100644 --- a/ImageFeed/Auth/AuthViewController.swift +++ b/ImageFeed/Auth/AuthViewController.swift @@ -21,6 +21,10 @@ final class AuthViewController: UIViewController { if segue.identifier == showWebViewSegueIdentifier { guard let webViewViewController = segue.destination as? WebViewViewController else { fatalError("Failed to prepare for \(showWebViewSegueIdentifier)") } + let authHelper = AuthHelper() + let webViewPresenter = WebViewPresenter(authHelper: authHelper) + webViewViewController.presenter = webViewPresenter + webViewPresenter.view = webViewViewController webViewViewController.delegate = self } else { super.prepare(for: segue, sender: sender) @@ -38,3 +42,4 @@ extension AuthViewController: WebViewViewControllerDelegate { } } + diff --git a/ImageFeed/Auth/WebViewPresenter.swift b/ImageFeed/Auth/WebViewPresenter.swift new file mode 100644 index 0000000..35496af --- /dev/null +++ b/ImageFeed/Auth/WebViewPresenter.swift @@ -0,0 +1,47 @@ +// +// WebViewPresenter.swift +// ImageFeed +// +// Created by admin on 16.09.2023. +// + +import Foundation + +public protocol WebViewPresenterProtocol { + var view: WebViewViewControllerProtocol? { get set } + func viewDidLoad() + func didUpdateProgressValue(_ newValue: Double) + func code(from url: URL) -> String? +} + +final class WebViewPresenter: WebViewPresenterProtocol { + + weak var view: WebViewViewControllerProtocol? + var authHelper: AuthHelperProtocol + + init(authHelper: AuthHelperProtocol) { + self.authHelper = authHelper + } + + func viewDidLoad() { + let request = authHelper.authRequest() + view?.load(request: request) + didUpdateProgressValue(0) + } + + func code(from url: URL) -> String? { + authHelper.code(from: url) + } + + func didUpdateProgressValue(_ newValue: Double) { + let newProgressValue = Float(newValue) + view?.setProgressValue(newProgressValue) + + let shouldHideProgress = shouldHideProgress(for: newProgressValue) + view?.setProgressHidden(shouldHideProgress) + } + + func shouldHideProgress(for value: Float) -> Bool { + abs(value - 1.0) <= 0.0001 + } +} diff --git a/ImageFeed/Auth/WebViewPresenterSpy.swift b/ImageFeed/Auth/WebViewPresenterSpy.swift new file mode 100644 index 0000000..2a1f733 --- /dev/null +++ b/ImageFeed/Auth/WebViewPresenterSpy.swift @@ -0,0 +1,26 @@ +// +// WebViewPresenterSpy.swift +// ImageFeed +// +// Created by admin on 16.09.2023. +// + +import ImageFeed +import Foundation + +final class WebViewPresenterSpy: WebViewPresenterProtocol { + var viewDidLoadCalled: Bool = false + var view: WebViewViewControllerProtocol? + + func viewDidLoad() { + viewDidLoadCalled = true + } + + func didUpdateProgressValue(_ newValue: Double) { + + } + + func code(from url: URL) -> String? { + return nil + } +} diff --git a/ImageFeed/Auth/WebViewViewControllerSpy.swift b/ImageFeed/Auth/WebViewViewControllerSpy.swift new file mode 100644 index 0000000..edc4b3a --- /dev/null +++ b/ImageFeed/Auth/WebViewViewControllerSpy.swift @@ -0,0 +1,25 @@ +// +// WebViewViewControllerSpy.swift +// ImageFeed +// +// Created by admin on 16.09.2023. +// + +import ImageFeed +import Foundation +import UIKit + +final class WebViewViewControllerSpy: UIViewController, WebViewViewControllerProtocol { + var loadRequestCalled: Bool = false + var presenter: ImageFeed.WebViewPresenterProtocol? + + func load(request: URLRequest) { + loadRequestCalled = true + } + + func setProgressValue(_ newValue: Float) { + } + + func setProgressHidden(_ isHidden: Bool) { + } +} diff --git a/ImageFeed/Base.lproj/Main.storyboard b/ImageFeed/Base.lproj/Main.storyboard index 76491b2..f522718 100644 --- a/ImageFeed/Base.lproj/Main.storyboard +++ b/ImageFeed/Base.lproj/Main.storyboard @@ -50,7 +50,7 @@ - + @@ -62,6 +62,9 @@ + + +