Skip to content
This repository was archived by the owner on Feb 24, 2025. It is now read-only.

Commit 7bbf088

Browse files
Disables domain exclusions in App Store builds (#3745)
Task/Issue URL: https://app.asana.com/0/1207603085593419/1209168347582143/f ## Description We're temporarily disabling domain exclusions from App Store builds. We'll migrate this to a remote feature flag next week, but doing that later as it requires 4 PRs (vs 1 for this approach).
1 parent f9c494c commit 7bbf088

File tree

15 files changed

+170
-30
lines changed

15 files changed

+170
-30
lines changed

DuckDuckGo/NetworkProtection/AppTargets/BothAppTargets/NetworkProtectionNavBarPopoverManager.swift

+1
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ final class NetworkProtectionNavBarPopoverManager: NetPPopoverManager {
191191
activeSitePublisher: activeSitePublisher,
192192
forMenuApp: false,
193193
vpnSettings: vpnSettings,
194+
proxySettings: proxySettings,
194195
logger: Logger(subsystem: "DuckDuckGo", category: "TipKit"))
195196

196197
let popover = NetworkProtectionPopover(

DuckDuckGo/Preferences/Model/VPNPreferencesModel.swift

+7-3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import Combine
2121
import Foundation
2222
import NetworkProtection
2323
import NetworkProtectionIPC
24+
import NetworkProtectionProxy
2425
import NetworkProtectionUI
2526
import BrowserServicesKit
2627

@@ -77,8 +78,9 @@ final class VPNPreferencesModel: ObservableObject {
7778
///
7879
/// Only necessary because this is feature flagged to internal users.
7980
///
80-
@Published
81-
var showExcludedSites: Bool
81+
var showExcludedSites: Bool {
82+
proxySettings.proxyAvailable
83+
}
8284

8385
@Published var notifyStatusChanges: Bool {
8486
didSet {
@@ -100,16 +102,19 @@ final class VPNPreferencesModel: ObservableObject {
100102

101103
private let vpnXPCClient: VPNControllerXPCClient
102104
private let settings: VPNSettings
105+
private let proxySettings: TransparentProxySettings
103106
private let pinningManager: PinningManager
104107
private var cancellables = Set<AnyCancellable>()
105108

106109
init(vpnXPCClient: VPNControllerXPCClient = .shared,
107110
settings: VPNSettings = .init(defaults: .netP),
111+
proxySettings: TransparentProxySettings = .init(defaults: .netP),
108112
pinningManager: PinningManager = LocalPinningManager.shared,
109113
defaults: UserDefaults = .netP) {
110114

111115
self.vpnXPCClient = vpnXPCClient
112116
self.settings = settings
117+
self.proxySettings = proxySettings
113118
self.pinningManager = pinningManager
114119

115120
connectOnLogin = settings.connectOnLogin
@@ -118,7 +123,6 @@ final class VPNPreferencesModel: ObservableObject {
118123
showInMenuBar = settings.showInMenuBar
119124
showInBrowserToolbar = pinningManager.isPinned(.networkProtection)
120125
showUninstallVPN = defaults.networkProtectionOnboardingStatus != .default
121-
showExcludedSites = true
122126
onboardingStatus = defaults.networkProtectionOnboardingStatus
123127
locationItem = VPNLocationPreferenceItemModel(selectedLocation: settings.selectedLocation)
124128

DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift

+11-1
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,17 @@ final class DuckDuckGoVPNAppDelegate: NSObject, NSApplicationDelegate {
173173

174174
private let tunnelSettings: VPNSettings
175175
private lazy var userDefaults = UserDefaults.netP
176-
private lazy var proxySettings = TransparentProxySettings(defaults: .netP)
176+
private lazy var proxySettings: TransparentProxySettings = {
177+
let settings = TransparentProxySettings(defaults: .netP)
178+
179+
#if APPSTORE
180+
settings.proxyAvailable = false
181+
#else
182+
settings.proxyAvailable = true
183+
#endif
184+
185+
return settings
186+
}()
177187

178188
@MainActor
179189
private lazy var vpnProxyLauncher = VPNProxyLauncher(

LocalPackages/NetworkProtectionMac/Package.swift

+1
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ let package = Package(
103103
name: "NetworkProtectionUI",
104104
dependencies: [
105105
"VPNPixels",
106+
"NetworkProtectionProxy",
106107
.product(name: "NetworkProtection", package: "BrowserServicesKit"),
107108
.product(name: "PixelKit", package: "BrowserServicesKit"),
108109
.product(name: "SwiftUIExtensions", package: "SwiftUIExtensions"),

LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionProxy/Controller/TransparentProxyController.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,8 @@ public final class TransparentProxyController {
246246
// MARK: - Start & stop the proxy
247247

248248
public var isRequiredForActiveFeatures: Bool {
249-
settings.appRoutingRules.count > 0 || settings.excludedDomains.count > 0
249+
settings.proxyAvailable
250+
&& (settings.appRoutingRules.count > 0 || settings.excludedDomains.count > 0)
250251
}
251252

252253
public func start() async throws {

LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionProxy/IPC/TransparentProxyAppMessageHandler.swift

+2
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ final class TransparentProxyAppMessageHandler {
8383
settings.appRoutingRules = routingRules
8484
case .excludedDomains(let excludedDomains):
8585
settings.excludedDomains = excludedDomains
86+
case .proxyAvailable(let available):
87+
settings.proxyAvailable = available
8688
}
8789
}
8890
}

LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionProxy/Provider/TransparentProxyProvider.swift

+3
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ open class TransparentProxyProvider: NETransparentProxyProvider {
130130
Task {
131131
try await self.updateNetworkSettings()
132132
}
133+
case .proxyAvailable:
134+
// no-op, handled by app
135+
break
133136
}
134137
}.store(in: &cancellables)
135138
}

LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionProxy/Settings/TransparentProxySettings.swift

+17
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public final class TransparentProxySettings {
2323
public enum Change: Codable {
2424
case appRoutingRules(_ routingRules: VPNAppRoutingRules)
2525
case excludedDomains(_ excludedDomains: [String])
26+
case proxyAvailable(_ available: Bool)
2627
}
2728

2829
let defaults: UserDefaults
@@ -35,6 +36,12 @@ public final class TransparentProxySettings {
3536
.map { routingRules in
3637
Change.appRoutingRules(routingRules)
3738
}.eraseToAnyPublisher(),
39+
defaults.vpnProxyFeatureAvailablePublisher
40+
.dropFirst()
41+
.removeDuplicates()
42+
.map { available in
43+
Change.proxyAvailable(available)
44+
}.eraseToAnyPublisher(),
3845
defaults.vpnProxyExcludedDomainsPublisher
3946
.dropFirst()
4047
.removeDuplicates()
@@ -70,6 +77,16 @@ public final class TransparentProxySettings {
7077
}
7178
}
7279

80+
public var proxyAvailable: Bool {
81+
get {
82+
defaults.vpnProxyFeatureAvailable
83+
}
84+
85+
set {
86+
defaults.vpnProxyFeatureAvailable = newValue
87+
}
88+
}
89+
7390
// MARK: - Reset to factory defaults
7491

7592
public func resetAll() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//
2+
// UserDefaults+vpnProxyFeatureAvailable.swift
3+
//
4+
// Copyright © 2024 DuckDuckGo. All rights reserved.
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
19+
import Combine
20+
import Foundation
21+
22+
extension UserDefaults {
23+
@objc
24+
dynamic var vpnProxyFeatureAvailable: Bool {
25+
get {
26+
bool(forKey: #keyPath(vpnProxyFeatureAvailable))
27+
}
28+
29+
set {
30+
set(newValue, forKey: #keyPath(vpnProxyFeatureAvailable))
31+
}
32+
}
33+
34+
var vpnProxyFeatureAvailablePublisher: AnyPublisher<Bool, Never> {
35+
publisher(for: \.vpnProxyFeatureAvailable).eraseToAnyPublisher()
36+
}
37+
38+
func resetVPNProxyFeatureAvailable() {
39+
removeObject(forKey: #keyPath(vpnProxyFeatureAvailable))
40+
}
41+
}

LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Menu/StatusBarMenu.swift

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import Combine
2222
import Common
2323
import LoginItems
2424
import NetworkProtection
25+
import NetworkProtectionProxy
2526
import os.log
2627
import SwiftUI
2728

@@ -159,6 +160,7 @@ public final class StatusBarMenu: NSObject {
159160
activeSitePublisher: activeSitePublisher,
160161
forMenuApp: true,
161162
vpnSettings: VPNSettings(defaults: userDefaults),
163+
proxySettings: TransparentProxySettings(defaults: userDefaults),
162164
logger: Logger(subsystem: "DuckDuckGo", category: "TipKit"))
163165

164166
let debugInformationViewModel = DebugInformationViewModel(showDebugInformation: isOptionKeyPressed)

LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/StatusView/NetworkProtectionStatusViewModel.swift

+1
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ extension NetworkProtectionStatusView {
128128
onboardingStatusPublisher: onboardingStatusPublisher,
129129
statusReporter: statusReporter,
130130
vpnSettings: .init(defaults: userDefaults),
131+
proxySettings: .init(defaults: userDefaults),
131132
locationFormatter: locationFormatter,
132133
uiActionHandler: uiActionHandler)
133134

LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TipViews/Model/VPNTipsModel.swift

+42
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import AppKit
2020
import Combine
2121
import Common
2222
import NetworkProtection
23+
import NetworkProtectionProxy
2324
import os.log
2425
import TipKit
2526
import PixelKit
@@ -57,6 +58,7 @@ public final class VPNTipsModel: ObservableObject {
5758

5859
private let isMenuApp: Bool
5960
private let vpnSettings: VPNSettings
61+
private let proxySettings: TransparentProxySettings
6062
private let logger: Logger
6163
private var cancellables = Set<AnyCancellable>()
6264

@@ -65,6 +67,7 @@ public final class VPNTipsModel: ObservableObject {
6567
activeSitePublisher: CurrentValuePublisher<ActiveSiteInfo?, Never>,
6668
forMenuApp isMenuApp: Bool,
6769
vpnSettings: VPNSettings,
70+
proxySettings: TransparentProxySettings,
6871
logger: Logger) {
6972

7073
self.activeSiteInfo = activeSitePublisher.value
@@ -73,6 +76,7 @@ public final class VPNTipsModel: ObservableObject {
7376
self.isMenuApp = isMenuApp
7477
self.logger = logger
7578
self.vpnSettings = vpnSettings
79+
self.proxySettings = proxySettings
7680

7781
guard !isMenuApp else {
7882
return
@@ -135,6 +139,44 @@ public final class VPNTipsModel: ObservableObject {
135139

136140
var geoswitchingStatusUpdateTask: Task<Void, Never>?
137141

142+
@available(macOS 14.0, *)
143+
var canShowDomainExclusionsTip: Bool {
144+
guard canShowTips else {
145+
return false
146+
}
147+
148+
// If the proxy is available, we can show this tip after the geoswitchin tip
149+
// Otherwise we can't show this tip
150+
if proxySettings.proxyAvailable,
151+
case .invalidated = geoswitchingTip.status {
152+
153+
return true
154+
}
155+
156+
return false
157+
}
158+
159+
@available(macOS 14.0, *)
160+
var canShowAutoconnectTip: Bool {
161+
guard canShowTips else {
162+
return false
163+
}
164+
165+
// If the proxy is available, we need to wait until the domain exclusions tip was shown.
166+
// If the proxy is not available, we can show this tip after the geoswitchin tip
167+
if proxySettings.proxyAvailable,
168+
case .invalidated = domainExclusionsTip.status {
169+
170+
return true
171+
} else if !proxySettings.proxyAvailable,
172+
case .invalidated = geoswitchingTip.status {
173+
174+
return true
175+
}
176+
177+
return false
178+
}
179+
138180
// MARK: - Tip Action handling
139181

140182
@available(macOS 14.0, *)

LocalPackages/NetworkProtectionMac/Sources/NetworkProtectionUI/Views/TunnelControllerView/TunnelControllerView.swift

+25-25
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,7 @@ public struct TunnelControllerView: View {
5656
featureToggleRow()
5757

5858
if #available(macOS 14.0, *),
59-
tipsModel.canShowTips,
60-
case .invalidated = tipsModel.domainExclusionsTip.status {
59+
tipsModel.canShowAutoconnectTip {
6160

6261
TipView(tipsModel.autoconnectTip, action: tipsModel.autoconnectTipActionHandler)
6362
.tipImageSize(VPNTipsModel.imageSize)
@@ -82,34 +81,35 @@ public struct TunnelControllerView: View {
8281
}
8382
}
8483

85-
SiteTroubleshootingView()
86-
.padding(.top, 5)
84+
if model.exclusionsFeatureEnabled {
85+
SiteTroubleshootingView()
86+
.padding(.top, 5)
8787

88-
if #available(macOS 14.0, *),
89-
tipsModel.canShowTips,
90-
case .invalidated = tipsModel.geoswitchingTip.status {
91-
92-
TipView(tipsModel.domainExclusionsTip)
93-
.tipImageSize(VPNTipsModel.imageSize)
94-
.tipBackground(Color(.tipBackground))
95-
.padding(.horizontal, 9)
96-
.padding(.vertical, 6)
97-
.onAppear {
98-
tipsModel.handleDomainExclusionsTipShown()
99-
}
100-
.task {
101-
var previousStatus = tipsModel.domainExclusionsTip.status
88+
if #available(macOS 14.0, *),
89+
tipsModel.canShowDomainExclusionsTip {
10290

103-
for await status in tipsModel.domainExclusionsTip.statusUpdates {
104-
if case .invalidated(let reason) = status {
105-
if case .available = previousStatus {
106-
tipsModel.handleDomainExclusionTipInvalidated(reason)
91+
TipView(tipsModel.domainExclusionsTip)
92+
.tipImageSize(VPNTipsModel.imageSize)
93+
.tipBackground(Color(.tipBackground))
94+
.padding(.horizontal, 9)
95+
.padding(.vertical, 6)
96+
.onAppear {
97+
tipsModel.handleDomainExclusionsTipShown()
98+
}
99+
.task {
100+
var previousStatus = tipsModel.domainExclusionsTip.status
101+
102+
for await status in tipsModel.domainExclusionsTip.statusUpdates {
103+
if case .invalidated(let reason) = status {
104+
if case .available = previousStatus {
105+
tipsModel.handleDomainExclusionTipInvalidated(reason)
106+
}
107107
}
108-
}
109108

110-
previousStatus = status
109+
previousStatus = status
110+
}
111111
}
112-
}
112+
}
113113
}
114114

115115
Divider()

0 commit comments

Comments
 (0)