import SwiftUI import Observation @Observable class MoneyCounterViewModel { struct Denomination: Identifiable, Codable, Equatable { var id: UUID = UUID() let value: Double var count: Int = 0 var isRoll: Bool = false } var looseDenominations: [Denomination] = [ Denomination(value: 0.05), Denomination(value: 0.10), Denomination(value: 0.25), Denomination(value: 1.00), Denomination(value: 2.00), Denomination(value: 5.00), Denomination(value: 10.00), Denomination(value: 20.00), Denomination(value: 50.00), Denomination(value: 100.00) ] var rollDenominations: [Denomination] = [ Denomination(value: 2.00, isRoll: true), Denomination(value: 5.00, isRoll: true), Denomination(value: 10.00, isRoll: true), Denomination(value: 25.00, isRoll: true), Denomination(value: 50.00, isRoll: true) ] var startingFloat: Double = 0.0 init() { loadCache() } var total: Double { let looseTotal = looseDenominations.reduce(0) { $0 + ($1.value * Double($1.count)) } let rollsTotal = rollDenominations.reduce(0) { $0 + ($1.value * Double($1.count)) } return looseTotal + rollsTotal } var discrepancy: Double { total - startingFloat } func reset() { for i in looseDenominations.indices { looseDenominations[i].count = 0 } for i in rollDenominations.indices { rollDenominations[i].count = 0 } // The float is intentionally NOT reset here anymore so it persists saveCache() } func generateSnapshot() -> [CountSnapshot] { let loose = looseDenominations.map { CountSnapshot(id: $0.id, value: $0.value, count: $0.count, isRoll: $0.isRoll) } let rolls = rollDenominations.map { CountSnapshot(id: $0.id, value: $0.value, count: $0.count, isRoll: $0.isRoll) } return loose + rolls } func restore(from snapshot: [CountSnapshot], float: Double) { startingFloat = float for snap in snapshot { if snap.isRoll { if let index = rollDenominations.firstIndex(where: { $0.value == snap.value }) { rollDenominations[index].count = snap.count } } else { if let index = looseDenominations.firstIndex(where: { $0.value == snap.value }) { looseDenominations[index].count = snap.count } } } saveCache() } // MARK: - Caching Logic func saveCache() { if let looseData = try? JSONEncoder().encode(looseDenominations) { UserDefaults.standard.set(looseData, forKey: "cachedLoose") } if let rollData = try? JSONEncoder().encode(rollDenominations) { UserDefaults.standard.set(rollData, forKey: "cachedRolls") } UserDefaults.standard.set(startingFloat, forKey: "cachedFloat") } private func loadCache() { if let looseData = UserDefaults.standard.data(forKey: "cachedLoose"), let decodedLoose = try? JSONDecoder().decode([Denomination].self, from: looseData) { looseDenominations = decodedLoose } if let rollData = UserDefaults.standard.data(forKey: "cachedRolls"), let decodedRolls = try? JSONDecoder().decode([Denomination].self, from: rollData) { rollDenominations = decodedRolls } startingFloat = UserDefaults.standard.double(forKey: "cachedFloat") } }