diff --git a/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionTunnelController.swift b/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionTunnelController.swift index 44394aa36f..8610b020c7 100644 --- a/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionTunnelController.swift +++ b/DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionTunnelController.swift @@ -37,6 +37,7 @@ import Subscription typealias NetworkProtectionStatusChangeHandler = (NetworkProtection.ConnectionStatus) -> Void typealias NetworkProtectionConfigChangeHandler = () -> Void +// swiftlint:disable:next type_body_length final class NetworkProtectionTunnelController: TunnelController, TunnelSessionProvider { // MARK: - Settings @@ -94,6 +95,13 @@ final class NetworkProtectionTunnelController: TunnelController, TunnelSessionPr /// private var internalManager: NETunnelProviderManager? + /// Simply clears the internal manager so the VPN manager is reloaded next time it's requested. + /// + @MainActor + private func clearInternalManager() { + internalManager = nil + } + /// The last known VPN status. /// /// Should not be used for checking the current status. @@ -178,6 +186,7 @@ final class NetworkProtectionTunnelController: TunnelController, TunnelSessionPr subscribeToSettingsChanges() subscribeToStatusChanges() + subscribeToConfigurationChanges() } // MARK: - Observing Status Changes @@ -209,6 +218,31 @@ final class NetworkProtectionTunnelController: TunnelController, TunnelSessionPr } } + // MARK: - Observing Configuation Changes + + private func subscribeToConfigurationChanges() { + notificationCenter.publisher(for: .NEVPNConfigurationChange) + .receive(on: DispatchQueue.main) + .sink { _ in + Task { @MainActor in + guard let manager = await self.manager else { + return + } + + do { + try await manager.loadFromPreferences() + + if manager.connection.status == .invalid { + self.clearInternalManager() + } + } catch { + self.clearInternalManager() + } + } + } + .store(in: &cancellables) + } + // MARK: - Subscriptions private func subscribeToSettingsChanges() { @@ -693,6 +727,14 @@ final class NetworkProtectionTunnelController: TunnelController, TunnelSessionPr func disableOnDemand(tunnelManager: NETunnelProviderManager) async throws { try await tunnelManager.loadFromPreferences() + guard tunnelManager.connection.status != .invalid else { + // An invalid connection status means the VPN isn't really configured + // so we don't want to save changed because that would re-create the VPN + // configuration. + clearInternalManager() + return + } + tunnelManager.isOnDemandEnabled = false try await tunnelManager.saveToPreferences()