summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJon Petersson <jon.petersson@mullvad.net>2024-10-15 16:50:04 +0200
committerBug Magnet <marco.nikic@mullvad.net>2024-10-18 14:01:07 +0200
commit74a61f552bbda718d3c952163f17676d2891f98f (patch)
tree285510b301d5353be2d3fa757c238816bced8de9
parent94051e2b51d6a3e389f43e21ae90de8f387a4cd9 (diff)
downloadmullvadvpn-74a61f552bbda718d3c952163f17676d2891f98f.tar.xz
mullvadvpn-74a61f552bbda718d3c952163f17676d2891f98f.zip
Fix crash when closing add/edit custom list view
-rw-r--r--ios/MullvadREST/Relay/MultihopDecisionFlow.swift28
-rw-r--r--ios/MullvadREST/Relay/RelayPicking.swift54
-rw-r--r--ios/MullvadREST/Relay/RelaySelectorWrapper.swift3
-rw-r--r--ios/MullvadSettings/DAITASettings.swift4
-rw-r--r--ios/MullvadVPN/Coordinators/LocationCoordinator.swift6
-rw-r--r--ios/MullvadVPN/View controllers/SelectLocation/LocationViewController.swift9
-rw-r--r--ios/MullvadVPN/View controllers/SelectLocation/LocationViewControllerWrapper.swift52
-rw-r--r--ios/MullvadVPN/View controllers/Settings/SettingsInteractor.swift22
-rw-r--r--ios/MullvadVPNTests/MullvadREST/Relay/MultihopDecisionFlowTests.swift8
-rw-r--r--ios/MullvadVPNTests/MullvadREST/Relay/RelayPickingTests.swift6
10 files changed, 93 insertions, 99 deletions
diff --git a/ios/MullvadREST/Relay/MultihopDecisionFlow.swift b/ios/MullvadREST/Relay/MultihopDecisionFlow.swift
index a8706f39fa..c8854bda3e 100644
--- a/ios/MullvadREST/Relay/MultihopDecisionFlow.swift
+++ b/ios/MullvadREST/Relay/MultihopDecisionFlow.swift
@@ -15,7 +15,7 @@ protocol MultihopDecisionFlow {
func pick(
entryCandidates: [RelayCandidate],
exitCandidates: [RelayCandidate],
- automaticDaitaRouting: Bool
+ daitaAutomaticRouting: Bool
) throws -> SelectedRelays
}
@@ -30,7 +30,7 @@ struct OneToOne: MultihopDecisionFlow {
func pick(
entryCandidates: [RelayCandidate],
exitCandidates: [RelayCandidate],
- automaticDaitaRouting: Bool
+ daitaAutomaticRouting: Bool
) throws -> SelectedRelays {
guard canHandle(entryCandidates: entryCandidates, exitCandidates: exitCandidates) else {
guard let next else {
@@ -39,7 +39,7 @@ struct OneToOne: MultihopDecisionFlow {
return try next.pick(
entryCandidates: entryCandidates,
exitCandidates: exitCandidates,
- automaticDaitaRouting: automaticDaitaRouting
+ daitaAutomaticRouting: daitaAutomaticRouting
)
}
@@ -50,7 +50,7 @@ struct OneToOne: MultihopDecisionFlow {
let exitMatch = try relayPicker.findBestMatch(from: exitCandidates)
let entryMatch = try relayPicker.findBestMatch(
from: entryCandidates,
- closeTo: automaticDaitaRouting ? exitMatch.location : nil
+ closeTo: daitaAutomaticRouting ? exitMatch.location : nil
)
return SelectedRelays(entry: entryMatch, exit: exitMatch, retryAttempt: relayPicker.connectionAttemptCount)
@@ -73,7 +73,7 @@ struct OneToMany: MultihopDecisionFlow {
func pick(
entryCandidates: [RelayCandidate],
exitCandidates: [RelayCandidate],
- automaticDaitaRouting: Bool
+ daitaAutomaticRouting: Bool
) throws -> SelectedRelays {
guard let multihopPicker = relayPicker as? MultihopPicker else {
fatalError("Could not cast picker to MultihopPicker")
@@ -86,13 +86,13 @@ struct OneToMany: MultihopDecisionFlow {
return try next.pick(
entryCandidates: entryCandidates,
exitCandidates: exitCandidates,
- automaticDaitaRouting: automaticDaitaRouting
+ daitaAutomaticRouting: daitaAutomaticRouting
)
}
- guard !automaticDaitaRouting else {
+ guard !daitaAutomaticRouting else {
return try ManyToOne(next: next, relayPicker: relayPicker)
- .pick(entryCandidates: entryCandidates, exitCandidates: exitCandidates, automaticDaitaRouting: true)
+ .pick(entryCandidates: entryCandidates, exitCandidates: exitCandidates, daitaAutomaticRouting: true)
}
let entryMatch = try multihopPicker.findBestMatch(from: entryCandidates)
@@ -118,7 +118,7 @@ struct ManyToOne: MultihopDecisionFlow {
func pick(
entryCandidates: [RelayCandidate],
exitCandidates: [RelayCandidate],
- automaticDaitaRouting: Bool
+ daitaAutomaticRouting: Bool
) throws -> SelectedRelays {
guard let multihopPicker = relayPicker as? MultihopPicker else {
fatalError("Could not cast picker to MultihopPicker")
@@ -131,7 +131,7 @@ struct ManyToOne: MultihopDecisionFlow {
return try next.pick(
entryCandidates: entryCandidates,
exitCandidates: exitCandidates,
- automaticDaitaRouting: automaticDaitaRouting
+ daitaAutomaticRouting: daitaAutomaticRouting
)
}
@@ -139,7 +139,7 @@ struct ManyToOne: MultihopDecisionFlow {
let entryMatch = try multihopPicker.exclude(
relay: exitMatch,
from: entryCandidates,
- closeTo: automaticDaitaRouting ? exitMatch.location : nil
+ closeTo: daitaAutomaticRouting ? exitMatch.location : nil
)
return SelectedRelays(entry: entryMatch, exit: exitMatch, retryAttempt: relayPicker.connectionAttemptCount)
@@ -162,7 +162,7 @@ struct ManyToMany: MultihopDecisionFlow {
func pick(
entryCandidates: [RelayCandidate],
exitCandidates: [RelayCandidate],
- automaticDaitaRouting: Bool
+ daitaAutomaticRouting: Bool
) throws -> SelectedRelays {
guard let multihopPicker = relayPicker as? MultihopPicker else {
fatalError("Could not cast picker to MultihopPicker")
@@ -175,7 +175,7 @@ struct ManyToMany: MultihopDecisionFlow {
return try next.pick(
entryCandidates: entryCandidates,
exitCandidates: exitCandidates,
- automaticDaitaRouting: automaticDaitaRouting
+ daitaAutomaticRouting: daitaAutomaticRouting
)
}
@@ -183,7 +183,7 @@ struct ManyToMany: MultihopDecisionFlow {
let entryMatch = try multihopPicker.exclude(
relay: exitMatch,
from: entryCandidates,
- closeTo: automaticDaitaRouting ? exitMatch.location : nil
+ closeTo: daitaAutomaticRouting ? exitMatch.location : nil
)
return SelectedRelays(entry: entryMatch, exit: exitMatch, retryAttempt: relayPicker.connectionAttemptCount)
diff --git a/ios/MullvadREST/Relay/RelayPicking.swift b/ios/MullvadREST/Relay/RelayPicking.swift
index a3680e28f3..4d5c98975b 100644
--- a/ios/MullvadREST/Relay/RelayPicking.swift
+++ b/ios/MullvadREST/Relay/RelayPicking.swift
@@ -46,31 +46,23 @@ struct SinglehopPicker: RelayPicking {
func pick() throws -> SelectedRelays {
do {
- let exitCandidates = try RelaySelector.WireGuard.findCandidates(
- by: constraints.exitLocations,
- in: relays,
- filterConstraint: constraints.filter,
- daitaEnabled: daitaSettings.daitaState.isEnabled
- )
-
- let match = try findBestMatch(from: exitCandidates)
- return SelectedRelays(entry: nil, exit: match, retryAttempt: connectionAttemptCount)
- } catch let error as NoRelaysSatisfyingConstraintsError where error.reason == .noDaitaRelaysFound {
- // If DAITA is on and Direct only is off, 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.shouldDoAutomaticRouting {
- var constraints = constraints
- constraints.entryLocations = .any
-
+ if daitaSettings.isAutomaticRouting {
return try MultihopPicker(
relays: relays,
constraints: constraints,
connectionAttemptCount: connectionAttemptCount,
- daitaSettings: daitaSettings,
- automaticDaitaRouting: true
+ daitaSettings: daitaSettings
).pick()
} else {
- throw error
+ let exitCandidates = try RelaySelector.WireGuard.findCandidates(
+ by: constraints.exitLocations,
+ in: relays,
+ filterConstraint: constraints.filter,
+ daitaEnabled: daitaSettings.daitaState.isEnabled
+ )
+
+ let match = try findBestMatch(from: exitCandidates)
+ return SelectedRelays(entry: nil, exit: match, retryAttempt: connectionAttemptCount)
}
}
}
@@ -81,7 +73,6 @@ struct MultihopPicker: RelayPicking {
let constraints: RelayConstraints
let connectionAttemptCount: UInt
let daitaSettings: DAITASettings
- let automaticDaitaRouting: Bool
func pick() throws -> SelectedRelays {
let exitCandidates = try RelaySelector.WireGuard.findCandidates(
@@ -117,7 +108,7 @@ struct MultihopPicker: RelayPicking {
do {
let entryCandidates = try RelaySelector.WireGuard.findCandidates(
- by: constraints.entryLocations,
+ by: daitaSettings.isAutomaticRouting ? .any : constraints.entryLocations,
in: relays,
filterConstraint: constraints.filter,
daitaEnabled: daitaSettings.daitaState.isEnabled
@@ -126,27 +117,8 @@ struct MultihopPicker: RelayPicking {
return try decisionFlow.pick(
entryCandidates: entryCandidates,
exitCandidates: exitCandidates,
- automaticDaitaRouting: automaticDaitaRouting
+ daitaAutomaticRouting: daitaSettings.isAutomaticRouting
)
- } catch let error as NoRelaysSatisfyingConstraintsError where error.reason == .noDaitaRelaysFound {
- // If DAITA is on and Direct only is off, 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.shouldDoAutomaticRouting {
- let entryCandidates = try RelaySelector.WireGuard.findCandidates(
- by: .any,
- in: relays,
- filterConstraint: constraints.filter,
- daitaEnabled: true
- )
-
- return try decisionFlow.pick(
- entryCandidates: entryCandidates,
- exitCandidates: exitCandidates,
- automaticDaitaRouting: true
- )
- } else {
- throw error
- }
}
}
diff --git a/ios/MullvadREST/Relay/RelaySelectorWrapper.swift b/ios/MullvadREST/Relay/RelaySelectorWrapper.swift
index a3fe1c216f..03fd113ecb 100644
--- a/ios/MullvadREST/Relay/RelaySelectorWrapper.swift
+++ b/ios/MullvadREST/Relay/RelaySelectorWrapper.swift
@@ -35,8 +35,7 @@ public final class RelaySelectorWrapper: RelaySelectorProtocol {
relays: relays,
constraints: tunnelSettings.relayConstraints,
connectionAttemptCount: connectionAttemptCount,
- daitaSettings: tunnelSettings.daita,
- automaticDaitaRouting: false
+ daitaSettings: tunnelSettings.daita
).pick()
}
}
diff --git a/ios/MullvadSettings/DAITASettings.swift b/ios/MullvadSettings/DAITASettings.swift
index f9d1c13892..eec7cc6ad7 100644
--- a/ios/MullvadSettings/DAITASettings.swift
+++ b/ios/MullvadSettings/DAITASettings.swift
@@ -40,11 +40,11 @@ public struct DAITASettings: Codable, Equatable {
public let daitaState: DAITAState
public let directOnlyState: DirectOnlyState
- public var shouldDoAutomaticRouting: Bool {
+ public var isAutomaticRouting: Bool {
daitaState.isEnabled && !directOnlyState.isEnabled
}
- public var shouldDoDirectOnly: Bool {
+ public var isDirectOnly: Bool {
daitaState.isEnabled && directOnlyState.isEnabled
}
diff --git a/ios/MullvadVPN/Coordinators/LocationCoordinator.swift b/ios/MullvadVPN/Coordinators/LocationCoordinator.swift
index 1a82d79b41..c9e7962224 100644
--- a/ios/MullvadVPN/Coordinators/LocationCoordinator.swift
+++ b/ios/MullvadVPN/Coordinators/LocationCoordinator.swift
@@ -52,10 +52,6 @@ class LocationCoordinator: Coordinator, Presentable, Presenting {
self.tunnelManager = tunnelManager
self.relayCacheTracker = relayCacheTracker
self.customListRepository = customListRepository
-
- super.init()
-
- addTunnelObserver()
}
func start() {
@@ -84,6 +80,8 @@ class LocationCoordinator: Coordinator, Presentable, Presenting {
}
didFinish?(self)
}
+
+ addTunnelObserver()
relayCacheTracker.addObserver(self)
if let cachedRelays = try? relayCacheTracker.getCachedRelays() {
diff --git a/ios/MullvadVPN/View controllers/SelectLocation/LocationViewController.swift b/ios/MullvadVPN/View controllers/SelectLocation/LocationViewController.swift
index 73c495259d..91305ddaea 100644
--- a/ios/MullvadVPN/View controllers/SelectLocation/LocationViewController.swift
+++ b/ios/MullvadVPN/View controllers/SelectLocation/LocationViewController.swift
@@ -118,7 +118,7 @@ final class LocationViewController: UIViewController {
dataSource?.setSelectedRelays(selectedRelays)
}
- func addDaitaInfoView() {
+ func enableDaitaAutomaticRouting() {
guard daitaInfoView == nil else { return }
let daitaInfoView = DAITAInfoView()
@@ -132,11 +132,16 @@ final class LocationViewController: UIViewController {
daitaInfoView.pinEdgesToSuperview(.all().excluding(.top))
daitaInfoView.topAnchor.constraint(equalTo: tableView.topAnchor)
}
+
+ searchBar.searchTextField.isEnabled = false
}
- func removeDaitaInfoView() {
+ func disableDaitaAutomaticRouting() {
daitaInfoView?.removeFromSuperview()
daitaInfoView = nil
+
+ searchBar.searchTextField.isEnabled = true
+ UITextField.SearchTextFieldAppearance.inactive.apply(to: searchBar)
}
// MARK: - Private
diff --git a/ios/MullvadVPN/View controllers/SelectLocation/LocationViewControllerWrapper.swift b/ios/MullvadVPN/View controllers/SelectLocation/LocationViewControllerWrapper.swift
index cd00c581cf..4c0ab086d2 100644
--- a/ios/MullvadVPN/View controllers/SelectLocation/LocationViewControllerWrapper.swift
+++ b/ios/MullvadVPN/View controllers/SelectLocation/LocationViewControllerWrapper.swift
@@ -44,7 +44,7 @@ final class LocationViewControllerWrapper: UIViewController {
}
}
- private var entryLocationViewController: LocationViewController
+ private var entryLocationViewController: LocationViewController?
private let exitLocationViewController: LocationViewController
private let segmentedControl = UISegmentedControl()
private let locationViewContainer = UIStackView()
@@ -71,20 +71,22 @@ final class LocationViewControllerWrapper: UIViewController {
selectedEntry = constraints.entryLocations.value
selectedExit = constraints.exitLocations.value
- entryLocationViewController = LocationViewController(
- customListRepository: customListRepository,
- selectedRelays: RelaySelection(),
- shouldFilterDaita: daitaSettings.shouldDoDirectOnly
- )
+ if multihopEnabled {
+ entryLocationViewController = LocationViewController(
+ customListRepository: customListRepository,
+ selectedRelays: RelaySelection(),
+ shouldFilterDaita: daitaSettings.isDirectOnly
+ )
- if daitaSettings.shouldDoAutomaticRouting {
- entryLocationViewController.addDaitaInfoView()
+ if daitaSettings.isAutomaticRouting {
+ entryLocationViewController?.enableDaitaAutomaticRouting()
+ }
}
exitLocationViewController = LocationViewController(
customListRepository: customListRepository,
selectedRelays: RelaySelection(),
- shouldFilterDaita: daitaSettings.shouldDoDirectOnly && !multihopEnabled
+ shouldFilterDaita: daitaSettings.isDirectOnly && !multihopEnabled
)
super.init(nibName: nil, bundle: nil)
@@ -121,7 +123,7 @@ final class LocationViewControllerWrapper: UIViewController {
}
if multihopEnabled {
- entryLocationViewController.setRelaysWithLocation(daitaFilteredRelays, filter: filter)
+ entryLocationViewController?.setRelaysWithLocation(daitaFilteredRelays, filter: filter)
exitLocationViewController.setRelaysWithLocation(relaysWithLocation, filter: filter)
} else {
exitLocationViewController.setRelaysWithLocation(daitaFilteredRelays, filter: filter)
@@ -139,12 +141,12 @@ final class LocationViewControllerWrapper: UIViewController {
guard multihopEnabled else { return }
setRelaysWithLocation(relaysWithLocation, filter: filter)
- entryLocationViewController.setShouldFilterDaita(settings.shouldDoDirectOnly)
+ entryLocationViewController?.setShouldFilterDaita(settings.isDirectOnly)
- if daitaSettings.shouldDoAutomaticRouting {
- entryLocationViewController.addDaitaInfoView()
- } else if daitaSettings.shouldDoDirectOnly {
- entryLocationViewController.removeDaitaInfoView()
+ if daitaSettings.isAutomaticRouting {
+ entryLocationViewController?.enableDaitaAutomaticRouting()
+ } else {
+ entryLocationViewController?.disableDaitaAutomaticRouting()
}
}
@@ -235,7 +237,11 @@ final class LocationViewControllerWrapper: UIViewController {
view.removeFromSuperview()
}
- let (selectedRelays, oldViewController, newViewController) = switch multihopContext {
+ var selectedRelays: RelaySelection
+ var oldViewController: LocationViewController?
+ var newViewController: LocationViewController?
+
+ (selectedRelays, oldViewController, newViewController) = switch multihopContext {
case .entry:
(
RelaySelection(
@@ -258,12 +264,16 @@ final class LocationViewControllerWrapper: UIViewController {
)
}
- oldViewController.removeFromParent()
- newViewController.setSelectedRelays(selectedRelays)
- addChild(newViewController)
- newViewController.didMove(toParent: self)
+ oldViewController?.removeFromParent()
+
+ if let newViewController {
+ newViewController.setSelectedRelays(selectedRelays)
- locationViewContainer.addArrangedSubview(newViewController.view)
+ addChild(newViewController)
+ newViewController.didMove(toParent: self)
+
+ locationViewContainer.addArrangedSubview(newViewController.view)
+ }
}
}
diff --git a/ios/MullvadVPN/View controllers/Settings/SettingsInteractor.swift b/ios/MullvadVPN/View controllers/Settings/SettingsInteractor.swift
index 32ef2e7b53..370f9253cd 100644
--- a/ios/MullvadVPN/View controllers/Settings/SettingsInteractor.swift
+++ b/ios/MullvadVPN/View controllers/Settings/SettingsInteractor.swift
@@ -7,6 +7,7 @@
//
import Foundation
+import MullvadREST
import MullvadSettings
final class SettingsInteractor {
@@ -46,11 +47,22 @@ final class SettingsInteractor {
var tunnelSettings = tunnelSettings
tunnelSettings.daita = settings
- // Return error if no relays could be selected.
- guard (try? tunnelManager.selectRelays(tunnelSettings: tunnelSettings)) != nil else {
- return tunnelSettings.tunnelMultihopState.isEnabled ? .multihop : .singlehop
- }
+ var compatibilityError: DAITASettingsCompatibilityError?
- return nil
+ do {
+ _ = try tunnelManager.selectRelays(tunnelSettings: tunnelSettings)
+ } catch let error as NoRelaysSatisfyingConstraintsError where error.reason == .noDaitaRelaysFound {
+ // Return error if no relays could be selected due to DAITA constraints.
+ compatibilityError = tunnelSettings.tunnelMultihopState.isEnabled ? .multihop : .singlehop
+ } catch let error as NoRelaysSatisfyingConstraintsError {
+ // Even if the constraints error is not DAITA specific, if both DAITA and Direct only are enabled,
+ // we should return a DAITA related error since the current settings would have resulted in the
+ // relay selector not being able to select a DAITA relay anyway.
+ if settings.isDirectOnly {
+ compatibilityError = tunnelSettings.tunnelMultihopState.isEnabled ? .multihop : .singlehop
+ }
+ } catch {}
+
+ return compatibilityError
}
}
diff --git a/ios/MullvadVPNTests/MullvadREST/Relay/MultihopDecisionFlowTests.swift b/ios/MullvadVPNTests/MullvadREST/Relay/MultihopDecisionFlowTests.swift
index b315b899d4..9ff0848ebd 100644
--- a/ios/MullvadVPNTests/MullvadREST/Relay/MultihopDecisionFlowTests.swift
+++ b/ios/MullvadVPNTests/MullvadREST/Relay/MultihopDecisionFlowTests.swift
@@ -99,7 +99,7 @@ class MultihopDecisionFlowTests: XCTestCase {
let selectedRelays = try oneToOne.pick(
entryCandidates: entryCandidates,
exitCandidates: exitCandidates,
- automaticDaitaRouting: false
+ daitaAutomaticRouting: false
)
XCTAssertEqual(selectedRelays.entry?.hostname, "se2-wireguard")
@@ -115,7 +115,7 @@ class MultihopDecisionFlowTests: XCTestCase {
let selectedRelays = try oneToMany.pick(
entryCandidates: entryCandidates,
exitCandidates: exitCandidates,
- automaticDaitaRouting: false
+ daitaAutomaticRouting: false
)
XCTAssertEqual(selectedRelays.entry?.hostname, "se2-wireguard")
@@ -131,7 +131,7 @@ class MultihopDecisionFlowTests: XCTestCase {
let selectedRelays = try manyToOne.pick(
entryCandidates: entryCandidates,
exitCandidates: exitCandidates,
- automaticDaitaRouting: false
+ daitaAutomaticRouting: false
)
XCTAssertEqual(selectedRelays.entry?.hostname, "se6-wireguard")
@@ -147,7 +147,7 @@ class MultihopDecisionFlowTests: XCTestCase {
let selectedRelays = try manyToMany.pick(
entryCandidates: entryCandidates,
exitCandidates: exitCandidates,
- automaticDaitaRouting: false
+ daitaAutomaticRouting: false
)
if selectedRelays.exit.hostname == "se2-wireguard" {
diff --git a/ios/MullvadVPNTests/MullvadREST/Relay/RelayPickingTests.swift b/ios/MullvadVPNTests/MullvadREST/Relay/RelayPickingTests.swift
index fb42ba17f7..00191ecaf4 100644
--- a/ios/MullvadVPNTests/MullvadREST/Relay/RelayPickingTests.swift
+++ b/ios/MullvadVPNTests/MullvadREST/Relay/RelayPickingTests.swift
@@ -45,8 +45,7 @@ class RelayPickingTests: XCTestCase {
relays: sampleRelays,
constraints: constraints,
connectionAttemptCount: 0,
- daitaSettings: DAITASettings(),
- automaticDaitaRouting: false
+ daitaSettings: DAITASettings()
)
let selectedRelays = try picker.pick()
@@ -65,8 +64,7 @@ class RelayPickingTests: XCTestCase {
relays: sampleRelays,
constraints: constraints,
connectionAttemptCount: 0,
- daitaSettings: DAITASettings(),
- automaticDaitaRouting: false
+ daitaSettings: DAITASettings()
)
XCTAssertThrowsError(