1import AppKit23456789class ImageCache {10 static let shared = ImageCache()1112 13 14 15 16 static let defaultCountLimit = 641718 19 20 21 22 static let defaultTotalCostLimit = 64 * 1024 * 10242324 private let cache = NSCache<NSString, NSImage>()25 private let session: URLSession2627 init(28 session: URLSession = .shared,29 countLimit: Int = ImageCache.defaultCountLimit,30 totalCostLimit: Int = ImageCache.defaultTotalCostLimit31 ) {32 self.session = session33 self.cache.countLimit = countLimit34 self.cache.totalCostLimit = totalCostLimit35 }3637 func image(for url: URL) -> NSImage? {38 return cache.object(forKey: url.absoluteString as NSString)39 }4041 func insertImage(_ image: NSImage, for url: URL) {42 cache.setObject(image, forKey: url.absoluteString as NSString, cost: estimatedCost(of: image))43 }4445 func fetchImage(url: URL) async throws -> NSImage {46 if let cached = image(for: url) {47 return cached48 }4950 let (data, response) = try await session.data(from: url)51 guard let response = response as? HTTPURLResponse,52 (200...299).contains(response.statusCode)53 else {54 throw WallhavenError.apiError("Image download failed")55 }56 guard let image = NSImage(data: data) else {57 throw WallhavenError.decodingError(NSError(domain: "ImageCache", code: -1, userInfo: nil))58 }5960 insertImage(image, for: url)61 return image62 }6364 65 66 67 68 private func estimatedCost(of image: NSImage) -> Int {69 let size = image.size70 let width = Int(size.width.rounded())71 let height = Int(size.height.rounded())72 guard width > 0, height > 0 else { return 0 }73 return width * height * 474 }75}