krz/domain-dig

an ios app for DNS & SSL analysis

clone: git clone https://gitbay.org/krz/domain-dig.git

v4.4.1: DomainDig/UsageCreditService.swift · raw

 1import Foundation
 2
 3actor UsageCreditService {
 4    static let shared = UsageCreditService()
 5
 6    private struct CreditLedger: Codable {
 7        let appVersion: String
 8        var remainingByFeature: [UsageCreditFeature: Int]
 9    }
10
11    private let storageKey = "usageCredits.ledger"
12    private var ledger: CreditLedger
13
14    init(defaults: UserDefaults = .standard) {
15        if let data = defaults.data(forKey: storageKey),
16           let decoded = try? JSONDecoder().decode(CreditLedger.self, from: data),
17           decoded.appVersion == AppVersion.current {
18            ledger = decoded
19        } else {
20            ledger = Self.makeLedger()
21            if let data = try? JSONEncoder().encode(ledger) {
22                defaults.set(data, forKey: storageKey)
23            }
24        }
25    }
26
27    func status(for feature: UsageCreditFeature) -> UsageCreditStatus {
28        let total = feature.defaultAllowance
29        let remaining = ledger.remainingByFeature[feature] ?? total
30        return UsageCreditStatus(
31            feature: feature,
32            remaining: remaining,
33            total: total,
34            resetContext: "Resets with app version \(ledger.appVersion)"
35        )
36    }
37
38    func allStatuses() -> [UsageCreditStatus] {
39        UsageCreditFeature.allCases.map { status(for: $0) }
40    }
41
42    func canUse(_ feature: UsageCreditFeature) -> Bool {
43        status(for: feature).remaining > 0
44    }
45
46    @discardableResult
47    func consume(_ feature: UsageCreditFeature) -> UsageCreditStatus {
48        let current = ledger.remainingByFeature[feature] ?? feature.defaultAllowance
49        ledger.remainingByFeature[feature] = max(0, current - 1)
50        persist()
51        return status(for: feature)
52    }
53
54    func resetForCurrentVersion() {
55        ledger = Self.makeLedger()
56        persist()
57    }
58
59    private static func makeLedger() -> CreditLedger {
60        CreditLedger(
61            appVersion: AppVersion.current,
62            remainingByFeature: Dictionary(
63                uniqueKeysWithValues: UsageCreditFeature.allCases.map { ($0, $0.defaultAllowance) }
64            )
65        )
66    }
67
68    private func persist(defaults: UserDefaults = .standard) {
69        if let data = try? JSONEncoder().encode(ledger) {
70            defaults.set(data, forKey: storageKey)
71        }
72    }
73}