diff --git a/Shared/SharedExtension/ExtensionsAddLinkRequestsManager.swift b/Shared/SharedExtension/ExtensionsAddLinkRequestsManager.swift index f6e16c8..51efaa9 100644 --- a/Shared/SharedExtension/ExtensionsAddLinkRequestsManager.swift +++ b/Shared/SharedExtension/ExtensionsAddLinkRequestsManager.swift @@ -8,101 +8,38 @@ import Foundation -final class ExtensionsAddLinkRequestsManager: NSObject, Logging { +final class ExtensionsAddLinkRequestsManager: Logging { - private static var filePathUrl: URL = { - URL.storeURL(for: "group.com.mattrighetti.Ulry", filename: "external_links.plist") - }() + private static let suite = "group.com.mattrighetti.Ulry" + private static let key = "pendingLinks" - var canSaveMoreLinks: Bool { - externalCache.count < 15 - } - - override init() { - super.init() - NotificationCenter.default.addObserver(self, selector: #selector(remove(_:)), name: NSNotification.Name("UserDidAddLink"), object: nil) - } - - @objc func remove(_ notification: Notification) { - guard let url = notification.userInfo?["toRemoveFromCache"] as? String else { return } - externalCache.removeValue(forKey: url) - } - - lazy var externalCache: [String:ExtensionsAddLinkRequests] = { - getCache() - }() - - func getCache() -> [String:ExtensionsAddLinkRequests] { - let decoder = PropertyListDecoder() - var cache = [String:ExtensionsAddLinkRequests]() - if let data = try? Data(contentsOf: Self.filePathUrl), - let requests = try? decoder.decode([ExtensionsAddLinkRequests].self, from: data) { - for req in requests { - cache[req.url] = req - } - } - return cache - } - - func dropCache() { - externalCache = [String:ExtensionsAddLinkRequests]() + private var defaults: UserDefaults? { + UserDefaults(suiteName: Self.suite) } - func persistCache() { - let encoder = PropertyListEncoder() - encoder.outputFormat = .binary - - coordinateFileWrite { url in - do { - let data = try encoder.encode(externalCache) - try data.write(to: url) - } catch { - logger.error("Save to disk failed: \(error.localizedDescription, privacy: .public)") - } - } + var canSaveMoreLinks: Bool { + pendingLinks.count < 15 } - /// Coordinates file access - private func coordinateFileWrite(run accessor: (URL) throws -> Void) { - let errorPointer: NSErrorPointer = nil - let fileCoordinator = NSFileCoordinator() - let fileUrl = ExtensionsAddLinkRequestsManager.filePathUrl - - fileCoordinator.coordinate(writingItemAt: fileUrl, options: [.forMerging], error: errorPointer, byAccessor: { url in - do { - try accessor(url) - } catch { - logger.error("Save to disk failed: \(error.localizedDescription, privacy: .public)") - } - }) - - if let error = errorPointer?.pointee { - logger.error("Save to disk coordination failed: \(error.localizedDescription, privacy: .public)") - } + var pendingLinks: [ExtensionsAddLinkRequests] { + guard + let data = defaults?.data(forKey: Self.key), + let links = try? JSONDecoder().decode([ExtensionsAddLinkRequests].self, from: data) + else { return [] } + return links } - /// Stores given `urlString` and `note` to external file + /// Stores given `urlString` and `note` to UserDefaults func add(_ urlString: String, note: String?) { logger.info("saving \(urlString) with note \(note ?? "")") - let decoder = PropertyListDecoder() - let encoder = PropertyListEncoder() - encoder.outputFormat = .binary - - coordinateFileWrite { url in - var requests: [ExtensionsAddLinkRequests] - if - let fileData = try? Data(contentsOf: url), - let decodedRequests = try? decoder.decode([ExtensionsAddLinkRequests].self, from: fileData) - { - requests = decodedRequests - } else { - requests = [ExtensionsAddLinkRequests]() - } - - requests.append(ExtensionsAddLinkRequests(url: urlString, note: note)) - - let data = try encoder.encode(requests) - try data.write(to: url) + var links = pendingLinks + links.append(ExtensionsAddLinkRequests(url: urlString, note: note)) + if let data = try? JSONEncoder().encode(links) { + defaults?.set(data, forKey: Self.key) } } + + func clearAll() { + defaults?.removeObject(forKey: Self.key) + } } diff --git a/Shared/Utils/+URL.swift b/Shared/Utils/+URL.swift index e1b744e..53d4a4c 100644 --- a/Shared/Utils/+URL.swift +++ b/Shared/Utils/+URL.swift @@ -18,7 +18,7 @@ public extension URL { var path: URL = fileContainer if let folders = folders { for folder in folders { - path = path.appendingPathExtension(folder) + path = path.appendingPathComponent(folder) } } return path.appendingPathComponent(filename) diff --git a/Ulry/AddToUlryAction/AddToUlryActionViewController.swift b/Ulry/AddToUlryAction/AddToUlryActionViewController.swift index 2b2c3d1..c1448de 100644 --- a/Ulry/AddToUlryAction/AddToUlryActionViewController.swift +++ b/Ulry/AddToUlryAction/AddToUlryActionViewController.swift @@ -40,13 +40,19 @@ class ActionViewController: UIViewController { navigationItem.title = "Action" - let extensionItem = extensionContext?.inputItems.first as! NSExtensionItem - let itemProvider = (extensionItem.attachments?.first)! as NSItemProvider - + guard + let extensionItem = extensionContext?.inputItems.first as? NSExtensionItem, + let itemProvider = extensionItem.attachments?.first + else { + os_log(.error, "Encountered error while trying to insert link from action: missing extension item") + extensionContext?.completeRequest(returningItems: [], completionHandler: nil) + return + } + let propertyList = String(describing: UTType.propertyList) if itemProvider.hasItemConformingToTypeIdentifier(propertyList) { itemProvider.loadItem(forTypeIdentifier: propertyList, options: nil) { item, error in - let dictionary = item as! NSDictionary + guard let dictionary = item as? NSDictionary else { return } OperationQueue.main.addOperation { [weak self] in if let results = dictionary[NSExtensionJavaScriptPreprocessingResultsKey] as? NSDictionary { self?.handleData(dict: results) diff --git a/Ulry/SceneDelegate.swift b/Ulry/SceneDelegate.swift index 6ea0adb..ee62e93 100644 --- a/Ulry/SceneDelegate.swift +++ b/Ulry/SceneDelegate.swift @@ -44,6 +44,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { NotificationCenter.default.addObserver(self, selector: #selector(processExternalLinks), name: UIApplication.willEnterForegroundNotification, object: nil) self.window = window + + processExternalLinks() } func sceneDidDisconnect(_ scene: UIScene) { @@ -77,13 +79,14 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } @objc private func processExternalLinks() { - guard addLinkRequestManger.getCache().count > 0 else { return } - os_log(.info, "moving \(self.addLinkRequestManger.externalCache.count) links from external file") - let links = addLinkRequestManger.externalCache.values.map { Link(url: $0.url, note: $0.note) } - + let pending = addLinkRequestManger.pendingLinks + guard !pending.isEmpty else { return } + os_log(.info, "moving \(pending.count) links from external file") + let links = pending.map { Link(url: $0.url, note: $0.note) } + Task { await account.insertBatch(links: links) - addLinkRequestManger.persistCache() + addLinkRequestManger.clearAll() } } }