Repository: dima-code/T-bank-Course
Pull request #7: Homework 8
Author: login: dima-code
Created: 2025-04-16T02:45:43Z | Merged: 2025-04-23T20:45:29Z



Diff:
diff --git a/T-bank_HW7/T-bank_HW7.xcodeproj/project.xcworkspace/xcuserdata/dmitriieselidze.xcuserdatad/UserInterfaceState.xcuserstate b/T-bank_HW7/T-bank_HW7.xcodeproj/project.xcworkspace/xcuserdata/dmitriieselidze.xcuserdatad/UserInterfaceState.xcuserstate
index 6855bdc..e4f24eb 100644
Binary files a/T-bank_HW7/T-bank_HW7.xcodeproj/project.xcworkspace/xcuserdata/dmitriieselidze.xcuserdatad/UserInterfaceState.xcuserstate and b/T-bank_HW7/T-bank_HW7.xcodeproj/project.xcworkspace/xcuserdata/dmitriieselidze.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/T-bank_HW8/T-bank_HW8.xcodeproj/project.pbxproj b/T-bank_HW8/T-bank_HW8.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..300b39e
--- /dev/null
+++ b/T-bank_HW8/T-bank_HW8.xcodeproj/project.pbxproj
@@ -0,0 +1,340 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 77;
+	objects = {
+
+/* Begin PBXFileReference section */
+		B1E0C52A2DA97E6900771792 /* T-bank_HW8.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "T-bank_HW8.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+/* End PBXFileReference section */
+
+/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
+		B1E0C53C2DA97E6B00771792 /* Exceptions for "T-bank_HW8" folder in "T-bank_HW8" target */ = {
+			isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
+			membershipExceptions = (
+				Info.plist,
+			);
+			target = B1E0C5292DA97E6900771792 /* T-bank_HW8 */;
+		};
+/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
+
+/* Begin PBXFileSystemSynchronizedRootGroup section */
+		B1E0C52C2DA97E6900771792 /* T-bank_HW8 */ = {
+			isa = PBXFileSystemSynchronizedRootGroup;
+			exceptions = (
+				B1E0C53C2DA97E6B00771792 /* Exceptions for "T-bank_HW8" folder in "T-bank_HW8" target */,
+			);
+			path = "T-bank_HW8";
+			sourceTree = "<group>";
+		};
+/* End PBXFileSystemSynchronizedRootGroup section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		B1E0C5272DA97E6900771792 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		B1E0C5212DA97E6900771792 = {
+			isa = PBXGroup;
+			children = (
+				B1E0C52C2DA97E6900771792 /* T-bank_HW8 */,
+				B1E0C52B2DA97E6900771792 /* Products */,
+			);
+			sourceTree = "<group>";
+		};
+		B1E0C52B2DA97E6900771792 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				B1E0C52A2DA97E6900771792 /* T-bank_HW8.app */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		B1E0C5292DA97E6900771792 /* T-bank_HW8 */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = B1E0C53D2DA97E6B00771792 /* Build configuration list for PBXNativeTarget "T-bank_HW8" */;
+			buildPhases = (
+				B1E0C5262DA97E6900771792 /* Sources */,
+				B1E0C5272DA97E6900771792 /* Frameworks */,
+				B1E0C5282DA97E6900771792 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			fileSystemSynchronizedGroups = (
+				B1E0C52C2DA97E6900771792 /* T-bank_HW8 */,
+			);
+			name = "T-bank_HW8";
+			packageProductDependencies = (
+			);
+			productName = "T-bank_HW8";
+			productReference = B1E0C52A2DA97E6900771792 /* T-bank_HW8.app */;
+			productType = "com.apple.product-type.application";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		B1E0C5222DA97E6900771792 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				BuildIndependentTargetsInParallel = 1;
+				LastSwiftUpdateCheck = 1610;
+				LastUpgradeCheck = 1610;
+				TargetAttributes = {
+					B1E0C5292DA97E6900771792 = {
+						CreatedOnToolsVersion = 16.1;
+					};
+				};
+			};
+			buildConfigurationList = B1E0C5252DA97E6900771792 /* Build configuration list for PBXProject "T-bank_HW8" */;
+			developmentRegion = en;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+			);
+			mainGroup = B1E0C5212DA97E6900771792;
+			minimizedProjectReferenceProxies = 1;
+			preferredProjectObjectVersion = 77;
+			productRefGroup = B1E0C52B2DA97E6900771792 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				B1E0C5292DA97E6900771792 /* T-bank_HW8 */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		B1E0C5282DA97E6900771792 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		B1E0C5262DA97E6900771792 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+		B1E0C53E2DA97E6B00771792 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+				CODE_SIGN_STYLE = Automatic;
+				CURRENT_PROJECT_VERSION = 1;
+				DEVELOPMENT_TEAM = Q4DXR3DR4Y;
+				GENERATE_INFOPLIST_FILE = YES;
+				INFOPLIST_FILE = "T-bank_HW8/Info.plist";
+				INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
+				INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
+				INFOPLIST_KEY_UIMainStoryboardFile = Main;
+				INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+				INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
+				MARKETING_VERSION = 1.0;
+				PRODUCT_BUNDLE_IDENTIFIER = "Eselidze.T-bank-HW8";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_EMIT_LOC_STRINGS = YES;
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Debug;
+		};
+		B1E0C53F2DA97E6B00771792 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+				CODE_SIGN_STYLE = Automatic;
+				CURRENT_PROJECT_VERSION = 1;
+				DEVELOPMENT_TEAM = Q4DXR3DR4Y;
+				GENERATE_INFOPLIST_FILE = YES;
+				INFOPLIST_FILE = "T-bank_HW8/Info.plist";
+				INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
+				INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
+				INFOPLIST_KEY_UIMainStoryboardFile = Main;
+				INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+				INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
+				MARKETING_VERSION = 1.0;
+				PRODUCT_BUNDLE_IDENTIFIER = "Eselidze.T-bank-HW8";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_EMIT_LOC_STRINGS = YES;
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Release;
+		};
+		B1E0C5402DA97E6B00771792 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				ENABLE_USER_SCRIPT_SANDBOXING = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu17;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 18.1;
+				LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+				MTL_FAST_MATH = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = iphoneos;
+				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+			};
+			name = Debug;
+		};
+		B1E0C5412DA97E6B00771792 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_USER_SCRIPT_SANDBOXING = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu17;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 18.1;
+				LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				MTL_FAST_MATH = YES;
+				SDKROOT = iphoneos;
+				SWIFT_COMPILATION_MODE = wholemodule;
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		B1E0C5252DA97E6900771792 /* Build configuration list for PBXProject "T-bank_HW8" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				B1E0C5402DA97E6B00771792 /* Debug */,
+				B1E0C5412DA97E6B00771792 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		B1E0C53D2DA97E6B00771792 /* Build configuration list for PBXNativeTarget "T-bank_HW8" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				B1E0C53E2DA97E6B00771792 /* Debug */,
+				B1E0C53F2DA97E6B00771792 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = B1E0C5222DA97E6900771792 /* Project object */;
+}
diff --git a/T-bank_HW8/T-bank_HW8.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/T-bank_HW8/T-bank_HW8.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/T-bank_HW8/T-bank_HW8.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:">
+   </FileRef>
+</Workspace>
diff --git a/T-bank_HW8/T-bank_HW8.xcodeproj/project.xcworkspace/xcuserdata/dmitriieselidze.xcuserdatad/UserInterfaceState.xcuserstate b/T-bank_HW8/T-bank_HW8.xcodeproj/project.xcworkspace/xcuserdata/dmitriieselidze.xcuserdatad/UserInterfaceState.xcuserstate
new file mode 100644
index 0000000..842289b
Binary files /dev/null and b/T-bank_HW8/T-bank_HW8.xcodeproj/project.xcworkspace/xcuserdata/dmitriieselidze.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/T-bank_HW8/T-bank_HW8.xcodeproj/xcuserdata/dmitriieselidze.xcuserdatad/xcschemes/xcschememanagement.plist b/T-bank_HW8/T-bank_HW8.xcodeproj/xcuserdata/dmitriieselidze.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 0000000..3f6a006
--- /dev/null
+++ b/T-bank_HW8/T-bank_HW8.xcodeproj/xcuserdata/dmitriieselidze.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>SchemeUserState</key>
+	<dict>
+		<key>T-bank_HW8.xcscheme_^#shared#^_</key>
+		<dict>
+			<key>orderHint</key>
+			<integer>0</integer>
+		</dict>
+	</dict>
+</dict>
+</plist>
diff --git a/T-bank_HW8/T-bank_HW8/AppDelegate.swift b/T-bank_HW8/T-bank_HW8/AppDelegate.swift
new file mode 100644
index 0000000..3ccf373
--- /dev/null
+++ b/T-bank_HW8/T-bank_HW8/AppDelegate.swift
@@ -0,0 +1,36 @@
+//
+//  AppDelegate.swift
+//  T-bank_HW8
+//
+//  Created by Dmitrii Eselidze on 11.04.2025.
+//
+
+import UIKit
+
+@main
+class AppDelegate: UIResponder, UIApplicationDelegate {
+
+
+
+    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
+        // Override point for customization after application launch.
+        return true
+    }
+
+    // MARK: UISceneSession Lifecycle
+
+    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
+        // Called when a new scene session is being created.
+        // Use this method to select a configuration to create the new scene with.
+        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
+    }
+
+    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
+        // Called when the user discards a scene session.
+        // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
+        // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
+    }
+
+
+}
+
diff --git a/T-bank_HW8/T-bank_HW8/Assets.xcassets/AccentColor.colorset/Contents.json b/T-bank_HW8/T-bank_HW8/Assets.xcassets/AccentColor.colorset/Contents.json
new file mode 100644
index 0000000..eb87897
--- /dev/null
+++ b/T-bank_HW8/T-bank_HW8/Assets.xcassets/AccentColor.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+  "colors" : [
+    {
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/T-bank_HW8/T-bank_HW8/Assets.xcassets/AppIcon.appiconset/Contents.json b/T-bank_HW8/T-bank_HW8/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..2305880
--- /dev/null
+++ b/T-bank_HW8/T-bank_HW8/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,35 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "platform" : "ios",
+      "size" : "1024x1024"
+    },
+    {
+      "appearances" : [
+        {
+          "appearance" : "luminosity",
+          "value" : "dark"
+        }
+      ],
+      "idiom" : "universal",
+      "platform" : "ios",
+      "size" : "1024x1024"
+    },
+    {
+      "appearances" : [
+        {
+          "appearance" : "luminosity",
+          "value" : "tinted"
+        }
+      ],
+      "idiom" : "universal",
+      "platform" : "ios",
+      "size" : "1024x1024"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/T-bank_HW8/T-bank_HW8/Assets.xcassets/Contents.json b/T-bank_HW8/T-bank_HW8/Assets.xcassets/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/T-bank_HW8/T-bank_HW8/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/T-bank_HW8/T-bank_HW8/Base.lproj/LaunchScreen.storyboard b/T-bank_HW8/T-bank_HW8/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..865e932
--- /dev/null
+++ b/T-bank_HW8/T-bank_HW8/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="EHf-IW-A2E">
+            <objects>
+                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
+                        <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="53" y="375"/>
+        </scene>
+    </scenes>
+</document>
diff --git a/T-bank_HW8/T-bank_HW8/Base.lproj/Main.storyboard b/T-bank_HW8/T-bank_HW8/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..25a7638
--- /dev/null
+++ b/T-bank_HW8/T-bank_HW8/Base.lproj/Main.storyboard
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="tne-QT-ifu">
+            <objects>
+                <viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
+                        <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
+            </objects>
+        </scene>
+    </scenes>
+</document>
diff --git a/T-bank_HW8/T-bank_HW8/Info.plist b/T-bank_HW8/T-bank_HW8/Info.plist
new file mode 100644
index 0000000..dd3c9af
--- /dev/null
+++ b/T-bank_HW8/T-bank_HW8/Info.plist
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>UIApplicationSceneManifest</key>
+	<dict>
+		<key>UIApplicationSupportsMultipleScenes</key>
+		<false/>
+		<key>UISceneConfigurations</key>
+		<dict>
+			<key>UIWindowSceneSessionRoleApplication</key>
+			<array>
+				<dict>
+					<key>UISceneConfigurationName</key>
+					<string>Default Configuration</string>
+					<key>UISceneDelegateClassName</key>
+					<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
+					<key>UISceneStoryboardFile</key>
+					<string>Main</string>
+				</dict>
+			</array>
+		</dict>
+	</dict>
+</dict>
+</plist>
diff --git a/T-bank_HW8/T-bank_HW8/MainSceneAssembler.swift b/T-bank_HW8/T-bank_HW8/MainSceneAssembler.swift
new file mode 100644
index 0000000..eb09e10
--- /dev/null
+++ b/T-bank_HW8/T-bank_HW8/MainSceneAssembler.swift
@@ -0,0 +1,19 @@
+//
+//  MainSceneAssembler.swift
+//  T-bank_HW8
+//
+//  Created by Dmitrii Eselidze on 16.04.2025.
+//
+
+import UIKit
+
+final class MainSceneAssembler {
+    func makeScene() -> UIViewController {
+        let networkClient = NetworkClient()
+        let presenter = MainPresenter(networkClient: networkClient)
+        let view = MainViewController (presenter: presenter)
+        presenter.view = view
+        
+        return view
+    }
+}
diff --git a/T-bank_HW8/T-bank_HW8/Model/NetworkClient.swift b/T-bank_HW8/T-bank_HW8/Model/NetworkClient.swift
new file mode 100644
index 0000000..83c74a3
--- /dev/null
+++ b/T-bank_HW8/T-bank_HW8/Model/NetworkClient.swift
@@ -0,0 +1,136 @@
+//
+//  NetworkClient.swift
+//  T-bank_HW8
+//
+//  Created by Dmitrii Eselidze on 15.04.2025.
+//
+
+
+import Foundation
+
+final class NetworkClient: NSObject {
+    private var progressBlocks: [Int: (Double) -> Void] = [:]
+    private var competionBlocks: [Int: (Result<Data, NetworkClientError>) -> Void] = [:]
+    
+    private lazy var urlSession: URLSession = {
+        let configuration = URLSessionConfiguration.default
+        return URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
+    }()
+}
+
+extension NetworkClient: NetworkClientInput {
+    func get<ResponseSchema: Decodable>(
+        urlString: String,
+        completion: @escaping (Result<ResponseSchema, NetworkClientError>) -> Void
+    ) {
+        guard let url = URL(string: urlString) else {
+            completion(.failure(.invalidURL))
+            return
+        }
+        let task = urlSession.dataTask(with: url) { data, response, error in
+            if let data = data {
+                do {
+                    let result = try JSONDecoder().decode(ResponseSchema.self, from: data)
+                    completion(.success(result))
+                } catch {
+                    completion(.failure(.decodingError(error)))
+                }
+            } else if let error = error {
+                completion(.failure(.networkError(error)))
+            }
+        }
+        task.resume()
+    }
+    func post<ResponseSchema: Decodable>(
+        urlString: String,
+        requestBody: Encodable,
+        completion: @escaping (Result<ResponseSchema, NetworkClientError>) -> Void
+    ) {
+        guard let url = URL(string: urlString) else {
+            completion(.failure(.invalidURL))
+            return
+        }
+        
+        var request = URLRequest(url: url)
+        request.httpMethod = "POST"
+        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
+        do {
+            let jsonData = try JSONEncoder().encode(requestBody)
+            request.httpBody = jsonData
+        } catch {
+            completion(.failure(.encodingError(error)))
+        }
+        
+        let task = urlSession.dataTask(with: request) { data, response, error in
+            if let data = data {
+                do {
+                    let result = try JSONDecoder().decode(ResponseSchema.self, from: data)
+                    completion(.success(result))
+                } catch {
+                    completion(.failure(.decodingError(error)))
+                }
+            } else if let error = error {
+                completion(.failure(.networkError(error)))
+            }
+        }
+        task.resume()
+        
+    }
+    
+    func download(
+        urlString: String,
+        progressBlock: ((Double) -> Void)?,
+        completion: @escaping (Result<Data, NetworkClientError>) -> Void) {
+            guard let url = URL(string: urlString) else {
+                completion(.failure(.invalidURL))
+                return
+            }
+            var request = URLRequest(url: url)
+            request.httpMethod = "GET"
+            
+            let task = urlSession.downloadTask(with: request)
+            progressBlocks[task.taskIdentifier] = progressBlock
+            competionBlocks[task.taskIdentifier] = completion
+            task.resume ( )
+        }
+}
+
+extension NetworkClient: URLSessionTaskDelegate {
+    func urlSession(
+        _ session: URLSession,
+        task: URLSessionTask,
+        didCompleteWithError error: Error?
+    ) {
+        if let error = error {
+            competionBlocks[task.taskIdentifier]?(.failure(.networkError(error)))
+            
+            competionBlocks.removeValue(forKey: task.taskIdentifier)
+            progressBlocks.removeValue(forKey: task.taskIdentifier)
+        }
+    }
+}
+
+extension NetworkClient: URLSessionDownloadDelegate {
+    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
+        if let completion = competionBlocks[downloadTask.taskIdentifier],
+           let data = try? Data (contentsOf: location) {
+            completion(.success(data))
+        }
+        competionBlocks.removeValue (forKey: downloadTask.taskIdentifier)
+        progressBlocks.removeValue(forKey: downloadTask.taskIdentifier)
+    }
+    
+    func urlSession(
+        _ session: URLSession,
+        downloadTask: URLSessionDownloadTask,
+        didWriteData bytesWritten: Int64,
+        totalBytesWritten: Int64,
+        totalBytesExpectedToWrite: Int64
+    )
+    {
+        if totalBytesExpectedToWrite > 0 {
+            let progress = Double(totalBytesWritten) / Double(totalBytesExpectedToWrite)
+            progressBlocks[downloadTask.taskIdentifier]?(progress)
+        }
+    }
+}
diff --git a/T-bank_HW8/T-bank_HW8/Model/NetworkClientError.swift b/T-bank_HW8/T-bank_HW8/Model/NetworkClientError.swift
new file mode 100644
index 0000000..33dd4c1
--- /dev/null
+++ b/T-bank_HW8/T-bank_HW8/Model/NetworkClientError.swift
@@ -0,0 +1,21 @@
+//
+//  NetworkClientError.swift
+//  T-bank_HW8
+//
+//  Created by Dmitrii Eselidze on 15.04.2025.
+//
+
+import Foundation
+
+enum NetworkClientError: Error {
+/// Неверный URL
+    case invalidURL
+/// Ошибка сериализации данных
+case encodingError(Error)
+/// Ошибка сети, обычно из "NSURLErrorDomain
+case networkError(Error)
+/// Ошибка HTTP (status code 4xx, 5xx)
+case httpError(Int)
+/// Ошибка декодирования
+case decodingError(Error)
+}
diff --git a/T-bank_HW8/T-bank_HW8/Model/NetworkClientInput.swift b/T-bank_HW8/T-bank_HW8/Model/NetworkClientInput.swift
new file mode 100644
index 0000000..cfcd57f
--- /dev/null
+++ b/T-bank_HW8/T-bank_HW8/Model/NetworkClientInput.swift
@@ -0,0 +1,26 @@
+//
+//  NetworkClientInput.swift
+//  T-bank_HW8
+//
+//  Created by Dmitrii Eselidze on 15.04.2025.
+//
+
+import Foundation
+
+protocol NetworkClientInput: AnyObject {
+    func get <ResponseSchema: Decodable>(
+        urlString: String,
+        completion: @escaping (Result<ResponseSchema, NetworkClientError>) -> Void
+    )
+    
+    func post<ResponseSchema: Decodable>(
+        urlString: String,
+        requestBody: Encodable,
+        completion: @escaping (Result<ResponseSchema, NetworkClientError>) -> Void
+    )
+    
+    func download(
+        urlString: String, progressBlock: ((Double) -> Void)?,
+        completion: @escaping (Result<Data, NetworkClientError>) -> Void
+        )
+}
diff --git a/T-bank_HW8/T-bank_HW8/Presenter/Item.swift b/T-bank_HW8/T-bank_HW8/Presenter/Item.swift
new file mode 100644
index 0000000..2eaca9e
--- /dev/null
+++ b/T-bank_HW8/T-bank_HW8/Presenter/Item.swift
@@ -0,0 +1,11 @@
+//
+//  Post.swift
+//  T-bank_HW8
+//
+//  Created by Dmitrii Eselidze on 16.04.2025.
+//
+
+struct Item: Codable {
+    let title: String
+    let image: String
+}
diff --git a/T-bank_HW8/T-bank_HW8/Presenter/MainPresenter.swift b/T-bank_HW8/T-bank_HW8/Presenter/MainPresenter.swift
new file mode 100644
index 0000000..4bfd5d1
--- /dev/null
+++ b/T-bank_HW8/T-bank_HW8/Presenter/MainPresenter.swift
@@ -0,0 +1,51 @@
+//
+//  Presenter.swift
+//  T-bank_HW8
+//
+//  Created by Dmitrii Eselidze on 14.04.2025.
+//
+
+import Foundation
+import UIKit
+
+final class MainPresenter {
+    
+    weak var view: MainViewInput?
+    private let networkClient: NetworkClientInput
+    private var items: [Item] = []
+    
+    init(networkClient: NetworkClientInput) {
+        self.networkClient = networkClient
+    }
+    
+    private func loadItems() {
+        let url = "https://fakestoreapi.com/products"
+        let completion: ((Result<[Item], NetworkClientError>) -> Void) = { result in
+            switch result {
+                case .success(let posts):
+                    self.items = posts
+                    DispatchQueue.main.async {
+                        self.view?.updateTable()
+                        self.view?.stopActivityIndicator()
+                    }
+                case .failure (let error):
+                    print (error.localizedDescription)
+            }
+        }
+        networkClient.get(urlString: url, completion: completion)
+    }
+}
+
+
+extension MainPresenter: MainPresenterInput {
+    var itemsCount: Int {
+        items.count
+    }
+    func viewDidLoad() {
+        loadItems()
+    }
+    
+    func item(at index: Int) -> Item {
+        items[index]
+    }
+}
diff --git a/T-bank_HW8/T-bank_HW8/Presenter/MainPresenterInput.swift b/T-bank_HW8/T-bank_HW8/Presenter/MainPresenterInput.swift
new file mode 100644
index 0000000..28816dc
--- /dev/null
+++ b/T-bank_HW8/T-bank_HW8/Presenter/MainPresenterInput.swift
@@ -0,0 +1,12 @@
+//
+//  MainPresenterInput.swift
+//  T-bank_HW8
+//
+//  Created by Dmitrii Eselidze on 15.04.2025.
+//
+
+protocol MainPresenterInput: AnyObject {
+    var itemsCount: Int { get }
+    func viewDidLoad()
+    func item(at index: Int) -> Item
+}
diff --git a/T-bank_HW8/T-bank_HW8/SceneDelegate.swift b/T-bank_HW8/T-bank_HW8/SceneDelegate.swift
new file mode 100644
index 0000000..8be0144
--- /dev/null
+++ b/T-bank_HW8/T-bank_HW8/SceneDelegate.swift
@@ -0,0 +1,60 @@
+//
+//  SceneDelegate.swift
+//  T-bank_HW8
+//
+//  Created by Dmitrii Eselidze on 11.04.2025.
+//
+
+import UIKit
+
+class SceneDelegate: UIResponder, UIWindowSceneDelegate {
+
+    var window: UIWindow?
+
+
+    func scene(
+        _ scene: UIScene,
+        willConnectTo session: UISceneSession,
+        options connectionOptions: UIScene.ConnectionOptions) {
+
+            guard let windowScene = (scene as? UIWindowScene) else { return }
+            
+            let window = UIWindow(windowScene: windowScene)
+            let viewController = MainSceneAssembler().makeScene ()
+            window.rootViewController = viewController
+            window.makeKeyAndVisible()
+            
+            self.window = window
+        }
+
+    func sceneDidDisconnect(_ scene: UIScene) {
+        // Called as the scene is being released by the system.
+        // This occurs shortly after the scene enters the background, or when its session is discarded.
+        // Release any resources associated with this scene that can be re-created the next time the scene connects.
+        // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
+    }
+
+    func sceneDidBecomeActive(_ scene: UIScene) {
+        // Called when the scene has moved from an inactive state to an active state.
+        // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
+    }
+
+    func sceneWillResignActive(_ scene: UIScene) {
+        // Called when the scene will move from an active state to an inactive state.
+        // This may occur due to temporary interruptions (ex. an incoming phone call).
+    }
+
+    func sceneWillEnterForeground(_ scene: UIScene) {
+        // Called as the scene transitions from the background to the foreground.
+        // Use this method to undo the changes made on entering the background.
+    }
+
+    func sceneDidEnterBackground(_ scene: UIScene) {
+        // Called as the scene transitions from the foreground to the background.
+        // Use this method to save data, release shared resources, and store enough scene-specific state information
+        // to restore the scene back to its current state.
+    }
+
+
+}
+
diff --git a/T-bank_HW8/T-bank_HW8/Task.swift b/T-bank_HW8/T-bank_HW8/Task.swift
new file mode 100644
index 0000000..4862a2a
--- /dev/null
+++ b/T-bank_HW8/T-bank_HW8/Task.swift
@@ -0,0 +1,54 @@
+//
+//  Task.swift
+//  T-bank_HW8
+//
+//  Created by Dmitrii Eselidze on 16.04.2025.
+//
+//
+
+///Описание
+///1. Получить список изображений с API:
+///https://fakestoreapi.com/products
+/// Этот API возвращает массив объектов в формате JSON, каждый объект содержит:
+///
+//{
+//  "id": 1,
+//  "title": "Fjallraven - Foldsack No. 1 Backpack, Fits 15 Laptops",
+//  "price": 109.95,
+//  "description": "Your perfect pack for everyday use and walks in the forest. Stash your laptop (up to 15 inches) in the padded sleeve, your everyday",
+//  "category": "men's clothing",
+//  "image": "https://fakestoreapi.com/img/81fPKd-2AYL._AC_SL1500_.jpg",
+//  "rating": {
+//    "rate": 3.9,
+//    "count": 120
+//  }
+//}
+//title — название товара
+//image — прямая ссылка на картинку
+//Эти данные необходимо распарсить с помощью Decodable
+///
+///2. Показать полноэкранный спиннер (ActivityIndicator), пока загружается JSON.
+///
+///3. После получения списка:
+    ///начать загружать все изображения
+    ///каждое изображение — через URLSession + URLSessionDataDelegate
+    ///отображать общий прогресс загрузки всех изображений
+///
+///4. Отображение списка в UITableView :
+    ///миниатюра изображения + название товара
+    ///можно использовать UITableViewCell с contentConfiguration
+///
+///5. Наверху таблицы — текст или прогресс-бар:
+    ///Загружено X из Y или Прогресс: 63%
+///
+///Как считать прогресс
+    ///Все изображения считаются одинакового размера
+    ///Прогресс можно считать как:
+        ///общий прогресс = сумма всех прогрессов / общее количество изображений
+///
+///Обязательные требования
+    ///Архитектура MVP
+    ///URLSession + URLSessionDataDelegate
+    ///UITableView
+    ///Decodable для парсинга JSON
+    ///Обязательный ActivityIndicator на загрузке списка
diff --git a/T-bank_HW8/T-bank_HW8/Views/MainViewController.swift b/T-bank_HW8/T-bank_HW8/Views/MainViewController.swift
new file mode 100644
index 0000000..df272a7
--- /dev/null
+++ b/T-bank_HW8/T-bank_HW8/Views/MainViewController.swift
@@ -0,0 +1,181 @@
+//
+//  ViewController.swift
+//  T-bank_HW8
+//
+//  Created by Dmitrii Eselidze on 11.04.2025.
+//
+
+import UIKit
+
+final class MainViewController: UIViewController {
+    private let presenter: MainPresenterInput
+    private var loadedImages = 0
+    private var totalImages: Int {
+        presenter.itemsCount
+    }
+    private var activityIndicator = UIActivityIndicatorView()
+    
+    private var loadedIndexPaths: Set<IndexPath> = []
+    private var imageCache: [IndexPath: UIImage] = [:]
+    
+    private lazy var tableView: UITableView = { // Строим TableView
+        let tableView = UITableView()
+        tableView.register(UITableViewCell.self, forCellReuseIdentifier: Constant.cellId)
+        tableView.translatesAutoresizingMaskIntoConstraints = false
+        tableView.dataSource = self
+        tableView.delegate = self
+        
+        return tableView
+    }()
+    
+    private lazy var progressLabel: UILabel = { // Окно с прогрессом
+        let label = UILabel()
+        label.translatesAutoresizingMaskIntoConstraints = false
+        label.textAlignment = .center
+        label.textColor = .darkGray
+        label.font = UIFont.systemFont(ofSize: 14)
+        label.text = "Ни одно изображение не загружено"
+        return label
+    }()
+    
+    
+    init(presenter: MainPresenterInput) {
+        self.presenter = presenter
+        super.init(nibName: nil, bundle: nil)
+    }
+    
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        setupActivityIndicator()
+        activityIndicator.startAnimating()
+        setupView()
+        presenter.viewDidLoad()
+    }
+    
+    private func setupView() { // Добавление всех View
+        view.backgroundColor = .white
+        view.addSubview(progressLabel)
+        view.addSubview(tableView)
+        view.addSubview(activityIndicator)
+        
+        NSLayoutConstraint.activate([
+            progressLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 8),
+            progressLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
+            progressLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),
+            
+            tableView.topAnchor.constraint(equalTo: progressLabel.bottomAnchor, constant: 8),
+            tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
+            tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
+            tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
+            
+            activityIndicator.centerXAnchor.constraint(equalTo: view.centerXAnchor),
+            activityIndicator.centerYAnchor.constraint(equalTo: view.centerYAnchor)
+        ])
+    }
+    
+    private func resizeImage(_ image: UIImage, targetSize: CGSize) -> UIImage {
+        let size = image.size
+        let widthRatio  = targetSize.width  / size.width
+        let heightRatio = targetSize.height / size.height
+        let scaleFactor = min(widthRatio, heightRatio)
+        let newSize = CGSize(width: size.width * scaleFactor, height: size.height * scaleFactor)
+        let renderer = UIGraphicsImageRenderer(size: newSize)
+        return renderer.image { _ in
+            image.draw(in: CGRect(origin: .zero, size: newSize))
+        }
+    }
+}
+
+
+extension MainViewController: MainViewInput {
+    
+    func setupActivityIndicator() {
+        activityIndicator.translatesAutoresizingMaskIntoConstraints = false
+        activityIndicator.center = view.center
+        activityIndicator.hidesWhenStopped = true
+        activityIndicator.style = .large
+        activityIndicator.color = UIColor.systemBlue
+        view.addSubview(activityIndicator)
+    }
+    
+    func stopActivityIndicator() {
+        activityIndicator.stopAnimating()
+    }
+    
+    func loadImage(for item: Item, at indexPath: IndexPath) {
+        guard let url = URL(string: item.image) else { return }
+        
+        if loadedIndexPaths.contains(indexPath) {
+            return
+        }
+
+        URLSession.shared.dataTask(with: url) { [weak self] data, _, error in
+            guard let self, let data = data, error == nil,
+                  let image = UIImage(data: data) else { return }
+
+            DispatchQueue.main.async {
+                self.loadedIndexPaths.insert(indexPath)
+                self.loadedImages += 1  // Увеличиваем счетчик загруженных картинок
+                self.updateImageLoadingProgress(loaded: self.loadedImages, total: self.totalImages)
+                let resizedImage = self.resizeImage(image, targetSize: CGSize(width: 40, height: 40))
+                self.imageCache[indexPath] = resizedImage
+                
+                if let visibleCell = self.tableView.cellForRow(at: indexPath) {
+                    var updatedContent = visibleCell.defaultContentConfiguration()
+                    updatedContent.text = item.title
+                    updatedContent.image = resizedImage
+                    
+                    updatedContent.imageProperties.maximumSize = CGSize(width: 40, height: 40)
+                    visibleCell.contentConfiguration = updatedContent
+                }
+            }
+        }.resume()
+    }
+    
+    func updateImageLoadingProgress(loaded: Int, total: Int) {
+        progressLabel.text = "Загружено \(loaded) из \(total)"
+    }
+    
+    func updateTable() {
+        tableView.reloadData()
+    }
+}
+
+extension MainViewController: UITableViewDataSource {
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        presenter.itemsCount
+    }
+    
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+        guard let cell = tableView.dequeueReusableCell(withIdentifier: Constant.cellId) else {
+            return UITableViewCell()
+        }
+        
+        let item = presenter.item(at: indexPath.row)
+        var content = cell.defaultContentConfiguration()
+        content.text = item.title
+        
+        if let cachedImage = imageCache[indexPath] {
+            content.image = cachedImage
+        } else {
+            content.image = UIImage(systemName: "photo")
+            loadImage(for: item, at: indexPath)
+        }
+        
+        content.text = item.title
+        cell.contentConfiguration = content
+        return cell
+    }
+}
+
+extension MainViewController: UITableViewDelegate {}
+
+extension MainViewController {
+    private enum Constant {
+        static let cellId = "cellId"
+    }
+}
diff --git a/T-bank_HW8/T-bank_HW8/Views/MainViewInput.swift b/T-bank_HW8/T-bank_HW8/Views/MainViewInput.swift
new file mode 100644
index 0000000..3a494ec
--- /dev/null
+++ b/T-bank_HW8/T-bank_HW8/Views/MainViewInput.swift
@@ -0,0 +1,17 @@
+//
+//  MainViewInput.swift
+//  T-bank_HW8
+//
+//  Created by Dmitrii Eselidze on 15.04.2025.
+//
+
+import UIKit
+
+protocol MainViewInput: AnyObject {
+    func updateTable()
+    func updateImageLoadingProgress(loaded: Int, total: Int)
+    func stopActivityIndicator()
+//    func indicatorAppear()
+//    func indicatorDisappear()
+    
+}


Reviews:
user: login: larsvonchiller
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-22T08:54:11Z

Review comments:
diff_hunk: @@ -0,0 +1,136 @@
+//
+//  NetworkClient.swift
+//  T-bank_HW8
+//
+//  Created by Dmitrii Eselidze on 15.04.2025.
+//
+
+
+import Foundation
+
+final class NetworkClient: NSObject {
+    private var progressBlocks: [Int: (Double) -> Void] = [:]
+    private var competionBlocks: [Int: (Result<Data, NetworkClientError>) -> Void] = [:]
+    
+    private lazy var urlSession: URLSession = {
+        let configuration = URLSessionConfiguration.default
+        return URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
+    }()
+}
+
+extension NetworkClient: NetworkClientInput {
+    func get<ResponseSchema: Decodable>(
+        urlString: String,
+        completion: @escaping (Result<ResponseSchema, NetworkClientError>) -> Void
+    ) {
+        guard let url = URL(string: urlString) else {
+            completion(.failure(.invalidURL))
+            return
+        }
+        let task = urlSession.dataTask(with: url) { data, response, error in
+            if let data = data {
+                do {
+                    let result = try JSONDecoder().decode(ResponseSchema.self, from: data)
+                    completion(.success(result))
+                } catch {
+                    completion(.failure(.decodingError(error)))
+                }
+            } else if let error = error {
+                completion(.failure(.networkError(error)))
+            }
+        }
+        task.resume()
+    }
path: T-bank_HW8/T-bank_HW8/Model/NetworkClient.swift
user: login: larsvonchiller
body: пробел
created_at: 2025-04-22T08:43:30Z
author_association: NONE
diff_hunk: @@ -0,0 +1,136 @@
+//
+//  NetworkClient.swift
+//  T-bank_HW8
+//
+//  Created by Dmitrii Eselidze on 15.04.2025.
+//
+
+
+import Foundation
+
+final class NetworkClient: NSObject {
+    private var progressBlocks: [Int: (Double) -> Void] = [:]
+    private var competionBlocks: [Int: (Result<Data, NetworkClientError>) -> Void] = [:]
+    
+    private lazy var urlSession: URLSession = {
+        let configuration = URLSessionConfiguration.default
+        return URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
+    }()
+}
+
+extension NetworkClient: NetworkClientInput {
+    func get<ResponseSchema: Decodable>(
+        urlString: String,
+        completion: @escaping (Result<ResponseSchema, NetworkClientError>) -> Void
+    ) {
+        guard let url = URL(string: urlString) else {
+            completion(.failure(.invalidURL))
+            return
+        }
+        let task = urlSession.dataTask(with: url) { data, response, error in
+            if let data = data {
+                do {
+                    let result = try JSONDecoder().decode(ResponseSchema.self, from: data)
+                    completion(.success(result))
+                } catch {
+                    completion(.failure(.decodingError(error)))
+                }
+            } else if let error = error {
+                completion(.failure(.networkError(error)))
+            }
+        }
+        task.resume()
+    }
path: T-bank_HW8/T-bank_HW8/Model/NetworkClient.swift
user: login: larsvonchiller
body: т е отступ
created_at: 2025-04-22T08:43:46Z
author_association: NONE
diff_hunk: @@ -0,0 +1,21 @@
+//
+//  NetworkClientError.swift
+//  T-bank_HW8
+//
+//  Created by Dmitrii Eselidze on 15.04.2025.
+//
+
+import Foundation
+
+enum NetworkClientError: Error {
+/// Неверный URL
+    case invalidURL
+/// Ошибка сериализации данных
+case encodingError(Error)
path: T-bank_HW8/T-bank_HW8/Model/NetworkClientError.swift
user: login: larsvonchiller
body: отступы
created_at: 2025-04-22T08:45:49Z
author_association: NONE
diff_hunk: @@ -0,0 +1,26 @@
+//
+//  NetworkClientInput.swift
+//  T-bank_HW8
+//
+//  Created by Dmitrii Eselidze on 15.04.2025.
+//
+
+import Foundation
+
+protocol NetworkClientInput: AnyObject {
path: T-bank_HW8/T-bank_HW8/Model/NetworkClientInput.swift
user: login: larsvonchiller
body: может лучше бы это сделалать enumОМ? можно не исправлять, чисто на подумать тебе
created_at: 2025-04-22T08:46:37Z
author_association: NONE
diff_hunk: @@ -0,0 +1,181 @@
+//
+//  ViewController.swift
+//  T-bank_HW8
+//
+//  Created by Dmitrii Eselidze on 11.04.2025.
+//
+
+import UIKit
+
+final class MainViewController: UIViewController {
+    private let presenter: MainPresenterInput
+    private var loadedImages = 0
+    private var totalImages: Int {
+        presenter.itemsCount
+    }
+    private var activityIndicator = UIActivityIndicatorView()
+    
+    private var loadedIndexPaths: Set<IndexPath> = []
+    private var imageCache: [IndexPath: UIImage] = [:]
+    
+    private lazy var tableView: UITableView = { // Строим TableView
+        let tableView = UITableView()
+        tableView.register(UITableViewCell.self, forCellReuseIdentifier: Constant.cellId)
+        tableView.translatesAutoresizingMaskIntoConstraints = false
+        tableView.dataSource = self
+        tableView.delegate = self
+        
+        return tableView
+    }()
+    
+    private lazy var progressLabel: UILabel = { // Окно с прогрессом
path: T-bank_HW8/T-bank_HW8/Views/MainViewController.swift
user: login: larsvonchiller
body: иногда лучше делать такие поля опционалами
created_at: 2025-04-22T08:50:01Z
author_association: NONE
diff_hunk: @@ -0,0 +1,181 @@
+//
+//  ViewController.swift
+//  T-bank_HW8
+//
+//  Created by Dmitrii Eselidze on 11.04.2025.
+//
+
+import UIKit
+
+final class MainViewController: UIViewController {
+    private let presenter: MainPresenterInput
+    private var loadedImages = 0
+    private var totalImages: Int {
+        presenter.itemsCount
+    }
+    private var activityIndicator = UIActivityIndicatorView()
+    
+    private var loadedIndexPaths: Set<IndexPath> = []
+    private var imageCache: [IndexPath: UIImage] = [:]
+    
+    private lazy var tableView: UITableView = { // Строим TableView
+        let tableView = UITableView()
+        tableView.register(UITableViewCell.self, forCellReuseIdentifier: Constant.cellId)
+        tableView.translatesAutoresizingMaskIntoConstraints = false
+        tableView.dataSource = self
+        tableView.delegate = self
+        
+        return tableView
+    }()
+    
+    private lazy var progressLabel: UILabel = { // Окно с прогрессом
+        let label = UILabel()
+        label.translatesAutoresizingMaskIntoConstraints = false
+        label.textAlignment = .center
+        label.textColor = .darkGray
+        label.font = UIFont.systemFont(ofSize: 14)
+        label.text = "Ни одно изображение не загружено"
+        return label
+    }()
+    
+    
+    init(presenter: MainPresenterInput) {
+        self.presenter = presenter
+        super.init(nibName: nil, bundle: nil)
+    }
+    
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        setupActivityIndicator()
+        activityIndicator.startAnimating()
+        setupView()
+        presenter.viewDidLoad()
+    }
+    
+    private func setupView() { // Добавление всех View
+        view.backgroundColor = .white
+        view.addSubview(progressLabel)
+        view.addSubview(tableView)
+        view.addSubview(activityIndicator)
+        
+        NSLayoutConstraint.activate([
+            progressLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 8),
+            progressLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
+            progressLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16),
+            
+            tableView.topAnchor.constraint(equalTo: progressLabel.bottomAnchor, constant: 8),
+            tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
+            tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
+            tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
+            
+            activityIndicator.centerXAnchor.constraint(equalTo: view.centerXAnchor),
+            activityIndicator.centerYAnchor.constraint(equalTo: view.centerYAnchor)
+        ])
+    }
+    
+    private func resizeImage(_ image: UIImage, targetSize: CGSize) -> UIImage {
+        let size = image.size
+        let widthRatio  = targetSize.width  / size.width
+        let heightRatio = targetSize.height / size.height
+        let scaleFactor = min(widthRatio, heightRatio)
+        let newSize = CGSize(width: size.width * scaleFactor, height: size.height * scaleFactor)
+        let renderer = UIGraphicsImageRenderer(size: newSize)
+        return renderer.image { _ in
path: T-bank_HW8/T-bank_HW8/Views/MainViewController.swift
user: login: larsvonchiller
body: не читабельно, можно не править
created_at: 2025-04-22T08:51:06Z
author_association: NONE

PR comments:
user: login: larsvonchiller
created_at: 2025-04-22T08:31:41Z
body: Можешь приложить видос?
author_association: NONE
user: login: dima-code
created_at: 2025-04-22T08:43:54Z
body: 
https://github.com/user-attachments/assets/a3b418fb-04bb-4246-8379-d275cfae9569


author_association: OWNER