import Foundation #if canImport(FoundationNetworking) import FoundationNetworking #endif public actor ImageService { private let cacheDir: URL private let memoryCache = NSCache() public init(cacheDir: URL? = nil) { let defaultCache = FileManager.default.urls( for: .cachesDirectory, in: .userDomainMask ).first!.appendingPathComponent("luminate/images") self.cacheDir = cacheDir ?? defaultCache try? FileManager.default.createDirectory( at: self.cacheDir, withIntermediateDirectories: true) } public func loadImage(url: URL) async throws -> Data { let key = url.absoluteString as NSString if let cached = memoryCache.object(forKey: key) { return cached as Data } let diskKey = url.absoluteString.data(using: .utf8)!.base64EncodedString() .replacingOccurrences(of: "/", with: "_") let diskURL = cacheDir.appendingPathComponent(diskKey) if let data = try? Data(contentsOf: diskURL) { memoryCache.setObject(data as NSData, forKey: key) return data } let (data, _) = try await URLSession.shared.data(from: url) let nsData = data as NSData memoryCache.setObject(nsData, forKey: key) try? data.write(to: diskURL) return data } public func prefetch(urls: [URL]) async { await withTaskGroup(of: Void.self) { group in for url in urls { group.addTask { _ = try? await self.loadImage(url: url) } } } } public func clearCache() { memoryCache.removeAllObjects() try? FileManager.default.removeItem(at: cacheDir) try? FileManager.default.createDirectory(at: cacheDir, withIntermediateDirectories: true) } }