diff --git a/Shared/Components/RemoteImageView.swift b/Shared/Components/RemoteImageView.swift index 4db60fd..388257e 100644 --- a/Shared/Components/RemoteImageView.swift +++ b/Shared/Components/RemoteImageView.swift @@ -1,11 +1,13 @@ import SwiftUI import Combine -// 1. Класс для загрузки изображения +// 1. Класс для загрузки изображения с поддержкой кеширования class ImageLoader: ObservableObject { @Published var image: UIImage? + private var cancellable: AnyCancellable? private let url: URL + private let cache = ImageCacheManager.shared init(url: URL) { self.url = url @@ -16,9 +18,22 @@ class ImageLoader: ObservableObject { } func load() { + // Проверяем, есть ли изображение в кеше + if let cachedImage = cache.get(forKey: url.absoluteString) { + self.image = cachedImage + return + } + + // Если в кеше нет, загружаем из сети cancellable = URLSession.shared.dataTaskPublisher(for: url) .map { UIImage(data: $0.data) } .replaceError(with: nil) + .handleEvents(receiveOutput: { [weak self] image in + // Сохраняем загруженное изображение в кеш + if let image = image, let key = self?.url.absoluteString { + self?.cache.set(image, forKey: key) + } + }) .receive(on: DispatchQueue.main) .assign(to: \.image, on: self) } diff --git a/Shared/Services/ImageCacheManager.swift b/Shared/Services/ImageCacheManager.swift new file mode 100644 index 0000000..30770aa --- /dev/null +++ b/Shared/Services/ImageCacheManager.swift @@ -0,0 +1,22 @@ +import UIKit + +// Менеджер для кеширования изображений в памяти +class ImageCacheManager { + static let shared = ImageCacheManager() + + // NSCache будет хранить UIImage по ключу NSString (URL) + // Он автоматически удаляет объекты при нехватке памяти + private let cache = NSCache() + + private init() {} + + // Добавить изображение в кеш + func set(_ image: UIImage, forKey key: String) { + cache.setObject(image, forKey: key as NSString) + } + + // Получить изображение из кеша + func get(forKey key: String) -> UIImage? { + return cache.object(forKey: key as NSString) + } +} diff --git a/yobble.xcodeproj/project.pbxproj b/yobble.xcodeproj/project.pbxproj index 2e784e7..f27b39c 100644 --- a/yobble.xcodeproj/project.pbxproj +++ b/yobble.xcodeproj/project.pbxproj @@ -38,6 +38,7 @@ 1A9B017C2E4C087F00887E0B /* SideMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A9B017B2E4C087F00887E0B /* SideMenuView.swift */; }; 1A9E4FB32E4D6A67002249D6 /* ThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A9E4FB22E4D6A67002249D6 /* ThemeManager.swift */; }; 1A9E4FD72E4E47EF002249D6 /* RemoteImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A9E4FD62E4E47EF002249D6 /* RemoteImageView.swift */; }; + 1A9E4FDF2E4E4AA1002249D6 /* ImageCacheManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A9E4FDE2E4E4AA1002249D6 /* ImageCacheManager.swift */; }; 1AB4F8CD2E22E341002B6E40 /* AccountShareSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AB4F8CC2E22E341002B6E40 /* AccountShareSheet.swift */; }; 1AB4F8F32E22EC9F002B6E40 /* FollowersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AB4F8F22E22EC9F002B6E40 /* FollowersView.swift */; }; 1AB4F8F72E22ECAC002B6E40 /* FollowingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AB4F8F62E22ECAC002B6E40 /* FollowingView.swift */; }; @@ -94,6 +95,7 @@ 1A9B017B2E4C087F00887E0B /* SideMenuView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SideMenuView.swift; sourceTree = ""; }; 1A9E4FB22E4D6A67002249D6 /* ThemeManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemeManager.swift; sourceTree = ""; }; 1A9E4FD62E4E47EF002249D6 /* RemoteImageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteImageView.swift; sourceTree = ""; }; + 1A9E4FDE2E4E4AA1002249D6 /* ImageCacheManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageCacheManager.swift; sourceTree = ""; }; 1AB4F8CC2E22E341002B6E40 /* AccountShareSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountShareSheet.swift; sourceTree = ""; }; 1AB4F8F22E22EC9F002B6E40 /* FollowersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowersView.swift; sourceTree = ""; }; 1AB4F8F62E22ECAC002B6E40 /* FollowingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowingView.swift; sourceTree = ""; }; @@ -231,6 +233,7 @@ 1A7940E52DF7B341002569DA /* Services */ = { isa = PBXGroup; children = ( + 1A9E4FDE2E4E4AA1002249D6 /* ImageCacheManager.swift */, 1A9E4FB22E4D6A67002249D6 /* ThemeManager.swift */, 1A7940E12DF7B1C5002569DA /* KeychainService.swift */, ); @@ -437,6 +440,7 @@ 1A7940C62DF7A98E002569DA /* ContactsTab.swift in Sources */, 1ACE61092E22F57100B37AC5 /* AppPreferencesView.swift in Sources */, 1ACE61052E22F56800B37AC5 /* SecuritySettingsView.swift in Sources */, + 1A9E4FDF2E4E4AA1002249D6 /* ImageCacheManager.swift in Sources */, 1A9B016E2E4BFB9000887E0B /* NewHomeTabViewModel.swift in Sources */, 1A7940E72DF7B5E5002569DA /* SplashScreenView.swift in Sources */, 1A7940B02DF77E26002569DA /* LoginView.swift in Sources */,