Create and use DataFile class. Start getting away from CoalescingQueue, since it’s very objc.
This commit is contained in:
parent
e74c81518e
commit
9c23b1351d
@ -18,21 +18,19 @@ import Core
|
||||
|
||||
private let fileURL: URL
|
||||
private let account: Account
|
||||
|
||||
private var isDirty = false {
|
||||
didSet {
|
||||
queueSaveToDiskIfNeeded()
|
||||
}
|
||||
}
|
||||
private let saveQueue = CoalescingQueue(name: "Save Queue", interval: 0.5)
|
||||
private let dataFile: DataFile
|
||||
|
||||
init(filename: String, account: Account) {
|
||||
self.fileURL = URL(fileURLWithPath: filename)
|
||||
|
||||
self.account = account
|
||||
self.fileURL = URL(fileURLWithPath: filename)
|
||||
self.dataFile = DataFile(fileURL: self.fileURL)
|
||||
|
||||
self.dataFile.delegate = self
|
||||
}
|
||||
|
||||
func markAsDirty() {
|
||||
isDirty = true
|
||||
dataFile.markAsDirty()
|
||||
}
|
||||
|
||||
func load() {
|
||||
@ -44,33 +42,15 @@ import Core
|
||||
account.loadOPMLItems(opmlItems)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func save() {
|
||||
guard !account.isDeleted else { return }
|
||||
let opmlDocumentString = opmlDocument()
|
||||
|
||||
do {
|
||||
try opmlDocumentString.write(to: fileURL, atomically: true, encoding: .utf8)
|
||||
} catch let error as NSError {
|
||||
os_log(.error, log: log, "OPML save to disk failed: %@.", error.localizedDescription)
|
||||
}
|
||||
|
||||
dataFile.save()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension OPMLFile {
|
||||
|
||||
func queueSaveToDiskIfNeeded() {
|
||||
saveQueue.add(self, #selector(saveToDiskIfNeeded))
|
||||
}
|
||||
|
||||
@objc func saveToDiskIfNeeded() {
|
||||
if isDirty {
|
||||
isDirty = false
|
||||
save()
|
||||
}
|
||||
}
|
||||
|
||||
func opmlFileData() -> Data? {
|
||||
var fileData: Data? = nil
|
||||
|
||||
@ -122,5 +102,29 @@ private extension OPMLFile {
|
||||
let opml = openingText + middleText + closingText
|
||||
return opml
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension OPMLFile: DataFileDelegate {
|
||||
|
||||
func data(for dataFile: DataFile) -> Data? {
|
||||
|
||||
guard !account.isDeleted else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let opmlDocumentString = opmlDocument()
|
||||
guard let data = opmlDocumentString.data(using: .utf8, allowLossyConversion: true) else {
|
||||
|
||||
assertionFailure("OPML String conversion to Data failed.")
|
||||
os_log(.error, log: log, "OPML String conversion to Data failed.")
|
||||
return nil
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
func dataFileWriteToDiskDidFail(for dataFile: DataFile, error: Error) {
|
||||
|
||||
os_log(.error, log: log, "OPML save to disk failed: %@.", error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
96
Core/Sources/Core/DataFile.swift
Normal file
96
Core/Sources/Core/DataFile.swift
Normal file
@ -0,0 +1,96 @@
|
||||
//
|
||||
// DataFile.swift
|
||||
//
|
||||
//
|
||||
// Created by Brent Simmons on 6/9/24.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import os
|
||||
|
||||
public protocol DataFileDelegate: AnyObject {
|
||||
|
||||
@MainActor func data(for dataFile: DataFile) -> Data?
|
||||
@MainActor func dataFileWriteToDiskDidFail(for dataFile: DataFile, error: Error)
|
||||
}
|
||||
|
||||
@MainActor public final class DataFile {
|
||||
|
||||
public weak var delegate: DataFileDelegate? = nil
|
||||
|
||||
private var isDirty = false {
|
||||
didSet {
|
||||
if isDirty {
|
||||
restartTimer()
|
||||
}
|
||||
else {
|
||||
invalidateTimer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let fileURL: URL
|
||||
private let saveInterval: TimeInterval = 1.0
|
||||
private var timer: Timer?
|
||||
|
||||
public init(fileURL: URL) {
|
||||
|
||||
self.fileURL = fileURL
|
||||
}
|
||||
|
||||
public func markAsDirty() {
|
||||
|
||||
isDirty = true
|
||||
}
|
||||
|
||||
public func save() {
|
||||
|
||||
assert(Thread.isMainThread)
|
||||
isDirty = false
|
||||
|
||||
guard let data = delegate?.data(for: self) else {
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
try data.write(to: fileURL)
|
||||
} catch {
|
||||
delegate?.dataFileWriteToDiskDidFail(for: self, error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension DataFile {
|
||||
|
||||
func saveToDiskIfNeeded() {
|
||||
|
||||
assert(Thread.isMainThread)
|
||||
|
||||
if isDirty {
|
||||
save()
|
||||
}
|
||||
}
|
||||
|
||||
func restartTimer() {
|
||||
|
||||
assert(Thread.isMainThread)
|
||||
|
||||
invalidateTimer()
|
||||
|
||||
timer = Timer.scheduledTimer(withTimeInterval: saveInterval, repeats: false) { timer in
|
||||
MainActor.assumeIsolated {
|
||||
self.saveToDiskIfNeeded()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func invalidateTimer() {
|
||||
|
||||
assert(Thread.isMainThread)
|
||||
|
||||
if let timer, timer.isValid {
|
||||
timer.invalidate()
|
||||
}
|
||||
timer = nil
|
||||
}
|
||||
}
|
@ -15,7 +15,6 @@ let package = Package(
|
||||
.target(
|
||||
name: "Web",
|
||||
dependencies: [],
|
||||
resources: [.copy("UTS46/uts46")],
|
||||
swiftSettings: [
|
||||
.define("SWIFT_PACKAGE"),
|
||||
.enableExperimentalFeature("StrictConcurrency")
|
||||
|
Loading…
x
Reference in New Issue
Block a user