// // DiskManager.swift // CapFinder // // Created by User on 23.04.18. // Copyright © 2018 User. All rights reserved. // import Foundation import UIKit final class DiskManager { enum LocalDirectory: String { /// Folder for new images to upload case upload = "Upload" /// Folder for downloaded images case images = "Images" /// Folder for downloaded images case thumbnails = "Thumbnails" /// Directory for name file case files = "Files" private static let fm = FileManager.default /// The url to the file sstem var url: URL { return URL(fileURLWithPath: self.rawValue, isDirectory: true, relativeTo: DiskManager.documentsDirectory) } fileprivate func create() -> Bool { return DiskManager.create(directory: url) } var content: [URL]? { do { return try LocalDirectory.fm.contentsOfDirectory(at: url, includingPropertiesForKeys: nil, options: []) } catch { print("[LocalDirectory] Could not read directory \(self.rawValue): \(error)") return nil } } } /// The folder where images and name list are stored static let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! private static let fm = FileManager.default // MARK: - First launch @discardableResult static func setupOnFirstLaunch() -> Bool { return LocalDirectory.files.create() && LocalDirectory.images.create() && LocalDirectory.thumbnails.create() && LocalDirectory.upload.create() } private static func create(directory: URL) -> Bool { do { if !fm.fileExists(atPath: directory.path) { try fm.createDirectory(at: directory, withIntermediateDirectories: true, attributes: nil) } return true } catch { event("Could not create \(directory): \(error)") return false } } // MARK: - Image retrieval /** Check if an image exists for a cap - parameter cap: The id of the cap - returns: True, if an image exists */ static func hasImage(for cap: Int) -> Bool { let url = localUrl(for: cap) return fm.fileExists(atPath: url.path) } private static func localUrl(for cap: Int) -> URL { return URL(fileURLWithPath: "\(cap).jpg", isDirectory: true, relativeTo: LocalDirectory.images.url) } private static func thumbnailUrl(for cap: Int) -> URL { return URL(fileURLWithPath: "\(cap).jpg", isDirectory: true, relativeTo: LocalDirectory.thumbnails.url) } /** Get the image for a cap. If the image exists on disk, it is returned. If no image exists locally, then this function returns nil. - parameter cap: The id of the cap - returns: The image data, or `nil` */ static func image(for cap: Int) -> Data? { // If the image exists on disk, get it let url = localUrl(for: cap) return readData(from: url) } private static let mosaicURL: URL = documentsDirectory.appendingPathComponent("mosaic.png") static var mosaicExists: Bool { return fm.fileExists(atPath: mosaicURL.path) } static func saveMosaicData(_ data: Data) -> Bool { return write(data, to: mosaicURL) } static var mosaicData: Data? { return readData(from: mosaicURL) } /** Get the thumbnail for a cap. If the image exists on disk, it is returned. If no image exists locally, then this function returns nil. - parameter cap: The id of the cap - returns: The image data, or `nil` */ static func thumbnail(for cap: Int) -> Data? { // If the image exists on disk, get it let url = thumbnailUrl(for: cap) return readData(from: url) } private static func readData(from url: URL) -> Data? { guard fm.fileExists(atPath: url.path) else { return nil } do { return try Data(contentsOf: url) } catch { self.error("Could not read data from \(url): \(error)") return nil } } /** Save an image to the download folder - parameter imageData: The data of the image - parameter cap: The cap id - returns: True, if the image was saved */ static func save(imageData: Data, for cap: Int) -> Bool { let url = localUrl(for: cap) return write(imageData, to: url) } /** Save a thumbnail to the download folder - parameter thumbnailData: The data of the image - parameter cap: The cap id - returns: True, if the image was saved */ static func save(thumbnailData: Data, for cap: Int) -> Bool { let url = thumbnailUrl(for: cap) return write(thumbnailData, to: url) } private static func write(_ data: Data, to url: URL) -> Bool { do { try data.write(to: url) } catch { self.error("Could not write data to \(url): \(error)") return false } return true } static func removeFromUpload(url: URL) -> Bool { guard fm.fileExists(atPath: url.path) else { return true } do { try fm.removeItem(at: url) return true } catch { self.error("Could not delete file \(url.path)") return false } } static var pendingUploads: [URL]? { return LocalDirectory.upload.content } static var availableImages: [Int]? { return LocalDirectory.images.content?.compactMap { Int($0.lastPathComponent.components(separatedBy: ".").first ?? "") } } /** Save an image to the uploads folder for later */ static func saveForUpload(imageData: Data, name: String) -> URL? { let url = LocalDirectory.upload.url.appendingPathComponent(name) return write(imageData, to: url) ? url : nil } } extension DiskManager: Logger { static let logToken = "[DiskManager]" }