Sesame-iOS/Sesame/Common/HistoryManager.swift

146 lines
4.0 KiB
Swift
Raw Normal View History

import Foundation
import CBORCoding
2023-12-12 17:33:42 +01:00
/*
2023-08-14 10:39:29 +02:00
class HistoryManagerBase: ObservableObject {
@Published
var entries: [HistoryItem] = []
}
protocol HistoryManagerProtocol: HistoryManagerBase {
var entries: [HistoryItem] { get }
func save(item: HistoryItem) throws
2023-08-14 10:39:29 +02:00
func delete(item: HistoryItem) -> Bool
}
2023-08-14 10:39:29 +02:00
final class HistoryManager: HistoryManagerBase, HistoryManagerProtocol {
private let encoder = CBOREncoder(dateEncodingStrategy: .secondsSince1970)
private var fm: FileManager {
.default
}
static var documentDirectory: URL {
try! FileManager.default.url(
for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil, create: true)
}
private let fileUrl: URL
2023-08-14 10:39:29 +02:00
override init() {
self.fileUrl = HistoryManager.documentDirectory.appendingPathComponent("history2.bin")
2023-08-14 10:39:29 +02:00
super.init()
Task {
print("Loading history...")
let all = loadEntries()
DispatchQueue.main.async {
self.entries = all
print("History loaded (\(self.entries.count) entries)")
}
}
}
2023-08-14 10:39:29 +02:00
private func loadEntries() -> [HistoryItem] {
guard fm.fileExists(atPath: fileUrl.path) else {
print("No history data found")
return []
}
let content: Data
do {
content = try Data(contentsOf: fileUrl)
} catch {
print("Failed to read history data: \(error)")
return []
}
let decoder = CBORDecoder()
var index = 0
var entries = [HistoryItem]()
while index < content.count {
let length = Int(content[index])
index += 1
if index + length > content.count {
print("Missing bytes in history file: needed \(length), has only \(content.count - index)")
return entries
}
let entryData = content[index..<index+length]
index += length
do {
let entry: HistoryItem = try decoder.decode(from: entryData)
entries.append(entry)
} catch {
print("Failed to decode history (index: \(index), length \(length)): \(error)")
return entries
}
}
return entries.sorted().reversed()
}
func save(item: HistoryItem) throws {
2023-08-14 10:39:29 +02:00
let data = try convertForStorage(item)
guard fm.fileExists(atPath: fileUrl.path) else {
try data.write(to: fileUrl)
print("First history item written (\(data[0]))")
return
}
let handle = try FileHandle(forWritingTo: fileUrl)
try handle.seekToEnd()
try handle.write(contentsOf: data)
try handle.close()
print("History item written (\(data[0]))")
}
2023-08-14 10:39:29 +02:00
@discardableResult
func delete(item: HistoryItem) -> Bool {
let newItems = entries
.filter { $0 != item }
let data: FlattenSequence<[Data]>
do {
data = try newItems
.map(convertForStorage)
.joined()
} catch {
print("Failed to encode items: \(error)")
return false
}
do {
try Data(data).write(to: fileUrl)
} catch {
print("Failed to save items: \(error)")
return false
}
entries = newItems
return true
}
private func convertForStorage(_ item: HistoryItem) throws -> Data {
let entryData = try encoder.encode(item)
return Data([UInt8(entryData.count)]) + entryData
}
}
2023-08-14 10:39:29 +02:00
final class HistoryManagerMock: HistoryManagerBase, HistoryManagerProtocol {
override init() {
super.init()
self.entries = [.mock]
}
func save(item: HistoryItem) throws {
2023-08-14 10:39:29 +02:00
entries.append(item)
}
func delete(item: HistoryItem) -> Bool {
entries = entries.filter { $0 != item }
return true
}
}
2023-12-12 17:33:42 +01:00
*/