diff options
| author | Jon Petersson <jon.petersson@mullvad.net> | 2024-10-15 16:50:04 +0200 |
|---|---|---|
| committer | Bug Magnet <marco.nikic@mullvad.net> | 2024-10-18 14:01:07 +0200 |
| commit | 74a61f552bbda718d3c952163f17676d2891f98f (patch) | |
| tree | 285510b301d5353be2d3fa757c238816bced8de9 | |
| parent | 94051e2b51d6a3e389f43e21ae90de8f387a4cd9 (diff) | |
| download | mullvadvpn-74a61f552bbda718d3c952163f17676d2891f98f.tar.xz mullvadvpn-74a61f552bbda718d3c952163f17676d2891f98f.zip | |
Fix crash when closing add/edit custom list view
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( |
