Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 22 additions & 85 deletions Shared/SharedExtension/ExtensionsAddLinkRequestsManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
2 changes: 1 addition & 1 deletion Shared/Utils/+URL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
14 changes: 10 additions & 4 deletions Ulry/AddToUlryAction/AddToUlryActionViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
13 changes: 8 additions & 5 deletions Ulry/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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()
}
}
}
Expand Down