diff --git a/Sources/NetworkProtection/PacketTunnelProvider.swift b/Sources/NetworkProtection/PacketTunnelProvider.swift index d40b104c2..2e980e1f9 100644 --- a/Sources/NetworkProtection/PacketTunnelProvider.swift +++ b/Sources/NetworkProtection/PacketTunnelProvider.swift @@ -830,7 +830,8 @@ open class PacketTunnelProvider: NEPacketTunnelProvider { .setNotifyStatusChanges, .setRegistrationKeyValidity, .setSelectedEnvironment, - .setShowInMenuBar: + .setShowInMenuBar, + .setVPNFirstEnabled: // Intentional no-op, as some setting changes don't require any further operation break } diff --git a/Sources/NetworkProtection/Settings/Extensions/UserDefaults+vpnFirstEnabled.swift b/Sources/NetworkProtection/Settings/Extensions/UserDefaults+vpnFirstEnabled.swift new file mode 100644 index 000000000..dbf9b1f30 --- /dev/null +++ b/Sources/NetworkProtection/Settings/Extensions/UserDefaults+vpnFirstEnabled.swift @@ -0,0 +1,41 @@ +// +// UserDefaults+showInMenuBar.swift +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Combine +import Foundation + +extension UserDefaults { + private var vpnFirstEnabledKey: String { + "vpnFirstEnabled" + } + + @objc + dynamic var vpnFirstEnabled: Date? { + get { + value(forKey: vpnFirstEnabledKey) as? Date + } + + set { + set(newValue, forKey: vpnFirstEnabledKey) + } + } + + var vpnFirstEnabledPublisher: AnyPublisher { + publisher(for: \.vpnFirstEnabled).eraseToAnyPublisher() + } +} diff --git a/Sources/NetworkProtection/Settings/VPNSettings.swift b/Sources/NetworkProtection/Settings/VPNSettings.swift index 8eeed00b4..a8b21ea94 100644 --- a/Sources/NetworkProtection/Settings/VPNSettings.swift +++ b/Sources/NetworkProtection/Settings/VPNSettings.swift @@ -19,7 +19,7 @@ import Combine import Foundation -// swiftlint:disable type_body_length +// swiftlint:disable type_body_length file_length /// Persists and publishes changes to tunnel settings. /// @@ -40,6 +40,7 @@ public final class VPNSettings { case setSelectedLocation(_ selectedLocation: SelectedLocation) case setSelectedEnvironment(_ selectedEnvironment: SelectedEnvironment) case setShowInMenuBar(_ showInMenuBar: Bool) + case setVPNFirstEnabled(_ vpnFirstEnabled: Date?) } public enum RegistrationKeyValidity: Codable { @@ -131,6 +132,10 @@ public final class VPNSettings { Change.setShowInMenuBar(showInMenuBar) }.eraseToAnyPublisher() + let vpnFirstEnabledPublisher = vpnFirstEnabledPublisher.map { vpnFirstEnabled in + Change.setVPNFirstEnabled(vpnFirstEnabled) + }.eraseToAnyPublisher() + return Publishers.MergeMany( connectOnLoginPublisher, includeAllNetworksPublisher, @@ -140,7 +145,8 @@ public final class VPNSettings { serverChangePublisher, locationChangePublisher, environmentChangePublisher, - showInMenuBarPublisher).eraseToAnyPublisher() + showInMenuBarPublisher, + vpnFirstEnabledPublisher).eraseToAnyPublisher() }() public init(defaults: UserDefaults) { @@ -163,6 +169,7 @@ public final class VPNSettings { // MARK: - Applying Changes + // swiftlint:disable cyclomatic_complexity public func apply(change: Change) { switch change { case .setConnectOnLogin(let connectOnLogin): @@ -185,8 +192,11 @@ public final class VPNSettings { self.selectedEnvironment = selectedEnvironment case .setShowInMenuBar(let showInMenuBar): self.showInMenuBar = showInMenuBar + case .setVPNFirstEnabled(let vpnFirstEnabled): + self.vpnFirstEnabled = vpnFirstEnabled } } + // swiftlint:enable cyclomatic_complexity // MARK: - Connect on Login @@ -375,6 +385,22 @@ public final class VPNSettings { } } } + + // MARK: - First time VPN is enabled + + public var vpnFirstEnabledPublisher: AnyPublisher { + defaults.vpnFirstEnabledPublisher + } + + public var vpnFirstEnabled: Date? { + get { + defaults.vpnFirstEnabled + } + + set { + defaults.vpnFirstEnabled = newValue + } + } } -// swiftlint:enable type_body_length +// swiftlint:enable type_body_length file_length