summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrew Bulhak <andrew.bulhak@mullvad.net>2024-11-05 13:01:03 +0100
committerEmīls <emils@mullvad.net>2024-11-18 11:49:53 +0100
commit61e5a9509ccd0799f3c72da6efe564703b32fc4c (patch)
treed2052b986d4e0850796d98ee174321663515d2d6
parent7d76afdaaf12b3fa7f8bd7930a8d105379d165f1 (diff)
downloadmullvadvpn-61e5a9509ccd0799f3c72da6efe564703b32fc4c.tar.xz
mullvadvpn-61e5a9509ccd0799f3c72da6efe564703b32fc4c.zip
Implement SwiftUI UI for UDP TCP Obfuscation port selector view
-rw-r--r--ios/MullvadSettings/WireGuardObfuscationSettings.swift6
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj36
-rw-r--r--ios/MullvadVPN/Classes/AccessbilityIdentifier.swift1
-rw-r--r--ios/MullvadVPN/View controllers/Settings/Obfuscation/TunnelObfuscationSettingsWatchingObservableObject.swift48
-rw-r--r--ios/MullvadVPN/View controllers/Settings/Obfuscation/UDPTCPObfuscationSettingsView.swift39
-rw-r--r--ios/MullvadVPN/View controllers/Settings/Obfuscation/UDPTCPObfuscationSettingsViewModel.swift37
-rw-r--r--ios/MullvadVPN/View controllers/Settings/SwiftUI components/SingleChoiceList.swift70
-rw-r--r--ios/MullvadVPN/View controllers/Settings/SwiftUI components/StatefulPreviewWrapper.swift35
-rw-r--r--ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift35
-rw-r--r--ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewController.swift28
10 files changed, 294 insertions, 41 deletions
diff --git a/ios/MullvadSettings/WireGuardObfuscationSettings.swift b/ios/MullvadSettings/WireGuardObfuscationSettings.swift
index 2828c4da9b..e8679fb0c4 100644
--- a/ios/MullvadSettings/WireGuardObfuscationSettings.swift
+++ b/ios/MullvadSettings/WireGuardObfuscationSettings.swift
@@ -124,9 +124,9 @@ public struct WireGuardObfuscationSettings: Codable, Equatable {
@available(*, deprecated, message: "Use `udpOverTcpPort` instead")
private var port: WireGuardObfuscationPort = .automatic
- public let state: WireGuardObfuscationState
- public let udpOverTcpPort: WireGuardObfuscationUdpOverTcpPort
- public let shadowsocksPort: WireGuardObfuscationShadowsockPort
+ public var state: WireGuardObfuscationState
+ public var udpOverTcpPort: WireGuardObfuscationUdpOverTcpPort
+ public var shadowsocksPort: WireGuardObfuscationShadowsockPort
public init(
state: WireGuardObfuscationState = .automatic,
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index 1b1c87beaf..6ce2fca25e 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -40,6 +40,11 @@
06799AFC28F98EE300ACD94E /* AddressCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06AC114128F8413A0037AF9A /* AddressCache.swift */; };
0697D6E728F01513007A9E99 /* TransportMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0697D6E628F01513007A9E99 /* TransportMonitor.swift */; };
06AC116228F94C450037AF9A /* ApplicationConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58BFA5CB22A7CE1F00A6173D /* ApplicationConfiguration.swift */; };
+ 44075DFB2CDA4F7400F61139 /* UDPTCPObfuscationSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44075DFA2CDA4F7400F61139 /* UDPTCPObfuscationSettingsViewModel.swift */; };
+ 440E5AB02CDBD67D00B09614 /* StatefulPreviewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440E5AAF2CDBD67D00B09614 /* StatefulPreviewWrapper.swift */; };
+ 440E5AB42CDCF24500B09614 /* TunnelObfuscationSettingsWatchingObservableObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440E5AB32CDCF24500B09614 /* TunnelObfuscationSettingsWatchingObservableObject.swift */; };
+ 4422C0712CCFF6790001A385 /* UDPTCPObfuscationSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4422C0702CCFF6790001A385 /* UDPTCPObfuscationSettingsView.swift */; };
+ 4424CDD32CDBD4A6009D8C9F /* SingleChoiceList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4424CDD22CDBD4A6009D8C9F /* SingleChoiceList.swift */; };
449275422C3570CA000526DE /* ICMP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 449275412C3570CA000526DE /* ICMP.swift */; };
449872E12B7BBC5400094DDC /* TunnelSettingsUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 449872E02B7BBC5400094DDC /* TunnelSettingsUpdate.swift */; };
449872E42B7CB96300094DDC /* TunnelSettingsUpdateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 449872E32B7CB96300094DDC /* TunnelSettingsUpdateTests.swift */; };
@@ -1391,6 +1396,11 @@
06FAE67A28F83CA50033DD93 /* RESTDevicesProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RESTDevicesProxy.swift; sourceTree = "<group>"; };
06FAE67B28F83CA50033DD93 /* REST.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = REST.swift; sourceTree = "<group>"; };
06FAE67D28F83CA50033DD93 /* RESTTransport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RESTTransport.swift; sourceTree = "<group>"; };
+ 44075DFA2CDA4F7400F61139 /* UDPTCPObfuscationSettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UDPTCPObfuscationSettingsViewModel.swift; sourceTree = "<group>"; };
+ 440E5AAF2CDBD67D00B09614 /* StatefulPreviewWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatefulPreviewWrapper.swift; sourceTree = "<group>"; };
+ 440E5AB32CDCF24500B09614 /* TunnelObfuscationSettingsWatchingObservableObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelObfuscationSettingsWatchingObservableObject.swift; sourceTree = "<group>"; };
+ 4422C0702CCFF6790001A385 /* UDPTCPObfuscationSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UDPTCPObfuscationSettingsView.swift; sourceTree = "<group>"; };
+ 4424CDD22CDBD4A6009D8C9F /* SingleChoiceList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleChoiceList.swift; sourceTree = "<group>"; };
449275412C3570CA000526DE /* ICMP.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ICMP.swift; sourceTree = "<group>"; };
449275432C3C3029000526DE /* TunnelPinger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelPinger.swift; sourceTree = "<group>"; };
449872E02B7BBC5400094DDC /* TunnelSettingsUpdate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelSettingsUpdate.swift; sourceTree = "<group>"; };
@@ -2597,6 +2607,25 @@
path = Protocols;
sourceTree = "<group>";
};
+ 4422C06F2CCFF6520001A385 /* Obfuscation */ = {
+ isa = PBXGroup;
+ children = (
+ 440E5AB32CDCF24500B09614 /* TunnelObfuscationSettingsWatchingObservableObject.swift */,
+ 4422C0702CCFF6790001A385 /* UDPTCPObfuscationSettingsView.swift */,
+ 44075DFA2CDA4F7400F61139 /* UDPTCPObfuscationSettingsViewModel.swift */,
+ );
+ path = Obfuscation;
+ sourceTree = "<group>";
+ };
+ 4424CDD12CDBD457009D8C9F /* SwiftUI components */ = {
+ isa = PBXGroup;
+ children = (
+ 440E5AAF2CDBD67D00B09614 /* StatefulPreviewWrapper.swift */,
+ 4424CDD22CDBD4A6009D8C9F /* SingleChoiceList.swift */,
+ );
+ path = "SwiftUI components";
+ sourceTree = "<group>";
+ };
449872E22B7CB91B00094DDC /* MullvadSettings */ = {
isa = PBXGroup;
children = (
@@ -2825,6 +2854,8 @@
583FE01829C19709006E85F9 /* Settings */ = {
isa = PBXGroup;
children = (
+ 4424CDD12CDBD457009D8C9F /* SwiftUI components */,
+ 4422C06F2CCFF6520001A385 /* Obfuscation */,
7A9FA1432A2E3FE5000B728D /* CheckableSettingsCell.swift */,
F041BE4E2C983C2B0083EC28 /* DAITASettingsPromptItem.swift */,
7A1A264A2A29D65E00B978AA /* SelectableSettingsCell.swift */,
@@ -5647,7 +5678,9 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ 44075DFB2CDA4F7400F61139 /* UDPTCPObfuscationSettingsViewModel.swift in Sources */,
7A6389DC2B7E3BD6008E77E1 /* CustomListViewModel.swift in Sources */,
+ 4422C0712CCFF6790001A385 /* UDPTCPObfuscationSettingsView.swift in Sources */,
7A9CCCC42A96302800DD6A34 /* TunnelCoordinator.swift in Sources */,
5827B0A42B0F38FD00CCBBA1 /* EditAccessMethodInteractorProtocol.swift in Sources */,
586C0D852B03D31E00E7CDD7 /* SocksSectionHandler.swift in Sources */,
@@ -5719,6 +5752,7 @@
58DFF7D22B0256A300F864E0 /* MarkdownStylingOptions.swift in Sources */,
5867770E29096984006F721F /* OutOfTimeInteractor.swift in Sources */,
F03580252A13842C00E5DAFD /* IncreasedHitButton.swift in Sources */,
+ 4424CDD32CDBD4A6009D8C9F /* SingleChoiceList.swift in Sources */,
5827B0BD2B14AC9200CCBBA1 /* AccessViewModel+TestingStatus.swift in Sources */,
58F8AC0E25D3F8CE002BE0ED /* ProblemReportReviewViewController.swift in Sources */,
58EFC7752AFB4CEF00E9F4CB /* AboutViewController.swift in Sources */,
@@ -5762,6 +5796,7 @@
7A5869C72B5A8E4C00640D27 /* MethodSettingsDataSourceConfiguration.swift in Sources */,
58F2E14C276A61C000A79513 /* RotateKeyOperation.swift in Sources */,
5871FB96254ADE4E0051A0A4 /* ConsolidatedApplicationLog.swift in Sources */,
+ 440E5AB02CDBD67D00B09614 /* StatefulPreviewWrapper.swift in Sources */,
F0E8E4C52A60499100ED26A3 /* AccountDeletionViewController.swift in Sources */,
7A9CCCC12A96302800DD6A34 /* AccountCoordinator.swift in Sources */,
58FEEB58260B662E00A621A8 /* AutomaticKeyboardResponder.swift in Sources */,
@@ -5892,6 +5927,7 @@
58CEB2FD2AFD19D300E6E088 /* UITableView+ReuseIdentifier.swift in Sources */,
F0FADDEA2BE90AAA000D0B02 /* LaunchArguments.swift in Sources */,
5835B7CC233B76CB0096D79F /* TunnelManager.swift in Sources */,
+ 440E5AB42CDCF24500B09614 /* TunnelObfuscationSettingsWatchingObservableObject.swift in Sources */,
588D7EDE2AF3A585005DF40A /* ListAccessMethodItem.swift in Sources */,
5827B0B02B0F4CCD00CCBBA1 /* ListAccessMethodViewControllerDelegate.swift in Sources */,
588D7EE02AF3A595005DF40A /* ListAccessMethodInteractor.swift in Sources */,
diff --git a/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift b/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift
index c706fd9298..f916e7ae50 100644
--- a/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift
+++ b/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift
@@ -177,6 +177,7 @@ public enum AccessibilityIdentifier: String {
case wireGuardObfuscationUdpOverTcp
case wireGuardObfuscationShadowsocks
case wireGuardPort
+ case udpOverTcpObfuscationSettings
// Custom DNS
case blockAll
diff --git a/ios/MullvadVPN/View controllers/Settings/Obfuscation/TunnelObfuscationSettingsWatchingObservableObject.swift b/ios/MullvadVPN/View controllers/Settings/Obfuscation/TunnelObfuscationSettingsWatchingObservableObject.swift
new file mode 100644
index 0000000000..58e8144ad3
--- /dev/null
+++ b/ios/MullvadVPN/View controllers/Settings/Obfuscation/TunnelObfuscationSettingsWatchingObservableObject.swift
@@ -0,0 +1,48 @@
+//
+// TunnelObfuscationSettingsWatchingObservableObject.swift
+// MullvadVPN
+//
+// Created by Andrew Bulhak on 2024-11-07.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+import MullvadSettings
+
+/// a generic ObservableObject that binds to obfuscation settings in TunnelManager.
+/// Used as the basis for ViewModels for SwiftUI interfaces for these settings.
+
+class TunnelObfuscationSettingsWatchingObservableObject<T: Equatable>: ObservableObject {
+ let tunnelManager: TunnelManager
+ let keyPath: WritableKeyPath<WireGuardObfuscationSettings, T>
+ private var tunnelObserver: TunnelObserver?
+
+ // this is essentially @Published from scratch
+ var value: T {
+ willSet(newValue) {
+ guard newValue != self.value else { return }
+ objectWillChange.send()
+ var obfuscationSettings = tunnelManager.settings.wireGuardObfuscation
+ obfuscationSettings[keyPath: keyPath] = newValue
+ tunnelManager.updateSettings([.obfuscation(obfuscationSettings)])
+ }
+ }
+
+ init(tunnelManager: TunnelManager, keyPath: WritableKeyPath<WireGuardObfuscationSettings, T>, _ initialValue: T) {
+ self.tunnelManager = tunnelManager
+ self.keyPath = keyPath
+ self.value = initialValue
+ tunnelObserver =
+ TunnelBlockObserver(didUpdateTunnelSettings: { [weak self] _, newSettings in
+ guard let self else { return }
+ updateValueFromSettings(newSettings.wireGuardObfuscation)
+ })
+ }
+
+ private func updateValueFromSettings(_ settings: WireGuardObfuscationSettings) {
+ let newValue = settings[keyPath: keyPath]
+ if value != newValue {
+ value = newValue
+ }
+ }
+}
diff --git a/ios/MullvadVPN/View controllers/Settings/Obfuscation/UDPTCPObfuscationSettingsView.swift b/ios/MullvadVPN/View controllers/Settings/Obfuscation/UDPTCPObfuscationSettingsView.swift
new file mode 100644
index 0000000000..70769d71ee
--- /dev/null
+++ b/ios/MullvadVPN/View controllers/Settings/Obfuscation/UDPTCPObfuscationSettingsView.swift
@@ -0,0 +1,39 @@
+//
+// UDPTCPObfuscationSettingsView.swift
+// MullvadVPN
+//
+// Created by Andrew Bulhak on 2024-10-28.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+import MullvadSettings
+import SwiftUI
+
+struct UDPTCPObfuscationSettingsView<VM>: View where VM: UDPTCPObfuscationSettingsViewModel {
+ @StateObject var viewModel: VM
+
+ var body: some View {
+ let portString = NSLocalizedString(
+ "UDP_TCP_PORT_LABEL",
+ tableName: "UdpToTcp",
+ value: "Port",
+ comment: ""
+ )
+ SingleChoiceList(
+ title: portString,
+ options: [WireGuardObfuscationUdpOverTcpPort.automatic, .port80, .port5001],
+ value: $viewModel.value,
+ itemDescription: { item in NSLocalizedString(
+ "UDP_TCP_PORT_VALUE_\(item)",
+ tableName: "UdpToTcp",
+ value: "\(item)",
+ comment: ""
+ ) }
+ )
+ }
+}
+
+#Preview {
+ let model = MockUDPTCPObfuscationSettingsViewModel(udpTcpPort: .port5001)
+ return UDPTCPObfuscationSettingsView(viewModel: model)
+}
diff --git a/ios/MullvadVPN/View controllers/Settings/Obfuscation/UDPTCPObfuscationSettingsViewModel.swift b/ios/MullvadVPN/View controllers/Settings/Obfuscation/UDPTCPObfuscationSettingsViewModel.swift
new file mode 100644
index 0000000000..f712f0e644
--- /dev/null
+++ b/ios/MullvadVPN/View controllers/Settings/Obfuscation/UDPTCPObfuscationSettingsViewModel.swift
@@ -0,0 +1,37 @@
+//
+// UDPTCPObfuscationSettingsViewModel.swift
+// MullvadVPN
+//
+// Created by Andrew Bulhak on 2024-11-05.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+import MullvadSettings
+
+protocol UDPTCPObfuscationSettingsViewModel: ObservableObject {
+ var value: WireGuardObfuscationUdpOverTcpPort { get set }
+}
+
+/** A simple mock view model for use in Previews and similar */
+class MockUDPTCPObfuscationSettingsViewModel: UDPTCPObfuscationSettingsViewModel {
+ @Published var value: WireGuardObfuscationUdpOverTcpPort
+
+ init(udpTcpPort: WireGuardObfuscationUdpOverTcpPort = .automatic) {
+ self.value = udpTcpPort
+ }
+}
+
+/** The live view model which interfaces with the TunnelManager */
+class TunnelUDPTCPObfuscationSettingsViewModel: TunnelObfuscationSettingsWatchingObservableObject<
+ WireGuardObfuscationUdpOverTcpPort
+>,
+ UDPTCPObfuscationSettingsViewModel {
+ init(tunnelManager: TunnelManager) {
+ super.init(
+ tunnelManager: tunnelManager,
+ keyPath: \.udpOverTcpPort,
+ tunnelManager.settings.wireGuardObfuscation.udpOverTcpPort
+ )
+ }
+}
diff --git a/ios/MullvadVPN/View controllers/Settings/SwiftUI components/SingleChoiceList.swift b/ios/MullvadVPN/View controllers/Settings/SwiftUI components/SingleChoiceList.swift
new file mode 100644
index 0000000000..6077539b5d
--- /dev/null
+++ b/ios/MullvadVPN/View controllers/Settings/SwiftUI components/SingleChoiceList.swift
@@ -0,0 +1,70 @@
+//
+// SingleChoiceList.swift
+// MullvadVPN
+//
+// Created by Andrew Bulhak on 2024-11-06.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+import SwiftUI
+
+/**
+ A component presenting a vertical list in the Mullvad style for selecting a single item from a list.
+ The items can be any Hashable type.
+ */
+
+struct SingleChoiceList<Item>: View where Item: Hashable {
+ let title: String
+ let options: [Item]
+ var value: Binding<Item>
+ let itemDescription: (Item) -> String
+
+ init(title: String, options: [Item], value: Binding<Item>, itemDescription: ((Item) -> String)? = nil) {
+ self.title = title
+ self.options = options
+ self.value = value
+ self.itemDescription = itemDescription ?? { "\($0)" }
+ }
+
+ func row(_ item: Item) -> some View {
+ let isSelected = value.wrappedValue == item
+ return HStack {
+ Image(uiImage: UIImage(resource: .iconTick)).opacity(isSelected ? 1.0 : 0.0)
+ Spacer().frame(width: UIMetrics.SettingsCell.selectableSettingsCellLeftViewSpacing)
+ Text(verbatim: itemDescription(item))
+ Spacer()
+ }
+ .padding(EdgeInsets(UIMetrics.SettingsCell.layoutMargins))
+ .background(
+ isSelected
+ ? Color(UIColor.Cell.Background.selected)
+ : Color(UIColor.Cell.Background.indentationLevelOne)
+ )
+ .foregroundColor(Color(UIColor.Cell.titleTextColor))
+ .onTapGesture {
+ value.wrappedValue = item
+ }
+ }
+
+ var body: some View {
+ VStack(spacing: UIMetrics.TableView.separatorHeight) {
+ HStack {
+ Text(title).fontWeight(.semibold)
+ Spacer()
+ }
+ .padding(EdgeInsets(UIMetrics.SettingsCell.layoutMargins))
+ .background(Color(UIColor.Cell.Background.normal))
+ ForEach(options, id: \.self) { opt in
+ row(opt)
+ }
+ Spacer()
+ }
+ .padding(EdgeInsets(top: 24, leading: 0, bottom: 0, trailing: 0))
+ .background(Color(.secondaryColor))
+ .foregroundColor(Color(.primaryTextColor))
+ }
+}
+
+#Preview {
+ StatefulPreviewWrapper(1) { SingleChoiceList(title: "Test", options: [1, 2, 3], value: $0) }
+}
diff --git a/ios/MullvadVPN/View controllers/Settings/SwiftUI components/StatefulPreviewWrapper.swift b/ios/MullvadVPN/View controllers/Settings/SwiftUI components/StatefulPreviewWrapper.swift
new file mode 100644
index 0000000000..c90e8e3f77
--- /dev/null
+++ b/ios/MullvadVPN/View controllers/Settings/SwiftUI components/StatefulPreviewWrapper.swift
@@ -0,0 +1,35 @@
+//
+// StatefulPreviewWrapper.swift
+// MullvadVPN
+//
+// Created by Andrew Bulhak on 2024-11-06.
+// Copyright © 2024 Mullvad VPN AB. All rights reserved.
+//
+
+// This should probably live somewhere more central than `View controllers/Settings/SwiftUI components`. Where exactly is to be determined.
+
+import SwiftUI
+
+/** A wrapper for providing a state binding for SwiftUI Views in #Preview. This takes as arguments an initial value for the binding and a block which accepts the binding and returns a View to be previewed
+ The usage looks like:
+
+ ```
+ #Preview {
+ StatefulPreviewWrapper(initvalue) { ComponentToBePreviewed(binding: $0) }
+ }
+ ```
+ */
+
+struct StatefulPreviewWrapper<Value, Content: View>: View {
+ @State var value: Value
+ var content: (Binding<Value>) -> Content
+
+ var body: some View {
+ content($value)
+ }
+
+ init(_ value: Value, content: @escaping (Binding<Value>) -> Content) {
+ self._value = State(wrappedValue: value)
+ self.content = content
+ }
+}
diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift
index 4adf52ce26..d07110c6d8 100644
--- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift
+++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift
@@ -65,7 +65,6 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
case ipOverrides
case wireGuardPorts
case wireGuardObfuscation
- case wireGuardObfuscationPort
case quantumResistance
case privacyAndSecurity
}
@@ -369,9 +368,6 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
case .wireGuardObfuscation:
configureObfuscationHeader(view)
return view
- case .wireGuardObfuscationPort:
- configureObfuscationPortHeader(view)
- return view
case .quantumResistance:
configureQuantumResistanceHeader(view)
return view
@@ -449,7 +445,6 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
snapshot.appendSections(Section.allCases)
snapshot.appendItems([.dnsSettings], toSection: .dnsSettings)
snapshot.appendItems([.ipOverrides], toSection: .ipOverrides)
-
snapshot.appendItems([.multihopSwitch], toSection: .privacyAndSecurity)
applySnapshot(snapshot, animated: animated, completion: completion)
@@ -547,36 +542,6 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource<
}
}
- private func configureObfuscationPortHeader(_ header: SettingsHeaderView) {
- let title = NSLocalizedString(
- "OBFUSCATION_PORT_HEADER_LABEL",
- tableName: "VPNSettings",
- value: "UDP-over-TCP Port",
- comment: ""
- )
-
- header.accessibilityIdentifier = .udpOverTCPPortCell
- header.titleLabel.text = title
- header.accessibilityCustomActionName = title
- header.isExpanded = isExpanded(.wireGuardObfuscationPort)
- header.didCollapseHandler = { [weak self] header in
- guard let self else { return }
-
- var snapshot = snapshot()
- if header.isExpanded {
- snapshot.deleteItems(Item.wireGuardObfuscationPort)
- } else {
- snapshot.appendItems(Item.wireGuardObfuscationPort, toSection: .wireGuardObfuscationPort)
- }
- header.isExpanded.toggle()
- applySnapshot(snapshot, animated: true)
- }
-
- header.infoButtonHandler = { [weak self] in
- self.map { $0.delegate?.showInfo(for: .wireGuardObfuscationPort) }
- }
- }
-
private func configureQuantumResistanceHeader(_ header: SettingsHeaderView) {
let title = NSLocalizedString(
"QUANTUM_RESISTANCE_HEADER_LABEL",
diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewController.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewController.swift
index 10a519a737..c957422c67 100644
--- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewController.swift
+++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewController.swift
@@ -7,6 +7,7 @@
//
import MullvadSettings
+import SwiftUI
import UIKit
protocol VPNSettingsViewControllerDelegate: AnyObject {
@@ -111,9 +112,13 @@ extension VPNSettingsViewController: VPNSettingsDataSourceDelegate {
alertPresenter.showAlert(presentation: presentation, animated: true)
}
- func showDetails(for: VPNSettingsDetailsButtonItem) {
- // TODO: When ready, add navigation to detail views for selecting obfuscation options for
- // UDP-over-TCP and shadowsocks.
+ func showDetails(for item: VPNSettingsDetailsButtonItem) {
+ switch item {
+ case .udpOverTcp:
+ showUDPOverTCPObfuscationSettings()
+ case .wireguardOverShadowsocks:
+ showShadowsocksObfuscationSettings()
+ }
}
func showDNSSettings() {
@@ -125,6 +130,23 @@ extension VPNSettingsViewController: VPNSettingsDataSourceDelegate {
delegate?.showIPOverrides()
}
+ private func showUDPOverTCPObfuscationSettings() {
+ let viewModel = TunnelUDPTCPObfuscationSettingsViewModel(tunnelManager: interactor.tunnelManager)
+ let view = UDPTCPObfuscationSettingsView(viewModel: viewModel)
+ let vc = UIHostingController(rootView: view)
+ vc.title = NSLocalizedString(
+ "UDP_OVER_TCP_TITLE",
+ tableName: "VPNSettings",
+ value: "UDP-over-TCP",
+ comment: ""
+ )
+ navigationController?.pushViewController(vc, animated: true)
+ }
+
+ private func showShadowsocksObfuscationSettings() {
+ // TODO:
+ }
+
func didSelectWireGuardPort(_ port: UInt16?) {
interactor.setPort(port)
}