// 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--.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(_ 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) } }