HistoryManager.swift

2.6 KB · raw

1import Foundation23class HistoryManager: ObservableObject {4  static let shared = HistoryManager()56  @Published var history: [Wallpaper] = []7  /// Latest persistence failure, surfaced so callers can react. The8  /// in-memory history still updates when persistence fails — the manager9  /// no longer hides corruption or unwritable storage behind `try?`.10  @Published var persistenceError: String?1112  private let key = "wallpaperHistory"13  private let maxHistoryItems: Int14  private let defaults: UserDefaults15  private let encoder: JSONEncoder16  private let decoder: JSONDecoder1718  init(19    defaults: UserDefaults = .standard,20    encoder: JSONEncoder = JSONEncoder(),21    decoder: JSONDecoder = JSONDecoder(),22    maxHistoryItems: Int = 5023  ) {24    self.defaults = defaults25    self.encoder = encoder26    self.decoder = decoder27    self.maxHistoryItems = maxHistoryItems28    loadHistory()29  }3031  func addToHistory(_ wallpaper: Wallpaper) {32    // Remove if existing to promote to top33    if let index = history.firstIndex(where: { $0.id == wallpaper.id }) {34      history.remove(at: index)35    }3637    history.insert(wallpaper, at: 0)3839    if history.count > maxHistoryItems {40      history = Array(history.prefix(maxHistoryItems))41    }4243    persist(history)44  }4546  func clearHistory() {47    history = []48    persist(history)49  }5051  /// Public for diagnostics/testing. Encodes the supplied list through the52  /// configured encoder, writes through the configured `UserDefaults`,53  /// and records the first failure through `persistenceError`. Keeping the54  /// encoder/userdefaults injectable lets us assert failure paths in tests55  /// without hijacking the shared `UserDefaults.standard` instance.56  func persist(_ entries: [Wallpaper]) {57    do {58      let data = try encoder.encode(entries)59      defaults.set(data, forKey: key)60      persistenceError = nil61    } catch {62      persistenceError = "Failed to save history: \(error.localizedDescription)"63    }64  }6566  /// Decodes the stored history. Public for testability. Returns nil when67  /// the stored data is missing or undecodable and records the failure68  /// reason through `persistenceError` so the app can react.69  func decodeHistory(from data: Data?) -> [Wallpaper]? {70    guard let data else {71      persistenceError = nil72      return nil73    }74    do {75      let decoded = try decoder.decode([Wallpaper].self, from: data)76      persistenceError = nil77      return decoded78    } catch {79      persistenceError = "Failed to load history: \(error.localizedDescription)"80      return nil81    }82  }8384  private func loadHistory() {85    let stored = defaults.data(forKey: key)86    history = decodeHistory(from: stored) ?? history87  }88}