// // CloudFileStorage.swift // Notes // // Created by Laurent Morvillier on 16/09/2022. // import Foundation import UIKit struct StorageRequest { var filename: String var content: String } let idleTimeBeforeSaving = 2.0 /// Should we have a way to go from local to iCloud? /// https://stackoverflow.com/questions/33886846/best-way-to-use-icloud-documents-storage /// Should we store the files locally whatever the iCloud choice is? class FileStorage : FileOperator { static var main: FileStorage = FileStorage() fileprivate var _containerURL: URL? fileprivate var _cloudStorageDetermined: Bool = false fileprivate let containerIdentifier = "notes" fileprivate var _timer: Timer? = nil fileprivate var _storageRequests: [String : String] = [:] init() { DispatchQueue.global(qos: .userInteractive).async { self._containerURL = FileManager.default.url(forUbiquityContainerIdentifier: self.containerIdentifier) self._cloudStorageDetermined = true print("Cloud container URL is : \(String(describing: self._containerURL?.absoluteString))") } } fileprivate func _directoryURL() throws -> URL { let documentDirectory = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) return documentDirectory } // MARK: - FileOperator func requestStorage(filename: String, content: String) { self._storageRequests[filename] = content self._timer?.invalidate() self._timer = Timer.scheduledTimer(timeInterval: idleTimeBeforeSaving, target: self, selector: #selector(self._storageRequested), userInfo: nil, repeats: false) } @objc fileprivate func _storageRequested() { for (filename, content) in self._storageRequests { do { try self._store(filename: filename, content: content) } catch { // TODO show errors to users, possibly by notifications print("error: \(error.localizedDescription)") } } self._storageRequests.removeAll() } fileprivate func _store(filename: String, content: String) throws { let fileURL = try self._directoryURL().appending(path: filename) print("Store file to: \(fileURL.absoluteString)") try content.write(to: fileURL, atomically: true, encoding: .utf8) try self._copyToCloudContainerIfNecessary(fileURL: fileURL, filename: filename) } func getContent(filename: String) throws -> String? { let fileURL = try self._directoryURL().appending(path: filename) return try String(contentsOf: fileURL) } func lastEditDate(filename: String) throws -> Date? { let fileURL = try self._directoryURL().appending(path: filename) let attributes = try FileManager.default.attributesOfItem(atPath: fileURL.absoluteString) return attributes[FileAttributeKey.modificationDate] as? Date } fileprivate func _copyToCloudContainerIfNecessary(fileURL: URL, filename: String) throws { guard let containerURL = self._containerURL else { return } let cloudURL = containerURL.appending(path: filename) try FileManager.default.copyItem(at: fileURL, to: cloudURL) print("cloud copy to: \(cloudURL)") } deinit { self._timer?.invalidate() } }