summaryrefslogtreecommitdiffhomepage
path: root/ios/MullvadVPN
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2022-12-13 10:19:43 +0100
committerAndrej Mihajlov <and@mullvad.net>2022-12-13 14:42:27 +0100
commit00a0ec2adcfdd7c15c25f0a1bfeb9554d85ba17a (patch)
tree30e0b33ed4b47c6d040e9a006da984a1fb663dd4 /ios/MullvadVPN
parent800c01bf3d521f23b919e7d5bb0c0a2cf386ec01 (diff)
downloadmullvadvpn-00a0ec2adcfdd7c15c25f0a1bfeb9554d85ba17a.tar.xz
mullvadvpn-00a0ec2adcfdd7c15c25f0a1bfeb9554d85ba17a.zip
Periodically update device data while capping it at 1 update per minute
Diffstat (limited to 'ios/MullvadVPN')
-rw-r--r--ios/MullvadVPN/AccountDataThrottling.swift2
-rw-r--r--ios/MullvadVPN/DeviceDataThrottling.swift55
-rw-r--r--ios/MullvadVPN/SceneDelegate.swift38
-rw-r--r--ios/MullvadVPN/TunnelManager/TunnelManager.swift12
4 files changed, 87 insertions, 20 deletions
diff --git a/ios/MullvadVPN/AccountDataThrottling.swift b/ios/MullvadVPN/AccountDataThrottling.swift
index f6093beeb2..3f8ae0daf4 100644
--- a/ios/MullvadVPN/AccountDataThrottling.swift
+++ b/ios/MullvadVPN/AccountDataThrottling.swift
@@ -66,7 +66,7 @@ struct AccountDataThrottling {
switch comparisonResult {
case .orderedAscending, .orderedSame:
- lastUpdate = Date()
+ lastUpdate = now
tunnelManager.updateAccountData()
case .orderedDescending:
diff --git a/ios/MullvadVPN/DeviceDataThrottling.swift b/ios/MullvadVPN/DeviceDataThrottling.swift
new file mode 100644
index 0000000000..cb30b130ff
--- /dev/null
+++ b/ios/MullvadVPN/DeviceDataThrottling.swift
@@ -0,0 +1,55 @@
+//
+// DeviceDataThrottling.swift
+// MullvadVPN
+//
+// Created by pronebird on 13/12/2022.
+// Copyright © 2022 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+
+/// Struct used for throttling UI calls to update device data via tunnel manager.
+struct DeviceDataThrottling {
+ /// Default cooldown interval between requests.
+ private static let defaultWaitInterval: TimeInterval = 60
+
+ let tunnelManager: TunnelManager
+ private(set) var lastUpdate: Date?
+
+ init(tunnelManager: TunnelManager) {
+ self.tunnelManager = tunnelManager
+ }
+
+ mutating func requestUpdate(forceUpdate: Bool) {
+ guard tunnelManager.deviceState.isLoggedIn else {
+ return
+ }
+
+ let now = Date()
+
+ guard !forceUpdate else {
+ startUpdate(now: now)
+ return
+ }
+
+ let nextUpdateAfter = lastUpdate?.addingTimeInterval(Self.defaultWaitInterval)
+ let comparisonResult = nextUpdateAfter?.compare(now) ?? .orderedAscending
+
+ switch comparisonResult {
+ case .orderedAscending, .orderedSame:
+ startUpdate(now: now)
+
+ case .orderedDescending:
+ break
+ }
+ }
+
+ mutating func reset() {
+ lastUpdate = nil
+ }
+
+ private mutating func startUpdate(now: Date) {
+ lastUpdate = now
+ tunnelManager.updateDeviceData()
+ }
+}
diff --git a/ios/MullvadVPN/SceneDelegate.swift b/ios/MullvadVPN/SceneDelegate.swift
index 26b29c3805..1f3838b2ae 100644
--- a/ios/MullvadVPN/SceneDelegate.swift
+++ b/ios/MullvadVPN/SceneDelegate.swift
@@ -38,7 +38,9 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate, UISplitViewControllerDe
private var connectController: ConnectViewController?
private weak var settingsNavController: SettingsNavigationController?
private var lastLoginAction: LoginAction?
- private lazy var accountDataThrottling = AccountDataThrottling(tunnelManager: tunnelManager)
+
+ private var accountDataThrottling: AccountDataThrottling?
+ private var deviceDataThrottling: DeviceDataThrottling?
private var outOfTimeTimer: Timer?
@@ -92,6 +94,9 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate, UISplitViewControllerDe
isSceneConfigured = true
+ accountDataThrottling = AccountDataThrottling(tunnelManager: tunnelManager)
+ deviceDataThrottling = DeviceDataThrottling(tunnelManager: tunnelManager)
+
rootContainer.delegate = self
window?.rootViewController = rootContainer
@@ -107,7 +112,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate, UISplitViewControllerDe
relayCacheTracker.addObserver(self)
NotificationManager.shared.delegate = self
- accountDataThrottling.requestUpdate(condition: .always)
+ refreshDeviceAndAccountData(forceUpdate: true)
}
private func setShowsPrivacyOverlay(_ showOverlay: Bool) {
@@ -120,6 +125,21 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate, UISplitViewControllerDe
}
}
+ private func refreshDeviceAndAccountData(forceUpdate: Bool) {
+ let condition: AccountDataThrottling.Condition =
+ settingsNavController == nil && !forceUpdate
+ ? .whenCloseToExpiryAndBeyond
+ : .always
+
+ accountDataThrottling?.requestUpdate(condition: condition)
+ deviceDataThrottling?.requestUpdate(forceUpdate: forceUpdate)
+ }
+
+ private func resetDeviceAndAccountDataThrottling() {
+ accountDataThrottling?.reset()
+ deviceDataThrottling?.reset()
+ }
+
// MARK: - UIWindowSceneDelegate
func scene(
@@ -148,11 +168,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate, UISplitViewControllerDe
func sceneDidBecomeActive(_ scene: UIScene) {
if isSceneConfigured {
- accountDataThrottling.requestUpdate(
- condition: settingsNavController == nil
- ? .whenCloseToExpiryAndBeyond
- : .always
- )
+ refreshDeviceAndAccountData(forceUpdate: false)
}
setShowsPrivacyOverlay(false)
@@ -190,7 +206,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate, UISplitViewControllerDe
let navController = makeSettingsNavigationController(route: route)
// Refresh account data each time user opens settings
- accountDataThrottling.requestUpdate(condition: .always)
+ refreshDeviceAndAccountData(forceUpdate: true)
// On iPad the login controller can be presented modally above the root container.
// in that case we have to use the presented controller to present the next modal.
@@ -734,7 +750,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate, UISplitViewControllerDe
) {
switch route {
case .root, .account:
- accountDataThrottling.requestUpdate(condition: .always)
+ refreshDeviceAndAccountData(forceUpdate: false)
default:
break
@@ -903,10 +919,10 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate, UISplitViewControllerDe
}
case .loggedOut:
- accountDataThrottling.reset()
+ resetDeviceAndAccountDataThrottling()
case .revoked:
- accountDataThrottling.reset()
+ resetDeviceAndAccountDataThrottling()
showRevokedDeviceView()
}
}
diff --git a/ios/MullvadVPN/TunnelManager/TunnelManager.swift b/ios/MullvadVPN/TunnelManager/TunnelManager.swift
index 5dd4711ed1..c117dee394 100644
--- a/ios/MullvadVPN/TunnelManager/TunnelManager.swift
+++ b/ios/MullvadVPN/TunnelManager/TunnelManager.swift
@@ -428,10 +428,7 @@ final class TunnelManager: StorePaymentObserver {
operationQueue.addOperation(operation)
}
- func updateDeviceData(_ completionHandler: @escaping (OperationCompletion<
- StoredDeviceData,
- Error
- >) -> Void) -> Cancellable {
+ func updateDeviceData(_ completionHandler: ((Error?) -> Void)? = nil) {
let operation = UpdateDeviceDataOperation(
dispatchQueue: internalQueue,
interactor: TunnelInteractorProxy(self),
@@ -442,11 +439,12 @@ final class TunnelManager: StorePaymentObserver {
operation.completionHandler = { [weak self] completion in
guard let self = self else { return }
- if let error = completion.error {
+ let error = completion.error
+ if let error = error {
self.checkIfDeviceRevoked(error)
}
- completionHandler(completion)
+ completionHandler?(error)
}
operation.addObserver(
@@ -462,8 +460,6 @@ final class TunnelManager: StorePaymentObserver {
)
operationQueue.addOperation(operation)
-
- return operation
}
func rotatePrivateKey(