Files
wristcode/watch/WristCode/FeedCache.swift
T
gib edc4b88990
Gateway / check (push) Successful in 9s
Gateway / image (push) Successful in 22s
Watch: snooze finished threads, name the files a file approval would edit, Settle on finished notifications, an instant launch from the saved feed, plain Loading states, and no more cancelled errors
Gateway: an approval push for a file change says which files it would edit
2026-11-02 14:45:34 -05:00

50 lines
1.9 KiB
Swift

// The last good feed, and the last detail of each thread opened, saved as JSON in the app's caches
// folder. Launch shows them at once while the first refresh runs. Signing out clears them, and demo
// mode never reads or writes them.
import Foundation
struct FeedCache: Sendable {
let directory: URL
static let standard = FeedCache(directory: .cachesDirectory.appending(path: "WristCode", directoryHint: .isDirectory))
func feed() -> Feed? {
read(Feed.self, "feed.json")
}
func save(_ feed: Feed) {
write(feed, "feed.json")
}
func thread(hostId: String, threadId: String) -> ThreadDetail? {
read(ThreadDetail.self, Self.threadFile(hostId, threadId))
}
func save(_ detail: ThreadDetail) {
write(detail, Self.threadFile(detail.item.hostId, detail.item.threadId))
}
/// Deletes everything saved, as when the watch unpairs.
func clear() {
try? FileManager.default.removeItem(at: directory)
}
/// `thread-<hostId>-<threadId>.json`, with the ids percent-encoded so any id is a safe file name.
private static func threadFile(_ hostId: String, _ threadId: String) -> String {
let safe = { (id: String) in id.addingPercentEncoding(withAllowedCharacters: .alphanumerics) ?? id }
return "thread-\(safe(hostId))-\(safe(threadId)).json"
}
private func read<Value: Decodable>(_ type: Value.Type, _ name: String) -> Value? {
guard let data = try? Data(contentsOf: directory.appending(path: name)) else { return nil }
return try? JSON.decoder().decode(type, from: data)
}
private func write(_ value: some Encodable, _ name: String) {
guard let data = try? JSON.encoder().encode(value) else { return }
try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
try? data.write(to: directory.appending(path: name), options: .atomic)
}
}