// // ImageCacheService.swift // EmployeeDirectory // // Created by Matt Bruce on 1/20/25. // import Foundation import UIKit /// A service that handles image caching using memory, disk, and network in priority order. public class ImageCacheService { // MARK: - Properties public static let shared = ImageCacheService() // Default shared instance /// Memory cache for storing images in RAM. private let memoryCache = NSCache() /// File manager for handling disk operations. private let fileManager = FileManager.default /// Directory where cached images are stored on disk. private let cacheDirectory: URL = { FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0] }() // MARK: - Initializer public init() {} // MARK: - Public Methods /// Loads an image from memory, disk, or network. /// /// - Parameter url: The URL of the image to load. /// - Returns: The loaded `UIImage` or `nil` if the image could not be loaded. public func loadImage(from url: URL) async -> UIImage? { let uniqueKey = url.uniqueIdentifier let cacheKey = uniqueKey as NSString // Step 1: Check the memory cache if let cachedImage = memoryCache.object(forKey: cacheKey) { return cachedImage } // Step 2: Check the disk cache let diskImagePath = cacheDirectory.appendingPathComponent(uniqueKey) if let diskImage = UIImage(contentsOfFile: diskImagePath.path) { memoryCache.setObject(diskImage, forKey: cacheKey) // Cache in memory for faster access next time return diskImage } // Step 3: Fetch the image from the network if let networkImage = await fetchFromNetwork(url: url) { memoryCache.setObject(networkImage, forKey: cacheKey) // Cache in memory try? saveImageToDisk(image: networkImage, at: diskImagePath) // Cache on disk return networkImage } // Step 4: Return nil if all options fail return nil } // MARK: - Private Methods /// Fetches an image from the network. /// /// - Parameter url: The URL of the image to fetch. /// - Returns: The fetched `UIImage` or `nil` if the network request fails. private func fetchFromNetwork(url: URL) async -> UIImage? { do { let (data, _) = try await URLSession.shared.data(from: url) return UIImage(data: data) } catch { print("Failed to fetch image from network for URL: \(url). Error: \(error)") return nil } } /// Saves an image to disk at the specified path. /// /// - Parameters: /// - image: The `UIImage` to save. /// - path: The file path where the image should be saved. private func saveImageToDisk(image: UIImage, at path: URL) throws { guard let data = image.pngData() else { return } try data.write(to: path) } }