// // ImageCache.swift // Tusker // // Created by Shadowfacts on 8/21/18. // Copyright © 2018 Shadowfacts. All rights reserved. // import UIKit import Cache class ImageCache { static let avatars = ImageCache(name: "Avatars", memoryExpiry: .seconds(60 * 60), diskExpiry: .seconds(60 * 60 * 24)) static let headers = ImageCache(name: "Headers", memoryExpiry: .seconds(60 * 60), diskExpiry: .seconds(60 * 60 * 24)) static let attachments = ImageCache(name: "Attachments", memoryExpiry: .seconds(60 * 2)) let cache: Cache var requests = [URL: Request]() init(name: String, memoryExpiry expiry: Expiry) { let storage = MemoryStorage(config: MemoryConfig(expiry: expiry)) self.cache = .memory(storage) } init(name: String, diskExpiry expiry: Expiry) { let storage = try! DiskStorage(config: DiskConfig(name: name, expiry: expiry), transformer: TransformerFactory.forData()) self.cache = .disk(storage) } init(name: String, memoryExpiry: Expiry, diskExpiry: Expiry) { let memory = MemoryStorage(config: MemoryConfig(expiry: memoryExpiry)) let disk = try! DiskStorage(config: DiskConfig(name: name, expiry: diskExpiry), transformer: TransformerFactory.forData()) self.cache = .hybrid(HybridStorage(memoryStorage: memory, diskStorage: disk)) } func get(_ url: URL, completion: ((Data?) -> Void)?) { let key = url.absoluteString if (try? cache.existsObject(forKey: key)) ?? false, let data = try? cache.object(forKey: key) { completion?(data) } else { if let completion = completion, let request = requests[url] { request.callbacks.append(completion) } else { let request = Request(url: url, completion: completion) requests[url] = request request.run { (data) in try? self.cache.setObject(data, forKey: key) } } } } func get(_ url: URL) -> Data? { return try? cache.object(forKey: url.absoluteString) } func cancel(_ url: URL) { requests[url]?.cancel() } class Request { let url: URL var task: URLSessionDataTask? var callbacks: [(Data?) -> Void] init(url: URL, completion: ((Data?) -> Void)?) { if let completion = completion { self.callbacks = [completion] } else { self.callbacks = [] } self.url = url } func run(cache: @escaping (Data) -> Void) { task = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in guard error == nil, let data = data else { self.complete(with: nil) return } cache(data) self.complete(with: data) }) task!.resume() } func cancel() { task?.cancel() complete(with: nil) } func complete(with data: Data?) { callbacks.forEach { $0(data) } } } }