MoneyCounterApp/MoneyCounter/MoneyCounterViewModel.swift

105 lines
3.6 KiB
Swift
Raw Permalink Normal View History

2026-04-06 01:25:41 -07:00
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")
}
}