summaryrefslogtreecommitdiff
path: root/DomainDig/UsageCreditService.swift
blob: 48f40be37bca5396be2ddbc8d8538c9d9b4c5a5e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import Foundation

actor UsageCreditService {
    static let shared = UsageCreditService()

    private struct CreditLedger: Codable {
        let appVersion: String
        var remainingByFeature: [UsageCreditFeature: Int]
    }

    private let storageKey = "usageCredits.ledger"
    private var ledger: CreditLedger

    init(defaults: UserDefaults = .standard) {
        if let data = defaults.data(forKey: storageKey),
           let decoded = try? JSONDecoder().decode(CreditLedger.self, from: data),
           decoded.appVersion == AppVersion.current {
            ledger = decoded
        } else {
            ledger = Self.makeLedger()
            if let data = try? JSONEncoder().encode(ledger) {
                defaults.set(data, forKey: storageKey)
            }
        }
    }

    func status(for feature: UsageCreditFeature) -> UsageCreditStatus {
        let total = feature.defaultAllowance
        let remaining = ledger.remainingByFeature[feature] ?? total
        return UsageCreditStatus(
            feature: feature,
            remaining: remaining,
            total: total,
            resetContext: "Resets with app version \(ledger.appVersion)"
        )
    }

    func allStatuses() -> [UsageCreditStatus] {
        UsageCreditFeature.allCases.map { status(for: $0) }
    }

    func canUse(_ feature: UsageCreditFeature) -> Bool {
        status(for: feature).remaining > 0
    }

    @discardableResult
    func consume(_ feature: UsageCreditFeature) -> UsageCreditStatus {
        let current = ledger.remainingByFeature[feature] ?? feature.defaultAllowance
        ledger.remainingByFeature[feature] = max(0, current - 1)
        persist()
        return status(for: feature)
    }

    func resetForCurrentVersion() {
        ledger = Self.makeLedger()
        persist()
    }

    private static func makeLedger() -> CreditLedger {
        CreditLedger(
            appVersion: AppVersion.current,
            remainingByFeature: Dictionary(
                uniqueKeysWithValues: UsageCreditFeature.allCases.map { ($0, $0.defaultAllowance) }
            )
        )
    }

    private func persist(defaults: UserDefaults = .standard) {
        if let data = try? JSONEncoder().encode(ledger) {
            defaults.set(data, forKey: storageKey)
        }
    }
}