public final class Cacher { let destination: URL private let queue = OperationQueue() public enum CacheDestination { case temporary case atFolder(String) } // MARK: Initialization public init(destination: CacheDestination) { // Create the URL for the location of the cache resources switch destination { case .temporary: self.destination = URL(fileURLWithPath: NSTemporaryDirectory()) case .atFolder(let folder): let documentFolder = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] self.destination = URL(fileURLWithPath: documentFolder).appendingPathComponent(folder, isDirectory: true) } let fileManager = FileManager.default do { try fileManager.createDirectory(at: self.destination, withIntermediateDirectories: true, attributes: nil) } catch { fatalError("Unable to create cache URL: \(error)") } } public func persist<T>(_ item: T, at cache: Cache<T>, completion: @escaping (_ url: URL) -> Void) { let url = destination.appendingPathComponent(cache.fileName, isDirectory: false) // Create an operation to process the request. let operation = BlockOperation { do { try cache.transform(item).write(to: url, options: [.atomicWrite]) } catch { fatalError("Failed to write item to cache: \(error)") } } // Set the operation's completion block to call the request's completion handler. operation.completionBlock = { completion(url) } // Add the operation to the queue to start the work. queue.addOperation(operation) } public func load<T>(from cache: Cache<T>, completion: @escaping (_ item: T) -> Void) { let url = destination.appendingPathComponent(cache.fileName, isDirectory: false) // Create an operation to process the request. let operation = BlockOperation { do { let data = try Data(contentsOf: url, options: []) let item = try cache.reverse(data) completion(item) } catch { fatalError("Failed to read item from cache: \(error)") } } // Add the operation to the queue to start the work. queue.addOperation(operation) } }