90 lines
3.0 KiB
Swift
90 lines
3.0 KiB
Swift
//
|
|
// 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<NSString, UIImage>()
|
|
|
|
/// 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)
|
|
}
|
|
}
|