// // ImageDataCache.swift // Tusker // // Created by Shadowfacts on 1/16/21. // Copyright © 2021 Shadowfacts. All rights reserved. // import UIKit import Cache class ImageDataCache { private let memory: MemoryCache private let disk: DiskCache? private let storeOriginalDataInMemory: Bool private let desiredPixelSize: CGSize? init(name: String, memoryExpiry: CacheExpiry, diskExpiry: CacheExpiry?, storeOriginalDataInMemory: Bool, desiredPixelSize: CGSize?) { self.memory = MemoryCache(defaultExpiry: memoryExpiry) if let diskExpiry = diskExpiry { self.disk = try! DiskCache(name: name, defaultExpiry: diskExpiry) } else { self.disk = nil } self.storeOriginalDataInMemory = storeOriginalDataInMemory self.desiredPixelSize = desiredPixelSize } func has(_ key: String) throws -> Bool { if memory.existsObject(forKey: key) { return true } else if let disk = self.disk, try disk.existsObject(forKey: key) { return true } else { return false } } func get(_ key: String) throws -> Entry? { if let memoryEntry = try? memory.object(forKey: key) { return memoryEntry } else if let disk = self.disk, let data = try? disk.object(forKey: key), let image = UIImage(data: data) { return Entry(data: data, image: image) } else { return nil } } func getImage(_ key: String) throws -> UIImage? { return try get(key)?.image } func getData(_ key: String) throws -> Data? { return try get(key)?.data } func set(_ key: String, data: Data) throws { guard let image = scaleImageIfDesired(data: data) else { return } let entry = Entry(data: storeOriginalDataInMemory ? data : nil, image: image) memory.setObject(entry, forKey: key) if let disk = self.disk { try disk.setObject(data, forKey: key) } } func removeAll() throws { memory.removeAll() try? disk?.removeAll() } private func scaleImageIfDesired(data: Data) -> UIImage? { guard let desiredPixelSize = desiredPixelSize, let source = CGImageSourceCreateWithData(data as CFData, [kCGImageSourceShouldCache: false] as CFDictionary) else { return UIImage(data: data) } let maxDimension = max(desiredPixelSize.width, desiredPixelSize.height) let downsampleOptions = [ kCGImageSourceCreateThumbnailFromImageAlways: true, kCGImageSourceShouldCacheImmediately: true, kCGImageSourceCreateThumbnailWithTransform: true, kCGImageSourceThumbnailMaxPixelSize: maxDimension ] as CFDictionary if let downsampled = CGImageSourceCreateThumbnailAtIndex(source, 0, downsampleOptions) { return UIImage(cgImage: downsampled) } else { return UIImage(data: data) } } } extension ImageDataCache { struct Entry { let data: Data? let image: UIImage } }