diff options
| author | Steffen Ernst <steffen.ernst@mullvad.net> | 2025-02-06 17:02:58 +0100 |
|---|---|---|
| committer | Bug Magnet <marco.nikic@mullvad.net> | 2025-02-18 11:50:42 +0100 |
| commit | 235a3b54088bc99b26f9519d3b337e040bb38bd8 (patch) | |
| tree | d2d2e72febfd10a7ab265f012756e2bd188f8659 /ios | |
| parent | 0f629c6d1158301f6f0314630e1d4f80ecc100ae (diff) | |
| download | mullvadvpn-235a3b54088bc99b26f9519d3b337e040bb38bd8.tar.xz mullvadvpn-235a3b54088bc99b26f9519d3b337e040bb38bd8.zip | |
Add local network sharing to settings and hook up
Diffstat (limited to 'ios')
16 files changed, 229 insertions, 41 deletions
diff --git a/ios/MullvadSettings/TunnelSettings.swift b/ios/MullvadSettings/TunnelSettings.swift index 17670bc032..d950ce39a4 100644 --- a/ios/MullvadSettings/TunnelSettings.swift +++ b/ios/MullvadSettings/TunnelSettings.swift @@ -58,7 +58,7 @@ public enum SchemaVersion: Int, Equatable, Sendable { case .v3: return .v4 case .v4: return .v5 case .v5: return .v6 - case .v6: return .v6 + case .v6: return .v7 case .v7: return .v7 } } diff --git a/ios/MullvadSettings/TunnelSettingsStrategy.swift b/ios/MullvadSettings/TunnelSettingsStrategy.swift index 830573e4f0..05db9132a4 100644 --- a/ios/MullvadSettings/TunnelSettingsStrategy.swift +++ b/ios/MullvadSettings/TunnelSettingsStrategy.swift @@ -9,19 +9,42 @@ import Foundation public protocol TunnelSettingsStrategyProtocol: Sendable { func shouldReconnectToNewRelay(oldSettings: LatestTunnelSettings, newSettings: LatestTunnelSettings) -> Bool + func getReconnectionStrategy( + oldSettings: LatestTunnelSettings, + newSettings: LatestTunnelSettings + ) -> TunnelSettingsReconnectionStrategy } public struct TunnelSettingsStrategy: TunnelSettingsStrategyProtocol, Sendable { public init() {} + public func shouldReconnectToNewRelay( oldSettings: LatestTunnelSettings, newSettings: LatestTunnelSettings ) -> Bool { + getReconnectionStrategy(oldSettings: oldSettings, newSettings: newSettings) != .noReconnect + } + + public func getReconnectionStrategy( + oldSettings: LatestTunnelSettings, + newSettings: LatestTunnelSettings + ) -> TunnelSettingsReconnectionStrategy { + if oldSettings.localNetworkSharing != newSettings.localNetworkSharing { + return .hardReconnect + } switch (oldSettings, newSettings) { case let (old, new) where old != new: - true + return .softReconnect default: - false + return .noReconnect } } } + +public enum TunnelSettingsReconnectionStrategy { + case noReconnect + case softReconnect +// This will fully disconnect and start a new connection +// Attention: This will leak traffic!!! + case hardReconnect +} diff --git a/ios/MullvadSettings/TunnelSettingsUpdate.swift b/ios/MullvadSettings/TunnelSettingsUpdate.swift index c6091453de..237e914617 100644 --- a/ios/MullvadSettings/TunnelSettingsUpdate.swift +++ b/ios/MullvadSettings/TunnelSettingsUpdate.swift @@ -10,6 +10,7 @@ import Foundation import MullvadTypes public enum TunnelSettingsUpdate: Sendable { + case localNetworkSharing(Bool) case dnsSettings(DNSSettings) case obfuscation(WireGuardObfuscationSettings) case relayConstraints(RelayConstraints) @@ -21,6 +22,8 @@ public enum TunnelSettingsUpdate: Sendable { extension TunnelSettingsUpdate { public func apply(to settings: inout LatestTunnelSettings) { switch self { + case let .localNetworkSharing(enabled): + settings.localNetworkSharing = enabled case let .dnsSettings(newDNSSettings): settings.dnsSettings = newDNSSettings case let .obfuscation(newObfuscationSettings): @@ -38,6 +41,7 @@ extension TunnelSettingsUpdate { public var subjectName: String { switch self { + case .localNetworkSharing: "Local network sharing" case .dnsSettings: "DNS settings" case .obfuscation: "obfuscation settings" case .relayConstraints: "relay constraints" diff --git a/ios/MullvadSettings/TunnelSettingsV6.swift b/ios/MullvadSettings/TunnelSettingsV6.swift index 9bfccce2df..3ec50d5658 100644 --- a/ios/MullvadSettings/TunnelSettingsV6.swift +++ b/ios/MullvadSettings/TunnelSettingsV6.swift @@ -45,12 +45,14 @@ public struct TunnelSettingsV6: Codable, Equatable, TunnelSettings, Sendable { } public func upgradeToNextVersion() -> any TunnelSettings { - TunnelSettingsV7(relayConstraints: relayConstraints, - dnsSettings: dnsSettings, - wireGuardObfuscation: wireGuardObfuscation, - tunnelQuantumResistance: tunnelQuantumResistance, - tunnelMultihopState: tunnelMultihopState, - daita: daita, - localNetworkSharing: false) + TunnelSettingsV7( + relayConstraints: relayConstraints, + dnsSettings: dnsSettings, + wireGuardObfuscation: wireGuardObfuscation, + tunnelQuantumResistance: tunnelQuantumResistance, + tunnelMultihopState: tunnelMultihopState, + daita: daita, + localNetworkSharing: false + ) } } diff --git a/ios/MullvadSettings/TunnelSettingsV7.swift b/ios/MullvadSettings/TunnelSettingsV7.swift index 09314d3014..bdd23f707b 100644 --- a/ios/MullvadSettings/TunnelSettingsV7.swift +++ b/ios/MullvadSettings/TunnelSettingsV7.swift @@ -6,7 +6,6 @@ // Copyright © 2025 Mullvad VPN AB. All rights reserved. // - import Foundation import MullvadTypes diff --git a/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift b/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift index ddd384e44a..27e68b1b67 100644 --- a/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift +++ b/ios/MullvadVPN/Classes/AccessbilityIdentifier.swift @@ -175,6 +175,7 @@ public enum AccessibilityIdentifier: Equatable { case statusImageView // DNS settings + case localNetworkSharing case dnsSettings case ipOverrides case wireGuardCustomPort diff --git a/ios/MullvadVPN/TunnelManager/StartTunnelOperation.swift b/ios/MullvadVPN/TunnelManager/StartTunnelOperation.swift index 6c2b97ac06..d3c88eb0a3 100644 --- a/ios/MullvadVPN/TunnelManager/StartTunnelOperation.swift +++ b/ios/MullvadVPN/TunnelManager/StartTunnelOperation.swift @@ -9,6 +9,7 @@ import Foundation import MullvadLogging import MullvadREST +import MullvadSettings import NetworkExtension import Operations import PacketTunnelCore @@ -48,7 +49,7 @@ class StartTunnelOperation: ResultOperation<Void>, @unchecked Sendable { finish(result: .success(())) - case .disconnected, .pendingReconnect: + case .disconnected, .pendingReconnect, .waitingForConnectivity: makeTunnelProviderAndStartTunnel { error in self.finish(result: error.map { .failure($0) } ?? .success(())) } @@ -106,28 +107,11 @@ class StartTunnelOperation: ResultOperation<Void>, @unchecked Sendable { ) { let persistentTunnels = interactor.getPersistentTunnels() let tunnel = persistentTunnels.first ?? interactor.createNewTunnel() - let configuration = Self.makeTunnelConfiguration() + let configuration = TunnelConfiguration(excludeLocalNetworks: interactor.settings.localNetworkSharing) tunnel.setConfiguration(configuration) tunnel.saveToPreferences { error in completionHandler(error.map { .failure($0) } ?? .success(tunnel)) } } - - private class func makeTunnelConfiguration() -> TunnelConfiguration { - let protocolConfig = NETunnelProviderProtocol() - protocolConfig.providerBundleIdentifier = ApplicationTarget.packetTunnel.bundleIdentifier - protocolConfig.serverAddress = "" - - let alwaysOnRule = NEOnDemandRuleConnect() - alwaysOnRule.interfaceTypeMatch = .any - - return TunnelConfiguration( - isEnabled: true, - localizedDescription: "WireGuard", - protocolConfiguration: protocolConfig, - onDemandRules: [alwaysOnRule], - isOnDemandEnabled: true - ) - } } diff --git a/ios/MullvadVPN/TunnelManager/StopTunnelOperation.swift b/ios/MullvadVPN/TunnelManager/StopTunnelOperation.swift index c4d727fe3e..bed05b1d1f 100644 --- a/ios/MullvadVPN/TunnelManager/StopTunnelOperation.swift +++ b/ios/MullvadVPN/TunnelManager/StopTunnelOperation.swift @@ -11,6 +11,7 @@ import Operations class StopTunnelOperation: ResultOperation<Void>, @unchecked Sendable { private let interactor: TunnelInteractor + var isOnDemandEnabled = false init( dispatchQueue: DispatchQueue, @@ -50,8 +51,7 @@ class StopTunnelOperation: ResultOperation<Void>, @unchecked Sendable { return } - // Disable on-demand when stopping the tunnel to prevent it from coming back up - tunnel.isOnDemandEnabled = false + tunnel.isOnDemandEnabled = isOnDemandEnabled tunnel.saveToPreferences { error in self.dispatchQueue.async { diff --git a/ios/MullvadVPN/TunnelManager/TunnelConfiguration.swift b/ios/MullvadVPN/TunnelManager/TunnelConfiguration.swift index 38c7c3b56b..274e9439be 100644 --- a/ios/MullvadVPN/TunnelManager/TunnelConfiguration.swift +++ b/ios/MullvadVPN/TunnelManager/TunnelConfiguration.swift @@ -16,6 +16,23 @@ struct TunnelConfiguration { var onDemandRules: [NEOnDemandRule] var isOnDemandEnabled: Bool + init(excludeLocalNetworks: Bool, isOnDemandEnabled: Bool = true) { + let protocolConfig = NETunnelProviderProtocol() + protocolConfig.providerBundleIdentifier = ApplicationTarget.packetTunnel.bundleIdentifier + protocolConfig.serverAddress = "" + protocolConfig.includeAllNetworks = true + protocolConfig.excludeLocalNetworks = excludeLocalNetworks + + let alwaysOnRule = NEOnDemandRuleConnect() + alwaysOnRule.interfaceTypeMatch = .any + + isEnabled = true + localizedDescription = "WireGuard" + protocolConfiguration = protocolConfig + onDemandRules = [alwaysOnRule] + self.isOnDemandEnabled = isOnDemandEnabled + } + func apply(to manager: TunnelProviderManagerType) { manager.isEnabled = isEnabled manager.localizedDescription = localizedDescription diff --git a/ios/MullvadVPN/TunnelManager/TunnelManager.swift b/ios/MullvadVPN/TunnelManager/TunnelManager.swift index 2039d1297e..d12be3b424 100644 --- a/ios/MullvadVPN/TunnelManager/TunnelManager.swift +++ b/ios/MullvadVPN/TunnelManager/TunnelManager.swift @@ -78,6 +78,8 @@ final class TunnelManager: StorePaymentObserver, @unchecked Sendable { /// Last processed device check. private var lastPacketTunnelKeyRotation: Date? + private var observer: TunnelObserver? + // MARK: - Initialization init( @@ -250,7 +252,7 @@ final class TunnelManager: StorePaymentObserver, @unchecked Sendable { operationQueue.addOperation(operation) } - func stopTunnel(completionHandler: ((Error?) -> Void)? = nil) { + func stopTunnel(isOnDemandEnabled: Bool = false, completionHandler: ((Error?) -> Void)? = nil) { let operation = StopTunnelOperation( dispatchQueue: internalQueue, interactor: TunnelInteractorProxy(self) @@ -272,7 +274,7 @@ final class TunnelManager: StorePaymentObserver, @unchecked Sendable { completionHandler?(result.error) } - + operation.isOnDemandEnabled = isOnDemandEnabled operation.addObserver(BackgroundObserver( backgroundTaskProvider: backgroundTaskProvider, name: "Stop tunnel", @@ -320,6 +322,35 @@ final class TunnelManager: StorePaymentObserver, @unchecked Sendable { operationQueue.addOperation(operation) } + func reapplyTunnelConfiguration() { + guard let tunnel else { return } + if self.tunnelStatus.state.isSecured { + let observer = TunnelBlockObserver( + didUpdateTunnelStatus: { _, status in + if case .disconnected = status.state { + if let observer = self.observer { + self.removeObserver(observer) + self.observer = nil + } + self.startTunnel() + } + } + ) + addObserver(observer) + self.observer = observer + + let configuration = TunnelConfiguration( + includeAllNetworks: settings.includeAllNetworks, + excludeLocalNetworks: settings.localNetworkSharing + ) + + tunnel.setConfiguration(configuration) + tunnel.saveToPreferences { _ in + self.stopTunnel(isOnDemandEnabled: true) + } + } + } + func setNewAccount() async throws -> StoredAccountData { try await setAccount(action: .new)! } @@ -940,11 +971,18 @@ final class TunnelManager: StorePaymentObserver, @unchecked Sendable { modificationBlock(&updatedSettings) self.setSettings(updatedSettings, persist: true) - self.reconnectTunnel( - selectNewRelay: settingsStrategy - .shouldReconnectToNewRelay(oldSettings: currentSettings, newSettings: updatedSettings), - completionHandler: nil + let reconnectionStrategy = settingsStrategy.getReconnectionStrategy( + oldSettings: currentSettings, + newSettings: updatedSettings ) + switch reconnectionStrategy { + case .noReconnect: + self.reconnectTunnel(selectNewRelay: false) + case .softReconnect: + self.reconnectTunnel(selectNewRelay: true) + case .hardReconnect: + self.reapplyTunnelConfiguration() + } } operation.completionBlock = { diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsCellFactory.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsCellFactory.swift index fff545a07e..faed3382ac 100644 --- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsCellFactory.swift +++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsCellFactory.swift @@ -16,6 +16,7 @@ protocol VPNSettingsCellEventHandler { func selectCustomPortEntry(_ port: UInt16) -> Bool func selectObfuscationState(_ state: WireGuardObfuscationState) func switchMultihop(_ state: MultihopState) + func setLocalNetworkSettings(_ enabled: Bool, onCancel: @escaping () -> Void) } @MainActor @@ -32,7 +33,6 @@ final class VPNSettingsCellFactory: @preconcurrency CellFactoryProtocol { func makeCell(for item: VPNSettingsDataSource.Item, indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: item.reuseIdentifier.rawValue, for: indexPath) - configureCell(cell, item: item, indexPath: indexPath) return cell @@ -42,6 +42,24 @@ final class VPNSettingsCellFactory: @preconcurrency CellFactoryProtocol { func configureCell(_ cell: UITableViewCell, item: VPNSettingsDataSource.Item, indexPath: IndexPath) { (cell as? SettingsCell)?.detailTitleLabel.accessibilityIdentifier = nil switch item { + case .localNetworkSharing: + guard let cell = cell as? SettingsSwitchCell else { return } + cell.infoButtonHandler = { self.delegate?.showInfo(for: .localNetworkSharing) } + cell.action = { enable in + self.delegate?.setLocalNetworkSettings(enable) { + cell.setOn(self.viewModel.localNetworkSharing, animated: true) + } + } + + cell.titleLabel.text = NSLocalizedString( + "LOCAL_NETWORK_SHARING_CELL_LABEL", + tableName: "VPNSettings", + value: "Local network sharing", + comment: "" + ) + cell.setAccessibilityIdentifier(item.accessibilityIdentifier) + cell.setOn(viewModel.localNetworkSharing, animated: true) + case .dnsSettings: guard let cell = cell as? SettingsCell else { return } diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift index 236ecddb62..50b7aeb558 100644 --- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift +++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSource.swift @@ -16,6 +16,7 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< typealias InfoButtonHandler = (Item) -> Void enum CellReuseIdentifiers: String, CaseIterable { + case localNetworkSharing case dnsSettings case ipOverrides case wireGuardPort @@ -27,6 +28,8 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< var reusableViewClass: AnyClass { switch self { + case .localNetworkSharing: + return SettingsSwitchCell.self case .dnsSettings: return SettingsCell.self case .ipOverrides: @@ -56,6 +59,7 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< } enum Section: String, Hashable, CaseIterable { + case localNetworkSharing case dnsSettings case ipOverrides case wireGuardPorts @@ -65,6 +69,7 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< } enum Item: Hashable { + case localNetworkSharing(_ enabled: Bool) case dnsSettings case ipOverrides case wireGuardPort(_ port: UInt16?) @@ -108,6 +113,8 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< var accessibilityIdentifier: AccessibilityIdentifier { switch self { + case .localNetworkSharing: + return .localNetworkSharing case .dnsSettings: return .dnsSettings case .ipOverrides: @@ -137,6 +144,8 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< var reuseIdentifier: CellReuseIdentifiers { switch self { + case .localNetworkSharing: + return .localNetworkSharing case .dnsSettings: return .dnsSettings case .ipOverrides: @@ -378,7 +387,7 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< let sectionIdentifier = snapshot().sectionIdentifiers[section] switch sectionIdentifier { - case .dnsSettings, .ipOverrides, .privacyAndSecurity: + case .dnsSettings, .ipOverrides, .privacyAndSecurity, .localNetworkSharing: return .leastNonzeroMagnitude default: return tableView.estimatedRowHeight @@ -391,7 +400,7 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< return switch sectionIdentifier { // 0 due to there already being a separator between .dnsSettings and .ipOverrides. case .dnsSettings: 0 - case .ipOverrides, .quantumResistance: UITableView.automaticDimension + case .ipOverrides, .quantumResistance, .localNetworkSharing: UITableView.automaticDimension default: 0.5 } } @@ -445,6 +454,7 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< snapshot.appendSections(Section.allCases) snapshot.appendItems([.dnsSettings], toSection: .dnsSettings) snapshot.appendItems([.ipOverrides], toSection: .ipOverrides) + snapshot.appendItems([.localNetworkSharing(viewModel.localNetworkSharing)], toSection: .localNetworkSharing) applySnapshot(snapshot, animated: animated, completion: completion) } @@ -601,6 +611,17 @@ final class VPNSettingsDataSource: UITableViewDiffableDataSource< } extension VPNSettingsDataSource: @preconcurrency VPNSettingsCellEventHandler { + func setLocalNetworkSettings(_ enabled: Bool, onCancel: @escaping () -> Void) { + delegate?.showLocalNetworkSharingWarning(enabled) { accepted in + if accepted { + self.delegate?.didUpdateTunnelSettings(.localNetworkSharing(enabled)) + self.viewModel.setLocalNetworkSharing(enabled) + } else { + onCancel() + } + } + } + func showInfo(for button: VPNSettingsInfoButtonItem) { delegate?.showInfo(for: button) } diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSourceDelegate.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSourceDelegate.swift index bf3856b464..45194c8582 100644 --- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSourceDelegate.swift +++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsDataSourceDelegate.swift @@ -22,4 +22,5 @@ protocol VPNSettingsDataSourceDelegate: AnyObject { func showIPOverrides() func didSelectWireGuardPort(_ port: UInt16?) func humanReadablePortRepresentation() -> String + func showLocalNetworkSharingWarning(_ enable: Bool, completion: @escaping (Bool) -> Void) } diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsInfoButtonItem.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsInfoButtonItem.swift index 2b87f5b67c..9df6ed2a41 100644 --- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsInfoButtonItem.swift +++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsInfoButtonItem.swift @@ -9,6 +9,7 @@ import Foundation enum VPNSettingsInfoButtonItem: CustomStringConvertible { + case localNetworkSharing case contentBlockers case blockMalware case wireGuardPorts(String) @@ -19,6 +20,25 @@ enum VPNSettingsInfoButtonItem: CustomStringConvertible { var description: String { switch self { + case .localNetworkSharing: + NSLocalizedString( + "VPN_SETTINGS_LOCAL_NETWORK_SHARING", + tableName: "LocalNetworkSharing", + value: """ + This feature allows access to other devices on the local network, such as for sharing, printing, streaming, etc. + + It does this by allowing network communication outside the tunnel to local multicast and broadcast ranges as well as to and from these private IP ranges: + 10.0.0.0/8 + 172.16.0.0/12 + 192.168.0.0/16 + 169.254.0.0/16 + fe80::/10 + fc00::/7 + + Attention: toggling “Local network sharing” requires restarting the VPN connection. + """, + comment: "" + ) case .contentBlockers: NSLocalizedString( "VPN_SETTINGS_CONTENT_BLOCKERS_GENERAL", diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewController.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewController.swift index 4b55c94f46..df8f8c2981 100644 --- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewController.swift +++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewController.swift @@ -159,4 +159,56 @@ extension VPNSettingsViewController: @preconcurrency VPNSettingsDataSourceDelega func didSelectWireGuardPort(_ port: UInt16?) { interactor.setPort(port) } + + func showLocalNetworkSharingWarning(_ enable: Bool, completion: @escaping (Bool) -> Void) { + if interactor.tunnelManager.tunnelStatus.state.isSecured { + let description = NSLocalizedString( + "VPN_SETTINGS_LOCAL_NETWORK_SHARING_WARNING", + tableName: "LocalNetworkSharing", + value: """ + \( + 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. + + Would you like to continue to enable “Local network sharing”? + """, + comment: "" + ) + + let presentation = AlertPresentation( + id: "vpn-settings-local-network-sharing-warning", + icon: .info, + message: description, + buttons: [ + AlertAction( + title: NSLocalizedString( + "VPN_SETTINGS_LOCAL_NETWORK_SHARING_OK_ACTION", + tableName: "ContentBlockers", + value: "Yes, continue", + comment: "" + ), + style: .destructive, + handler: { completion(true) } + ), + AlertAction( + title: NSLocalizedString( + "VPN_SETTINGS_LOCAL_NETWORK_SHARING_CANCEL_ACTION", + tableName: "ContentBlockers", + value: "Cancel", + comment: "" + ), + style: .default, + handler: { completion(false) } + ), + ] + ) + + alertPresenter.showAlert(presentation: presentation, animated: true) + } else { + completion(true) + } + } } diff --git a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewModel.swift b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewModel.swift index 05f0b2652d..cf791e0e29 100644 --- a/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewModel.swift +++ b/ios/MullvadVPN/View controllers/VPNSettings/VPNSettingsViewModel.swift @@ -103,6 +103,8 @@ struct VPNSettingsViewModel: Equatable { private(set) var quantumResistance: TunnelQuantumResistance private(set) var multihopState: MultihopState + private(set) var localNetworkSharing: Bool + static let defaultWireGuardPorts: [UInt16] = [51820, 53] var enabledBlockersCount: Int { @@ -195,6 +197,10 @@ struct VPNSettingsViewModel: Equatable { multihopState = newState } + mutating func setLocalNetworkSharing(_ newState: Bool) { + localNetworkSharing = newState + } + /// Precondition for enabling Custom DNS. var customDNSPrecondition: CustomDNSPrecondition { if blockAdvertising || blockTracking || blockMalware || @@ -252,6 +258,8 @@ struct VPNSettingsViewModel: Equatable { quantumResistance = tunnelSettings.tunnelQuantumResistance multihopState = tunnelSettings.tunnelMultihopState + + localNetworkSharing = tunnelSettings.localNetworkSharing } /// Produce merged view model, keeping entry `identifier` for matching DNS entries and |
