Files
NetNewsWire/Core/Sources/Core/ManagedResourceFile.swift

128 lines
2.6 KiB
Swift

//
// ManagedResourceFile.swift
// RSCore
//
// Created by Maurice Parker on 9/13/19.
// Copyright © 2020 Ranchero Software, LLC. All rights reserved.
//
import Foundation
public final class ManagedResourceFile: NSObject, NSFilePresenter {
private var isDirty = false {
didSet {
queueSaveToDiskIfNeeded()
}
}
private var isLoading = false
private let fileURL: URL
private let operationQueue: OperationQueue
private var saveQueue: CoalescingQueue
private let loadCallback: () -> Void
private let saveCallback: () -> Void
public var saveInterval: TimeInterval = 5.0 {
didSet {
saveQueue.performCallsImmediately()
saveQueue = CoalescingQueue(name: "ManagedResourceFile Save Queue", interval: saveInterval)
}
}
public var presentedItemURL: URL? {
return fileURL
}
public var presentedItemOperationQueue: OperationQueue {
return operationQueue
}
public init(fileURL: URL, load: @escaping () -> Void, save: @escaping () -> Void) {
self.fileURL = fileURL
self.loadCallback = load
self.saveCallback = save
saveQueue = CoalescingQueue(name: "ManagedResourceFile Save Queue", interval: saveInterval)
operationQueue = OperationQueue()
operationQueue.qualityOfService = .userInteractive
operationQueue.maxConcurrentOperationCount = 1
super.init()
NSFileCoordinator.addFilePresenter(self)
}
public func presentedItemDidChange() {
guard !isDirty else { return }
DispatchQueue.main.async {
self.load()
}
}
public func savePresentedItemChanges(completionHandler: @escaping (Error?) -> Void) {
saveIfNecessary()
completionHandler(nil)
}
public func relinquishPresentedItem(toReader reader: @escaping ((() -> Void)?) -> Void) {
saveQueue.isPaused = true
reader() {
self.saveQueue.isPaused = false
}
}
public func relinquishPresentedItem(toWriter writer: @escaping ((() -> Void)?) -> Void) {
saveQueue.isPaused = true
writer() {
self.saveQueue.isPaused = false
}
}
public func markAsDirty() {
if !isLoading {
isDirty = true
}
}
public func queueSaveToDiskIfNeeded() {
saveQueue.add(self, #selector(saveToDiskIfNeeded))
}
public func load() {
isLoading = true
loadCallback()
isLoading = false
}
public func saveIfNecessary() {
saveQueue.performCallsImmediately()
}
public func resume() {
NSFileCoordinator.addFilePresenter(self)
}
public func suspend() {
NSFileCoordinator.removeFilePresenter(self)
}
deinit {
NSFileCoordinator.removeFilePresenter(self)
}
}
private extension ManagedResourceFile {
@objc func saveToDiskIfNeeded() {
if isDirty {
isDirty = false
saveCallback()
}
}
}