summaryrefslogtreecommitdiffhomepage
path: root/ios/MullvadVPN/Classes/AccountDataThrottling.swift
blob: 9a8926332d34c685233a64ce107b71b754af0c90 (plain)
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
74
75
76
77
78
79
80
81
82
83
84
//
//  AccountDataThrottling.swift
//  MullvadVPN
//
//  Created by pronebird on 09/08/2022.
//  Copyright © 2025 Mullvad VPN AB. All rights reserved.
//

import Foundation
import MullvadTypes

/// Struct used for throttling UI calls to update account data via tunnel manager.
struct AccountDataThrottling {
    /// Default cooldown interval between requests.
    private static let defaultWaitInterval: Duration = .minutes(1)

    /// Cooldown interval used when account has already expired.
    private static let waitIntervalForExpiredAccount: Duration = .seconds(10)

    /// Interval in days when account is considered to be close to expiry.
    private static let closeToExpiryDays = 4

    enum Condition {
        /// Always update account data.
        case always

        /// Only update account data when account is close to expiry or already expired.
        case whenCloseToExpiryAndBeyond
    }

    let tunnelManager: TunnelManager
    private(set) var lastUpdate: Date?

    init(tunnelManager: TunnelManager) {
        self.tunnelManager = tunnelManager
    }

    mutating func requestUpdate(condition: Condition) {
        guard let accountData = tunnelManager.deviceState.accountData else {
            return
        }

        let now = Date()

        switch condition {
        case .always:
            break

        case .whenCloseToExpiryAndBeyond:
            guard
                let closeToExpiry = Calendar.current.date(
                    byAdding: .day,
                    value: Self.closeToExpiryDays * -1,
                    to: accountData.expiry
                )
            else { return }

            if closeToExpiry > now {
                return
            }
        }

        let waitInterval =
            accountData.expiry > now
            ? Self.defaultWaitInterval
            : Self.waitIntervalForExpiredAccount

        let nextUpdateAfter = lastUpdate?.addingTimeInterval(waitInterval.timeInterval)
        let comparisonResult = nextUpdateAfter?.compare(now) ?? .orderedAscending

        switch comparisonResult {
        case .orderedAscending, .orderedSame:
            lastUpdate = now
            tunnelManager.updateAccountData()

        case .orderedDescending:
            break
        }
    }

    mutating func reset() {
        lastUpdate = nil
    }
}