import Foundation struct PodcastEntry: Codable, Identifiable { var id: String { articleUrl } let articleUrl: String let filename: String var title: String? let createdAt: Date } enum PodcastIndex { private static let indexURL: URL = { let dir = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0] .appendingPathComponent("podcasts") return dir.appendingPathComponent("index.json") }() private static let encoder: JSONEncoder = { let e = JSONEncoder() e.dateEncodingStrategy = .iso8601 e.outputFormatting = .prettyPrinted return e }() private static let decoder: JSONDecoder = { let d = JSONDecoder() d.dateDecodingStrategy = .iso8601 return d }() static func all() -> [PodcastEntry] { guard let data = try? Data(contentsOf: indexURL), let entries = try? decoder.decode([PodcastEntry].self, from: data) else { return [] } return entries.sorted { $0.createdAt > $1.createdAt } } static func upsert(articleUrl: String, filename: String, title: String?) { var entries = all() entries.removeAll { $0.articleUrl == articleUrl } entries.insert(PodcastEntry(articleUrl: articleUrl, filename: filename, title: title, createdAt: Date()), at: 0) save(entries) } static func remove(articleUrl: String) { var entries = all() entries.removeAll { $0.articleUrl == articleUrl } save(entries) } private static func save(_ entries: [PodcastEntry]) { guard let data = try? encoder.encode(entries) else { return } try? data.write(to: indexURL, options: .atomic) } }