summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--ios/MullvadREST/Relay/RelayPicking/SinglehopPicker.swift29
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj5
-rw-r--r--ios/MullvadVPN/Coordinators/LoginCoordinator.swift8
-rw-r--r--ios/MullvadVPN/Coordinators/Settings/DAITA/DAITAMultihopNotice.swift34
-rw-r--r--ios/MullvadVPN/Coordinators/Settings/DAITA/DAITATunnelSettingsViewModel.swift17
-rw-r--r--ios/MullvadVPN/Coordinators/Settings/DAITA/SettingsDAITAView.swift15
-rw-r--r--ios/MullvadVPN/View controllers/InAppPurchase/InAppPurchaseViewController.swift4
-rw-r--r--ios/MullvadVPN/View controllers/OutOfTime/OutOfTimeViewController.swift2
-rw-r--r--ios/MullvadVPN/View controllers/RelayFilter/ChipViewCell.swift2
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ChipView/ChipFeature.swift28
-rw-r--r--ios/MullvadVPN/View controllers/Tunnel/ConnectionView/FeatureIndicatorsViewModel.swift4
-rw-r--r--ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewController.swift4
-rw-r--r--ios/MullvadVPN/Views/List/MullvadList.swift2
-rw-r--r--ios/MullvadVPNUITests/Pages/TunnelControlPage.swift12
-rw-r--r--ios/MullvadVPNUITests/RelayTests.swift8
-rw-r--r--ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift2
-rw-r--r--ios/PacketTunnelCoreTests/MultiHopEphemeralPeerExchangerTests.swift2
17 files changed, 138 insertions, 40 deletions
diff --git a/ios/MullvadREST/Relay/RelayPicking/SinglehopPicker.swift b/ios/MullvadREST/Relay/RelayPicking/SinglehopPicker.swift
index 68ebec446d..cf51578031 100644
--- a/ios/MullvadREST/Relay/RelayPicking/SinglehopPicker.swift
+++ b/ios/MullvadREST/Relay/RelayPicking/SinglehopPicker.swift
@@ -22,22 +22,21 @@ struct SinglehopPicker: RelayPicking {
// If DAITA is on, Direct only is off and obfuscation is on, and no supported relays are found, we should see if
// the obfuscated subset of exit relays is the cause of this. We can do this by checking if relay selection would
// have been successful with all relays available. If that's the case, throw error and point to obfuscation.
- do {
- _ = try pick(from: obfuscation.unfilteredRelays)
+ if (try? pick(from: obfuscation.unfilteredRelays)) != nil {
throw NoRelaysSatisfyingConstraintsError(.noObfuscatedRelaysFound)
- } catch let error as NoRelaysSatisfyingConstraintsError where error.reason == .noDaitaRelaysFound {
- // If DAITA is on, Direct only is off and obfuscation has been ruled out, and no supported relays are found,
- // we should try to find the nearest available relay that supports DAITA and use it as entry in a multihop selection.
- if daitaSettings.isAutomaticRouting {
- return try MultihopPicker(
- obfuscation: obfuscation,
- constraints: constraints,
- connectionAttemptCount: connectionAttemptCount,
- daitaSettings: daitaSettings
- ).pick()
- } else {
- throw error
- }
+ }
+
+ // If DAITA is on, Direct only is off and obfuscation has been ruled out, and no supported relays are found,
+ // we should try to find the nearest available relay that supports DAITA and use it as entry in a multihop selection.
+ if daitaSettings.isAutomaticRouting {
+ return try MultihopPicker(
+ obfuscation: obfuscation,
+ constraints: constraints,
+ connectionAttemptCount: connectionAttemptCount,
+ daitaSettings: daitaSettings
+ ).pick()
+ } else {
+ throw error
}
}
}
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index 7f27111634..47b1b7f97f 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -518,6 +518,7 @@
7A45CFC62C05FF6A00D80B21 /* ScreenshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A45CFC22C05FF2F00D80B21 /* ScreenshotTests.swift */; };
7A45CFC72C071DD400D80B21 /* SnapshotHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58D0C79D23F1CEBA00FE9BA7 /* SnapshotHelper.swift */; };
7A4D849E2C0F289800687980 /* RelaySelectorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5824037F2A827DF300163DE8 /* RelaySelectorProtocol.swift */; };
+ 7A5110D72DE734DE00686850 /* Color+Mullvad.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9394EEB2DBF56AA009595EA /* Color+Mullvad.swift */; };
7A516C2E2B6D357500BBD33D /* URL+Scoping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A516C2D2B6D357500BBD33D /* URL+Scoping.swift */; };
7A516C3A2B7111A700BBD33D /* IPOverrideWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A516C392B7111A700BBD33D /* IPOverrideWrapper.swift */; };
7A516C3C2B712F0B00BBD33D /* IPOverrideWrapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A516C3B2B712F0B00BBD33D /* IPOverrideWrapperTests.swift */; };
@@ -685,6 +686,7 @@
7AF10EB42ADE85BC00C090B9 /* RelayFilterCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF10EB32ADE85BC00C090B9 /* RelayFilterCoordinator.swift */; };
7AF36A9A2CA2964200E1D497 /* AnyIPAddressTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF36A992CA2964000E1D497 /* AnyIPAddressTests.swift */; };
7AF6E5F02A95051E00F2679D /* RouterBlockDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF6E5EF2A95051E00F2679D /* RouterBlockDelegate.swift */; };
+ 7AF822C72DF0664700BA4255 /* DAITAMultihopNotice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF822C62DF0663B00BA4255 /* DAITAMultihopNotice.swift */; };
7AF84F462D12C5B000C72690 /* SelectedRelaysStub+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF84F452D12C59F00C72690 /* SelectedRelaysStub+Stubs.swift */; };
7AF9BE882A30C62100DBFEDB /* SelectableSettingsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1A264A2A29D65E00B978AA /* SelectableSettingsCell.swift */; };
7AF9BE8C2A321D1F00DBFEDB /* RelayFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AF9BE8A2A321BEF00DBFEDB /* RelayFilter.swift */; };
@@ -2234,6 +2236,7 @@
7AF10EB32ADE85BC00C090B9 /* RelayFilterCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RelayFilterCoordinator.swift; sourceTree = "<group>"; };
7AF36A992CA2964000E1D497 /* AnyIPAddressTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyIPAddressTests.swift; sourceTree = "<group>"; };
7AF6E5EF2A95051E00F2679D /* RouterBlockDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RouterBlockDelegate.swift; sourceTree = "<group>"; };
+ 7AF822C62DF0663B00BA4255 /* DAITAMultihopNotice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DAITAMultihopNotice.swift; sourceTree = "<group>"; };
7AF84F452D12C59F00C72690 /* SelectedRelaysStub+Stubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SelectedRelaysStub+Stubs.swift"; sourceTree = "<group>"; };
7AF9BE8A2A321BEF00DBFEDB /* RelayFilter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayFilter.swift; sourceTree = "<group>"; };
7AF9BE8D2A331C7B00DBFEDB /* RelayFilterViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayFilterViewModel.swift; sourceTree = "<group>"; };
@@ -4375,6 +4378,7 @@
7A8A19082CE5FFD7000BCB5B /* DAITA */ = {
isa = PBXGroup;
children = (
+ 7AF822C62DF0663B00BA4255 /* DAITAMultihopNotice.swift */,
7A95B67C2D5F7C5B00687524 /* DAITASettingsCoordinator.swift */,
F041BE4E2C983C2B0083EC28 /* DAITASettingsPromptItem.swift */,
7A8A19132CEF2527000BCB5B /* DAITATunnelSettingsViewModel.swift */,
@@ -6484,6 +6488,7 @@
7AB2B6712BA1EB8C00B03E3B /* ListCustomListCoordinator.swift in Sources */,
582BB1AF229566420055B6EF /* SettingsCell.swift in Sources */,
7AF9BE8E2A331C7B00DBFEDB /* RelayFilterViewModel.swift in Sources */,
+ 7AF822C72DF0664700BA4255 /* DAITAMultihopNotice.swift in Sources */,
7A8A191E2CEF5CF2000BCB5B /* TunnelSettingsObservable.swift in Sources */,
58F3C0A4249CB069003E76BE /* HeaderBarView.swift in Sources */,
5864AF0829C78849005B0CD9 /* CellFactoryProtocol.swift in Sources */,
diff --git a/ios/MullvadVPN/Coordinators/LoginCoordinator.swift b/ios/MullvadVPN/Coordinators/LoginCoordinator.swift
index a7dd9dc999..b6f69f95d8 100644
--- a/ios/MullvadVPN/Coordinators/LoginCoordinator.swift
+++ b/ios/MullvadVPN/Coordinators/LoginCoordinator.swift
@@ -107,11 +107,9 @@ final class LoginCoordinator: Coordinator, Presenting, @preconcurrency DeviceMan
}
private func returnToLogin(repeatLogin: Bool) {
- guard let loginController else { return }
-
- navigationController.dismiss(animated: true) {
- if let lastLoginAction = self.lastLoginAction, repeatLogin {
- self.loginController?.start(action: lastLoginAction)
+ navigationController.dismiss(animated: true) { [weak self] in
+ if let lastLoginAction = self?.lastLoginAction, repeatLogin {
+ self?.loginController?.start(action: lastLoginAction)
}
}
}
diff --git a/ios/MullvadVPN/Coordinators/Settings/DAITA/DAITAMultihopNotice.swift b/ios/MullvadVPN/Coordinators/Settings/DAITA/DAITAMultihopNotice.swift
new file mode 100644
index 0000000000..007fd22cba
--- /dev/null
+++ b/ios/MullvadVPN/Coordinators/Settings/DAITA/DAITAMultihopNotice.swift
@@ -0,0 +1,34 @@
+//
+// DAITAMultihopNotice.swift
+// MullvadVPN
+//
+// Created by Jon Petersson on 2025-06-04.
+// Copyright © 2025 Mullvad VPN AB. All rights reserved.
+//
+
+import SwiftUI
+
+struct DAITAMultihopNotice: View {
+ var body: some View {
+ HStack(spacing: 8) {
+ Image(.iconInfo)
+ .resizable()
+ .frame(width: 18, height: 18)
+ .foregroundStyle(Color(.primaryTextColor).opacity(0.6))
+ Text(NSLocalizedString(
+ "SETTINGS_DAITA_MULTIHOP_ENABLED",
+ tableName: "Settings",
+ value: "Multihop is being used to enable DAITA for your selected location.",
+ comment: ""
+ ))
+ .font(.mullvadTinySemiBold)
+ .foregroundColor(Color(.primaryTextColor).opacity(0.6))
+ }
+ }
+}
+
+#Preview {
+ SettingsInfoContainerView {
+ DAITAMultihopNotice()
+ }
+}
diff --git a/ios/MullvadVPN/Coordinators/Settings/DAITA/DAITATunnelSettingsViewModel.swift b/ios/MullvadVPN/Coordinators/Settings/DAITA/DAITATunnelSettingsViewModel.swift
index 4bfa8a06c6..6c900b228f 100644
--- a/ios/MullvadVPN/Coordinators/Settings/DAITA/DAITATunnelSettingsViewModel.swift
+++ b/ios/MullvadVPN/Coordinators/Settings/DAITA/DAITATunnelSettingsViewModel.swift
@@ -15,6 +15,8 @@ class DAITATunnelSettingsViewModel: TunnelSettingsObserver, ObservableObject {
let tunnelManager: TunnelManager
var tunnelObserver: TunnelObserver?
+ var isAutomaticRoutingActive: Bool
+
var didFailDAITAValidation: (((item: DAITASettingsPromptItem, setting: DAITASettings)) -> Void)?
var value: DAITASettings {
@@ -30,9 +32,20 @@ class DAITATunnelSettingsViewModel: TunnelSettingsObserver, ObservableObject {
self.tunnelManager = tunnelManager
value = tunnelManager.settings.daita
- tunnelObserver = TunnelBlockObserver(didUpdateTunnelSettings: { [weak self] _, newSettings in
- self?.value = newSettings.daita
+ var isAutomaticRoutingActive: Bool {
+ tunnelManager.tunnelStatus.state.isMultihop && !tunnelManager.settings.tunnelMultihopState.isEnabled
+ }
+ self.isAutomaticRoutingActive = isAutomaticRoutingActive
+
+ let tunnelObserver = TunnelBlockObserver(didUpdateTunnelStatus: { [weak self] _, _ in
+ if isAutomaticRoutingActive != self?.isAutomaticRoutingActive {
+ self?.isAutomaticRoutingActive = isAutomaticRoutingActive
+ self?.objectWillChange.send()
+ }
})
+ self.tunnelObserver = tunnelObserver
+
+ tunnelManager.addObserver(tunnelObserver)
}
func evaluate(setting: DAITASettings) {
diff --git a/ios/MullvadVPN/Coordinators/Settings/DAITA/SettingsDAITAView.swift b/ios/MullvadVPN/Coordinators/Settings/DAITA/SettingsDAITAView.swift
index 492eab5619..57f6be62cd 100644
--- a/ios/MullvadVPN/Coordinators/Settings/DAITA/SettingsDAITAView.swift
+++ b/ios/MullvadVPN/Coordinators/Settings/DAITA/SettingsDAITAView.swift
@@ -15,6 +15,16 @@ struct SettingsDAITAView<ViewModel>: View where ViewModel: TunnelSettingsObserva
var body: some View {
SettingsInfoContainerView {
VStack(alignment: .leading, spacing: 8) {
+ if isAutomaticRoutingActive {
+ DAITAMultihopNotice()
+ .padding(EdgeInsets(
+ top: -UIMetrics.contentInsets.top,
+ leading: UIMetrics.contentInsets.toEdgeInsets.leading,
+ bottom: 8,
+ trailing: UIMetrics.contentInsets.toEdgeInsets.trailing
+ ))
+ }
+
SettingsInfoView(viewModel: dataViewModel)
VStack {
@@ -93,6 +103,11 @@ extension SettingsDAITAView {
}
)
}
+
+ var isAutomaticRoutingActive: Bool {
+ let viewModel = tunnelViewModel as? DAITATunnelSettingsViewModel
+ return viewModel?.isAutomaticRoutingActive ?? false
+ }
}
extension SettingsDAITAView {
diff --git a/ios/MullvadVPN/View controllers/InAppPurchase/InAppPurchaseViewController.swift b/ios/MullvadVPN/View controllers/InAppPurchase/InAppPurchaseViewController.swift
index 283073bf37..03cd30bf64 100644
--- a/ios/MullvadVPN/View controllers/InAppPurchase/InAppPurchaseViewController.swift
+++ b/ios/MullvadVPN/View controllers/InAppPurchase/InAppPurchaseViewController.swift
@@ -68,7 +68,7 @@ class InAppPurchaseViewController: UIViewController, StorePaymentObserver {
self.errorPresenter.showAlertForError(failure, context: .purchase) {
self.didFinish?()
}
- case let .failure(failure):
+ case .failure:
self.didFinish?()
}
}
@@ -87,7 +87,7 @@ class InAppPurchaseViewController: UIViewController, StorePaymentObserver {
self.errorPresenter.showAlertForError(failure, context: .restoration) {
self.didFinish?()
}
- case let .failure(failure):
+ case .failure:
self.didFinish?()
}
}
diff --git a/ios/MullvadVPN/View controllers/OutOfTime/OutOfTimeViewController.swift b/ios/MullvadVPN/View controllers/OutOfTime/OutOfTimeViewController.swift
index 40e4291445..19260838ab 100644
--- a/ios/MullvadVPN/View controllers/OutOfTime/OutOfTimeViewController.swift
+++ b/ios/MullvadVPN/View controllers/OutOfTime/OutOfTimeViewController.swift
@@ -31,7 +31,7 @@ class OutOfTimeViewController: UIViewController, RootContainment {
.lightContent
}
- nonisolated(unsafe) var preferredHeaderBarPresentation: HeaderBarPresentation {
+ nonisolated var preferredHeaderBarPresentation: HeaderBarPresentation {
let tunnelState = interactor.tunnelStatus.state
return HeaderBarPresentation(
diff --git a/ios/MullvadVPN/View controllers/RelayFilter/ChipViewCell.swift b/ios/MullvadVPN/View controllers/RelayFilter/ChipViewCell.swift
index b5d1ae32c0..fda13fdb48 100644
--- a/ios/MullvadVPN/View controllers/RelayFilter/ChipViewCell.swift
+++ b/ios/MullvadVPN/View controllers/RelayFilter/ChipViewCell.swift
@@ -110,7 +110,7 @@ struct ChipConfiguration: UIContentConfiguration {
var group: Group
var title: String
- var accessibilityId: AccessibilityIdentifier? = nil
+ var accessibilityId: AccessibilityIdentifier?
var textColor: UIColor = .white
var font = UIFont.preferredFont(forTextStyle: .caption1)
var backgroundColor: UIColor = .primaryColor
diff --git a/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ChipView/ChipFeature.swift b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ChipView/ChipFeature.swift
index 02cc17f5e7..b9016064b4 100644
--- a/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ChipView/ChipFeature.swift
+++ b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/ChipView/ChipFeature.swift
@@ -31,18 +31,28 @@ enum FeatureType {
struct DaitaFeature: ChipFeature {
let id: FeatureType = .daita
let state: TunnelState
+ let settings: LatestTunnelSettings
var isEnabled: Bool {
state.isDaita ?? false
}
var name: String {
- NSLocalizedString(
- "FEATURE_INDICATORS_CHIP_DAITA",
- tableName: "FeatureIndicatorsChip",
- value: "DAITA",
- comment: ""
- )
+ // When multihop is enabled via DAITA without being explicitly enabled
+ // by the user, display combined indicator instead.
+ state.isMultihop && !settings.tunnelMultihopState.isEnabled
+ ? NSLocalizedString(
+ "FEATURE_INDICATORS_CHIP_DAITA_MULTIHOP",
+ tableName: "FeatureIndicatorsChip",
+ value: "DAITA: Multihop",
+ comment: ""
+ )
+ : NSLocalizedString(
+ "FEATURE_INDICATORS_CHIP_DAITA",
+ tableName: "FeatureIndicatorsChip",
+ value: "DAITA",
+ comment: ""
+ )
}
}
@@ -67,8 +77,12 @@ struct QuantumResistanceFeature: ChipFeature {
struct MultihopFeature: ChipFeature {
let id: FeatureType = .multihop
let state: TunnelState
+ let settings: LatestTunnelSettings
+
var isEnabled: Bool {
- state.isMultihop
+ // Multihop indicator should only be visible when user has explicitly turned on
+ // multihop, not when using multihop via DAITA.
+ state.isMultihop && settings.tunnelMultihopState.isEnabled
}
var name: String {
diff --git a/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/FeatureIndicatorsViewModel.swift b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/FeatureIndicatorsViewModel.swift
index 5b2ca87561..59a1b6ae74 100644
--- a/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/FeatureIndicatorsViewModel.swift
+++ b/ios/MullvadVPN/View controllers/Tunnel/ConnectionView/FeatureIndicatorsViewModel.swift
@@ -34,9 +34,9 @@ class FeatureIndicatorsViewModel: ChipViewModelProtocol {
case .connecting, .reconnecting, .negotiatingEphemeralPeer,
.connected, .pendingReconnect:
let features: [any ChipFeature] = [
- DaitaFeature(state: tunnelState),
+ DaitaFeature(state: tunnelState, settings: tunnelSettings),
QuantumResistanceFeature(state: tunnelState),
- MultihopFeature(state: tunnelState),
+ MultihopFeature(state: tunnelState, settings: tunnelSettings),
ObfuscationFeature(settings: tunnelSettings, state: observedState),
DNSFeature(settings: tunnelSettings),
IPOverrideFeature(state: tunnelState, overrides: ipOverrides),
diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewController.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewController.swift
index 377cd76247..053b2188b6 100644
--- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewController.swift
+++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewController.swift
@@ -180,8 +180,8 @@ extension VPNSettingsViewController: @preconcurrency VPNSettingsDataSourceDelega
enable
? "Enabling"
: "Disabling"
- ) “Local network sharing” requires restarting the VPN connection, which will disconnect you and briefly expose your traffic.
- To prevent this, manually enable Airplane Mode and turn off Wi-Fi before continuing.
+ ) “Local network sharing” requires restarting the VPN connection, which will disconnect you and briefly expose your traffic.
+ To prevent this, manually enable Airplane Mode and turn off Wi-Fi before continuing.
Would you like to continue to enable “Local network sharing”?
""",
comment: ""
diff --git a/ios/MullvadVPN/Views/List/MullvadList.swift b/ios/MullvadVPN/Views/List/MullvadList.swift
index 51b9fe808b..c951292fb5 100644
--- a/ios/MullvadVPN/Views/List/MullvadList.swift
+++ b/ios/MullvadVPN/Views/List/MullvadList.swift
@@ -12,7 +12,7 @@ struct MullvadList<Content: View, Data: RandomAccessCollection<ID>, ID: Hashable
@State var itemHeight: CGFloat = 0
var maxListHeight: CGFloat {
- var height = itemHeight * CGFloat(data.count)
+ let height = itemHeight * CGFloat(data.count)
return height > 0 ? height : .infinity
}
diff --git a/ios/MullvadVPNUITests/Pages/TunnelControlPage.swift b/ios/MullvadVPNUITests/Pages/TunnelControlPage.swift
index 89d1d8ee07..b671be2234 100644
--- a/ios/MullvadVPNUITests/Pages/TunnelControlPage.swift
+++ b/ios/MullvadVPNUITests/Pages/TunnelControlPage.swift
@@ -199,12 +199,24 @@ class TunnelControlPage: Page {
return self
}
+ /// Verify that the app attempts to connect over Multihop.
+ @discardableResult func verifyNotConnectingOverMultihop() -> Self {
+ XCTAssertFalse(app.buttons["Multihop"].exists)
+ return self
+ }
+
/// Verify that the app attempts to connect using DAITA.
@discardableResult func verifyConnectingUsingDAITA() -> Self {
XCTAssertTrue(app.buttons["DAITA"].exists)
return self
}
+ /// Verify that the app attempts to connect using DAITA.
+ @discardableResult func verifyConnectingUsingDAITAThroughMultihop() -> Self {
+ XCTAssertTrue(app.buttons["DAITA: Multihop"].exists)
+ return self
+ }
+
func getInIPAddressFromConnectionStatus() -> String {
let inAddressRow = app.staticTexts[.connectionPanelInAddressRow]
return inAddressRow.label.components(separatedBy: ":")[0]
diff --git a/ios/MullvadVPNUITests/RelayTests.swift b/ios/MullvadVPNUITests/RelayTests.swift
index f1d3c6a501..0decdb1b01 100644
--- a/ios/MullvadVPNUITests/RelayTests.swift
+++ b/ios/MullvadVPNUITests/RelayTests.swift
@@ -509,6 +509,14 @@ class RelayTests: LoggedInWithTimeUITestCase {
TunnelControlPage(app)
.waitForConnectedLabel()
+ .verifyConnectingUsingDAITAThroughMultihop()
+ .verifyNotConnectingOverMultihop()
+ .tapSelectLocationButton()
+
+ SelectLocationPage(app)
+ .tapLocationCell(withName: BaseUITestCase.testsDefaultQuicCountryName)
+
+ TunnelControlPage(app)
.verifyConnectingUsingDAITA()
.tapDisconnectButton()
}
diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift
index 8be5f883b9..68b09095a8 100644
--- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift
+++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift
@@ -446,4 +446,4 @@ extension PacketTunnelProvider: EphemeralPeerReceiving {
// and it will not try to reconnect
actor.reconnect(to: .random, reconnectReason: .connectionLoss)
}
-}
+} // swiftlint:disable:this file_length
diff --git a/ios/PacketTunnelCoreTests/MultiHopEphemeralPeerExchangerTests.swift b/ios/PacketTunnelCoreTests/MultiHopEphemeralPeerExchangerTests.swift
index 7221e72a55..c77211b5fa 100644
--- a/ios/PacketTunnelCoreTests/MultiHopEphemeralPeerExchangerTests.swift
+++ b/ios/PacketTunnelCoreTests/MultiHopEphemeralPeerExchangerTests.swift
@@ -160,7 +160,7 @@ final class MultiHopEphemeralPeerExchangerTests: XCTestCase {
keyExchanger: peerExchangeActor,
enablePostQuantum: false,
enableDaita: true
- ) { params in
+ ) { _ in
reconfigurationExpectation.fulfill()
} onFinish: {
negotiationSuccessful.fulfill()