Files
nucleic-remote-ios/NucleicRemote/NucleicRemote/Models/CastCache.swift
T

48 lines
2.3 KiB
Swift

import Foundation
import NucleicProtocol
/// The on-device snapshot of the phone's mesh-cast ledger (`CastLedger`) — the activity feed,
/// mesh messages, and runner-presence cards it has received, plus (implicitly) the cursors a
/// reconnect subscribes with. Like `SessionCache`, a *projection cache*, never canonical: a
/// live host always supersedes it; it exists so a relaunch renders the feed instantly and
/// resubscribes O(gap) instead of re-pulling every channel from zero.
///
/// One JSON file under Application Support (`NucleicCastCache/casts.json`) — the ledger's
/// per-channel retention already bounds it. Reads/writes hop onto a utility `Task.detached`.
enum CastCache {
private static var root: URL? {
guard let base = try? FileManager.default.url(
for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
else { return nil }
return base.appendingPathComponent("NucleicCastCache", isDirectory: true)
}
private static var castsURL: URL? { root?.appendingPathComponent("casts.json") }
/// Load the persisted ledger — called once at launch, before any host connects. The ledger's
/// init re-applies retention, so a policy change between builds re-caps on load.
static func loadLedger() -> CastLedger {
guard let url = castsURL, let data = try? Data(contentsOf: url),
let casts = try? JSONDecoder().decode([WireCast].self, from: data)
else { return CastLedger() }
return CastLedger(casts: casts)
}
/// Persist the ledger's snapshot. Off the main actor; callers debounce bursts.
static func save(_ ledger: CastLedger) async {
let casts = ledger.allCasts
await Task.detached(priority: .utility) {
guard let root, let url = castsURL else { return }
try? FileManager.default.createDirectory(at: root, withIntermediateDirectories: true)
if let data = try? JSONEncoder().encode(casts) {
try? data.write(to: url, options: .atomic)
}
}.value
}
/// Drop the cache — on full unpair (no Mac left whose casts this device should hold).
static func clear() {
guard let root else { return }
try? FileManager.default.removeItem(at: root)
}
}