NetNewsWire/Shared/ShareExtension/ExtensionFeedAddRequestFile...

168 lines
4.6 KiB
Swift

//
// ExtensionFeedAddRequestFile.swift
// NetNewsWire-iOS
//
// Created by Maurice Parker on 2/11/20.
// Copyright © 2020 Ranchero Software. All rights reserved.
//
import Foundation
import os.log
import Account
final class ExtensionFeedAddRequestFile: NSObject, NSFilePresenter, Sendable {
private static let log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "extensionFeedAddRequestFile")
private static let filePath: String = {
let appGroup = Bundle.main.object(forInfoDictionaryKey: "AppGroup") as! String
let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroup)
return containerURL!.appendingPathComponent("extension_feed_add_request.plist").path
}()
private let operationQueue: OperationQueue
var presentedItemURL: URL? {
return URL(fileURLWithPath: ExtensionFeedAddRequestFile.filePath)
}
var presentedItemOperationQueue: OperationQueue {
return operationQueue
}
@MainActor override init() {
operationQueue = OperationQueue()
operationQueue.maxConcurrentOperationCount = 1
super.init()
NSFileCoordinator.addFilePresenter(self)
process()
}
func presentedItemDidChange() {
Task { @MainActor in
self.process()
}
}
func resume() {
NSFileCoordinator.addFilePresenter(self)
Task { @MainActor in
process()
}
}
func suspend() {
NSFileCoordinator.removeFilePresenter(self)
}
static func save(_ feedAddRequest: ExtensionFeedAddRequest) {
let decoder = PropertyListDecoder()
let encoder = PropertyListEncoder()
encoder.outputFormat = .binary
let errorPointer: NSErrorPointer = nil
let fileCoordinator = NSFileCoordinator()
let fileURL = URL(fileURLWithPath: ExtensionFeedAddRequestFile.filePath)
fileCoordinator.coordinate(writingItemAt: fileURL, options: [.forMerging], error: errorPointer, byAccessor: { url in
do {
var requests: [ExtensionFeedAddRequest]
if let fileData = try? Data(contentsOf: url),
let decodedRequests = try? decoder.decode([ExtensionFeedAddRequest].self, from: fileData) {
requests = decodedRequests
} else {
requests = [ExtensionFeedAddRequest]()
}
requests.append(feedAddRequest)
let data = try encoder.encode(requests)
try data.write(to: url)
} catch let error as NSError {
os_log(.error, log: Self.log, "Save to disk failed: %@.", error.localizedDescription)
}
})
if let error = errorPointer?.pointee {
os_log(.error, log: Self.log, "Save to disk coordination failed: %@.", error.localizedDescription)
}
}
}
private extension ExtensionFeedAddRequestFile {
@MainActor func process() {
let decoder = PropertyListDecoder()
let encoder = PropertyListEncoder()
encoder.outputFormat = .binary
let errorPointer: NSErrorPointer = nil
let fileCoordinator = NSFileCoordinator(filePresenter: self)
let fileURL = URL(fileURLWithPath: ExtensionFeedAddRequestFile.filePath)
var requests: [ExtensionFeedAddRequest]? = nil
fileCoordinator.coordinate(writingItemAt: fileURL, options: [.forMerging], error: errorPointer, byAccessor: { url in
do {
if let fileData = try? Data(contentsOf: url),
let decodedRequests = try? decoder.decode([ExtensionFeedAddRequest].self, from: fileData) {
requests = decodedRequests
}
let data = try encoder.encode([ExtensionFeedAddRequest]())
try data.write(to: url)
} catch let error as NSError {
os_log(.error, log: Self.log, "Save to disk failed: %@.", error.localizedDescription)
}
})
if let error = errorPointer?.pointee {
os_log(.error, log: Self.log, "Save to disk coordination failed: %@.", error.localizedDescription)
}
if let requests {
for request in requests {
processRequest(request)
}
}
}
@MainActor func processRequest(_ request: ExtensionFeedAddRequest) {
var destinationAccountID: String? = nil
switch request.destinationContainerID {
case .account(let accountID):
destinationAccountID = accountID
case .folder(let accountID, _):
destinationAccountID = accountID
default:
break
}
guard let accountID = destinationAccountID, let account = AccountManager.shared.existingAccount(with: accountID) else {
return
}
var destinationContainer: Container? = nil
if account.containerID == request.destinationContainerID {
destinationContainer = account
} else {
destinationContainer = account.folders?.first(where: { $0.containerID == request.destinationContainerID })
}
guard let container = destinationContainer else { return }
Task { @MainActor in
try? await account.createFeed(url: request.feedURL.absoluteString, name: request.name, container: container, validateFeed: true)
}
}
}