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 fileURL: URL
|
||||||
private let account: Account
|
private let account: Account
|
||||||
|
private let dataFile: DataFile
|
||||||
private var isDirty = false {
|
|
||||||
didSet {
|
|
||||||
queueSaveToDiskIfNeeded()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private let saveQueue = CoalescingQueue(name: "Save Queue", interval: 0.5)
|
|
||||||
|
|
||||||
init(filename: String, account: Account) {
|
init(filename: String, account: Account) {
|
||||||
self.fileURL = URL(fileURLWithPath: filename)
|
|
||||||
self.account = account
|
self.account = account
|
||||||
|
self.fileURL = URL(fileURLWithPath: filename)
|
||||||
|
self.dataFile = DataFile(fileURL: self.fileURL)
|
||||||
|
|
||||||
|
self.dataFile.delegate = self
|
||||||
}
|
}
|
||||||
|
|
||||||
func markAsDirty() {
|
func markAsDirty() {
|
||||||
isDirty = true
|
dataFile.markAsDirty()
|
||||||
}
|
}
|
||||||
|
|
||||||
func load() {
|
func load() {
|
||||||
@ -46,31 +44,13 @@ import Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
func save() {
|
func save() {
|
||||||
guard !account.isDeleted else { return }
|
|
||||||
let opmlDocumentString = opmlDocument()
|
|
||||||
|
|
||||||
do {
|
dataFile.save()
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension OPMLFile {
|
private extension OPMLFile {
|
||||||
|
|
||||||
func queueSaveToDiskIfNeeded() {
|
|
||||||
saveQueue.add(self, #selector(saveToDiskIfNeeded))
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func saveToDiskIfNeeded() {
|
|
||||||
if isDirty {
|
|
||||||
isDirty = false
|
|
||||||
save()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func opmlFileData() -> Data? {
|
func opmlFileData() -> Data? {
|
||||||
var fileData: Data? = nil
|
var fileData: Data? = nil
|
||||||
|
|
||||||
@ -122,5 +102,29 @@ private extension OPMLFile {
|
|||||||
let opml = openingText + middleText + closingText
|
let opml = openingText + middleText + closingText
|
||||||
return opml
|
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(
|
.target(
|
||||||
name: "Web",
|
name: "Web",
|
||||||
dependencies: [],
|
dependencies: [],
|
||||||
resources: [.copy("UTS46/uts46")],
|
|
||||||
swiftSettings: [
|
swiftSettings: [
|
||||||
.define("SWIFT_PACKAGE"),
|
.define("SWIFT_PACKAGE"),
|
||||||
.enableExperimentalFeature("StrictConcurrency")
|
.enableExperimentalFeature("StrictConcurrency")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user