// // AutoSaveManager.swift // PDF Reader Pro // // Created by tangchao on 2023/11/8. // import Cocoa public let KAutoSaveTimeValueChangedNoti = "KAutoSaveTimeValueChangedNoti" @objcMembers class AutoSaveManager: NSObject { public static let kTimeValueChangedNotificationName = Notification.Name(KAutoSaveTimeValueChangedNoti) //APP是否允许进行自动缓存 var autoSaveEnabled = false { didSet { UserDefaults.standard.setValue(self.autoSaveEnabled, forKey: "ComAutoSaveKey") UserDefaults.standard.synchronize() } } var timeInterval: CGFloat = 15 { didSet { UserDefaults.standard.setValue(Float(self.timeInterval), forKey: "AutoSaveTimeIntervalKey") UserDefaults.standard.synchronize() } } //防止重复弹出 var autoSaveAlertShow = false var autoSaveDidEndAction = false // /当前是否正在保存 var isSaving = false private var _autoSaveFolder: String = "" var autoSaveFolder: String { get { return self._autoSaveFolder } } private var _infoDict: NSMutableDictionary? // 保存的原文件信息 private var _originalPaths: NSMutableArray? var originalPaths: NSMutableArray? { get { return self._originalPaths } } // 对应保存的信息 private var _autoSavePaths: NSMutableArray? var autoSavePaths: NSMutableArray? { get { return self._autoSavePaths } } // 所有打开的文件信息汇总 var opendPaths: NSMutableArray? { get { return self._opendPaths } } private var _opendPaths: NSMutableArray? static let manager: AutoSaveManager = { let man = AutoSaveManager() man.autoSaveEnabled = UserDefaults.standard.bool(forKey: "ComAutoSaveKey") let timeInterval = UserDefaults.standard.float(forKey: "AutoSaveTimeIntervalKey") if timeInterval > 0 { man.timeInterval = timeInterval.cgFloat } else { man.timeInterval = 15 } man._loadData() man.autoSaveDidEndAction = true return man }() // MARK: - Public Method func clearCache() { if FileManager.default.fileExists(atPath: self.autoSaveFolder) { try?FileManager.default.removeItem(atPath: self.autoSaveFolder) } if FileManager.default.fileExists(atPath: self.autoSaveFolder) == false { try?FileManager.default.createDirectory(atPath: self.autoSaveFolder, withIntermediateDirectories: true) } } func autoSaveWithPath(_ filePath: String) -> String { let autoSaveFolder = self.autoSaveFolder let plistPath = String(format: "%@/autoSaveInfo.plist", autoSaveFolder) var savePath = String(format: "%@/%@_%@.%@", autoSaveFolder, filePath.deletingPathExtension.lastPathComponent, "recovered", filePath.customPathExtension) if FileManager.default.fileExists(atPath: plistPath) { var dict = NSMutableDictionary(contentsOfFile: plistPath) if let keys = dict?.allKeys as? [String], keys.contains(filePath) { savePath = dict?.value(forKey: filePath) as? String ?? "" } if savePath.isEmpty == false && filePath.isEmpty == false { if let values = dict?.allValues as? [String], values.contains(savePath) { //不同路径下同名文件的保存覆盖的情况处理 var values = NSArray(array: values) for key in dict?.allKeys ?? [] { guard let _key = key as? String else { continue } let value = dict?.object(forKey: key) as? String ?? "" if savePath == value && _key != filePath { var count = 1 savePath = String(format: "%@/%@_%@(%d).%@", autoSaveFolder, filePath.deletingPathExtension.lastPathComponent, "recovered", count, filePath.customPathExtension) while values.contains(savePath) { count += 1 savePath = String(format: "%@/%@_%@(%d).%@", autoSaveFolder, filePath.deletingPathExtension.lastPathComponent, "recovered", count, filePath.customPathExtension) } } } } dict?.setValue(savePath, forKey: filePath) } try?dict?.write(to: URL(fileURLWithPath: plistPath)) } else { var dict = NSMutableDictionary() if savePath.isEmpty == false && filePath.isEmpty == false { dict.setValue(savePath, forKey: filePath) } try?dict.write(to: URL(fileURLWithPath: plistPath)) } return savePath } func removeAutoSavePath(_ filePath: String) { let autoSaveFolder = self.autoSaveFolder let plistPath = String(format: "%@/autoSaveInfo.plist", autoSaveFolder) if FileManager.default.fileExists(atPath: plistPath) { var dict = NSMutableDictionary(contentsOfFile: plistPath) if let savePath = dict?.value(forKey: filePath) as? String { if FileManager.default.fileExists(atPath: savePath) { try?FileManager.default.removeItem(atPath: savePath) } dict?.removeObject(forKey: filePath) } if let keys = dict?.allKeys, keys.count > 0 { try?dict?.write(to: URL(fileURLWithPath: plistPath)) } else { try?FileManager.default.removeItem(atPath: plistPath) } } } // MARK: - Private Methods private func _loadData() { var cachesPath = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true).first ?? "" let filepath = "\(cachesPath)/\(Bundle.main.bundleIdentifier ?? "")" if FileManager.default.fileExists(atPath: filepath) { cachesPath = filepath } cachesPath = "\(cachesPath)/autoSaveFolder" if FileManager.default.fileExists(atPath: cachesPath) == false { try?FileManager.default.createDirectory(atPath: cachesPath, withIntermediateDirectories: true) } self._autoSaveFolder = cachesPath KMPrint("autoSaveFolder===\(cachesPath)") let plistPath = String(format: "%@/autoSaveInfo.plist", self.autoSaveFolder) guard let dict = NSMutableDictionary(contentsOfFile: plistPath) else { return } if (dict.allKeys.count == 0) { return } self._infoDict = dict if (self._autoSavePaths == nil) { self._autoSavePaths = NSMutableArray() } if (self._originalPaths == nil) { self._originalPaths = NSMutableArray() } self._originalPaths?.addObjects(from: dict.allKeys) self._opendPaths = NSMutableArray() var arr = (try?FileManager.default.contentsOfDirectory(atPath: self.autoSaveFolder)) ?? [] for fileName in arr { let savedKey = "\(self.autoSaveFolder)/\(fileName)" guard let values = dict.allValues as? [String] else { continue } if values.contains(savedKey) { self.autoSavePaths?.add(savedKey) } else { if fileName == "autoSaveInfo.plist" { } else { try?FileManager.default.removeItem(atPath: savedKey) } } } } /* - (NSString *)URLEncodedString:(NSString *)string { CFStringRef stringRef = CFBridgingRetain(string); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" CFStringRef encoded = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, stringRef, NULL, CFSTR("!*'\"();:@&=+$,/?%#[]% "), kCFStringEncodingUTF8); #pragma clang diagnostic pop CFRelease(stringRef); return CFBridgingRelease(encoded); } - (NSString *)getValidFilePath:(NSString *)oldPath { NSFileManager *fileManager = [NSFileManager defaultManager]; NSDictionary *fileDic = [fileManager attributesOfItemAtPath:oldPath error:nil]; NSString *fileType = [fileDic objectForKey:NSFileType]; int i =1; NSString *newPath = oldPath; while ([fileManager fileExistsAtPath:newPath]) { if ([fileType isEqualToString:NSFileTypeDirectory]) { newPath = [oldPath stringByAppendingFormat:@"(%d)",i]; } else { NSString *fileExtension = [oldPath pathExtension]; newPath = [[oldPath stringByDeletingPathExtension] stringByAppendingFormat:@"(%d).%@",i,fileExtension]; } i++; } return newPath; } */ }