diff options
| -rw-r--r-- | ios/MullvadVPN.xcodeproj/project.pbxproj | 3 | ||||
| -rw-r--r-- | ios/MullvadVPN/AppDelegate.swift | 391 | ||||
| -rw-r--r-- | ios/MullvadVPN/ConnectViewController.swift | 182 | ||||
| -rw-r--r-- | ios/MullvadVPN/ConsentViewController.swift | 6 | ||||
| -rw-r--r-- | ios/MullvadVPN/RelayCache.swift | 2 | ||||
| -rw-r--r-- | ios/MullvadVPN/RootContainerViewController.swift | 8 |
6 files changed, 369 insertions, 223 deletions
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index 63276bead6..55425ced99 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -153,8 +153,8 @@ 58B0A2AA238EE6A900BC001D /* RelaySelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58781CD422AFBA39009B9D8E /* RelaySelector.swift */; }; 58B0A2AC238EE6D500BC001D /* IPAddress+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5840250022B1124600E4CFEC /* IPAddress+Codable.swift */; }; 58B0A2AD238EE6EC00BC001D /* MullvadEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5840250322B11AB700E4CFEC /* MullvadEndpoint.swift */; }; - 58B67B482602079E008EF58E /* RelaySelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58781CD422AFBA39009B9D8E /* RelaySelector.swift */; }; 58B43C1925F77DB60002C8C3 /* ConnectMainContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58B43C1825F77DB60002C8C3 /* ConnectMainContentView.swift */; }; + 58B67B482602079E008EF58E /* RelaySelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58781CD422AFBA39009B9D8E /* RelaySelector.swift */; }; 58B8743222B25A7600015324 /* WireguardAssociatedAddresses.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58B8743122B25A7600015324 /* WireguardAssociatedAddresses.swift */; }; 58B8743B22B788D200015324 /* PacketTunnelSettingsGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58B8743722B25EAB00015324 /* PacketTunnelSettingsGenerator.swift */; }; 58B9814E24FEA70D00C0D59E /* WireguardKeysViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 58B9814D24FEA70D00C0D59E /* WireguardKeysViewController.xib */; }; @@ -879,7 +879,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 58D9AF6B2501111800B6FAB5 /* ConnectViewController.xib in Resources */, 58F3C0A624A50157003E76BE /* relays.json in Resources */, 58CE5E6E224146210008646E /* LaunchScreen.storyboard in Resources */, 58CE5E6B224146210008646E /* Assets.xcassets in Resources */, diff --git a/ios/MullvadVPN/AppDelegate.swift b/ios/MullvadVPN/AppDelegate.swift index f36a4c8560..3276d5c433 100644 --- a/ios/MullvadVPN/AppDelegate.swift +++ b/ios/MullvadVPN/AppDelegate.swift @@ -14,20 +14,39 @@ import Logging class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - var rootContainer: RootContainerViewController? + + private var logger: Logger? #if targetEnvironment(simulator) - let simulatorTunnelProvider = SimulatorTunnelProviderHost() + private let simulatorTunnelProvider = SimulatorTunnelProviderHost() #endif #if DEBUG private let packetTunnelLogForwarder = LogStreamer<UTF8>(fileURLs: [ApplicationConfiguration.packetTunnelLogFileURL!]) #endif + private var rootContainer: RootContainerViewController? + private var selectLocationViewController: SelectLocationViewController? + private var connectController: ConnectViewController? + + private var cachedRelays: CachedRelays? { + didSet { + if let cachedRelays = cachedRelays { + self.selectLocationViewController?.setCachedRelays(cachedRelays) + } + } + } + private var relayConstraints: RelayConstraints? + private let alertPresenter = AlertPresenter() + + // MARK: - Application lifecycle + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Setup logging initLoggingSystem(bundleIdentifier: Bundle.main.bundleIdentifier!) + self.logger = Logger(label: "AppDelegate") + #if DEBUG let stdoutStream = TextFileOutputStream.standardOutputStream() packetTunnelLogForwarder.start { (str) in @@ -49,43 +68,54 @@ class AppDelegate: UIResponder, UIApplicationDelegate { self.window?.rootViewController = launchController // Update relays + RelayCache.shared.addObserver(self) RelayCache.shared.updateRelays() + // Load initial relays + RelayCache.shared.read { (result) in + DispatchQueue.main.async { + switch result { + case .success(let cachedRelays): + self.cachedRelays = cachedRelays + + case .failure(let error): + self.logger?.error(chainedError: error, message: "Failed to fetch initial relays") + } + } + } + // Load tunnels - let accountToken = Account.shared.token - TunnelManager.shared.loadTunnel(accountToken: accountToken) { (result) in + TunnelManager.shared.loadTunnel(accountToken: Account.shared.token) { (result) in DispatchQueue.main.async { if case .failure(let error) = result { fatalError(error.displayChain(message: "Failed to load the tunnel for account")) } - let rootViewController = RootContainerViewController() - rootViewController.delegate = self + TunnelManager.shared.getRelayConstraints { (result) in + DispatchQueue.main.async { + switch result { + case .success(let relayConstraints): + self.relayConstraints = relayConstraints - let showMainController = { (_ animated: Bool) in - self.showConnectController(in: rootViewController, animated: animated) { - self.didPresentTheMainController() - } - } + case .failure(let error): + self.logger?.error(chainedError: error, message: "Failed to load relay constraints") + } - if Account.shared.isAgreedToTermsOfService { - showMainController(false) - } else { - self.showTermsOfService(in: rootViewController) { - Account.shared.agreeToTermsOfService() + self.rootContainer = RootContainerViewController() + self.rootContainer?.delegate = self + self.window?.rootViewController = self.rootContainer - showMainController(true) + self.setupPhoneUI() } } - - self.window?.rootViewController = rootViewController - self.rootContainer = rootViewController } } // Show the window self.window?.makeKeyAndVisible() + startPaymentQueueHandling() + return true } @@ -93,79 +123,207 @@ class AppDelegate: UIResponder, UIApplicationDelegate { TunnelManager.shared.refreshTunnelState(completionHandler: nil) } - private func didPresentTheMainController() { - let paymentManager = AppStorePaymentManager.shared - paymentManager.delegate = self + // MARK: - Private - paymentManager.startPaymentQueueMonitoring() - Account.shared.startPaymentMonitoring(with: paymentManager) + private func setupPhoneUI() { + let showNextController = { [weak self] (_ animated: Bool) in + guard let self = self else { return } + + let loginViewController = self.makeLoginController() + var viewControllers: [UIViewController] = [loginViewController] + + if Account.shared.isLoggedIn { + let connectController = self.makeConnectViewController() + viewControllers.append(connectController) + self.connectController = connectController + } + + self.rootContainer?.setViewControllers(viewControllers, animated: animated) { + self.showAccountSettingsControllerIfAccountExpired() + } + } + + if Account.shared.isAgreedToTermsOfService { + showNextController(false) + } else { + let consentViewController = self.makeConsentController { (consentController) in + showNextController(true) + } + + self.rootContainer?.setViewControllers([consentViewController], animated: false) + } } - private func showTermsOfService(in rootViewController: RootContainerViewController, completionHandler: @escaping () -> Void) { - let consentViewController = ConsentViewController() - consentViewController.completionHandler = completionHandler + private func makeConnectViewController() -> ConnectViewController { + let connectController = ConnectViewController() + connectController.delegate = self - rootViewController.setViewControllers([consentViewController], animated: false) + return connectController } - private func showConnectController( - in rootViewController: RootContainerViewController, - animated: Bool, - completionHandler: @escaping () -> Void) - { - let loginViewController = LoginViewController() - loginViewController.delegate = self + private func makeSelectLocationController() -> SelectLocationViewController { + let selectLocationController = SelectLocationViewController() + selectLocationController.delegate = self - var viewControllers: [UIViewController] = [loginViewController] + if let cachedRelays = cachedRelays { + selectLocationController.setCachedRelays(cachedRelays) + } - if Account.shared.isLoggedIn { - viewControllers.append(ConnectViewController()) + if let relayLocation = relayConstraints?.location.value { + selectLocationController.setSelectedRelayLocation(relayLocation, animated: false, scrollPosition: .middle) } - rootViewController.setViewControllers(viewControllers, animated: animated, completion: completionHandler) + return selectLocationController } -} + private func makeConsentController(completion: @escaping (UIViewController) -> Void) -> ConsentViewController { + let consentViewController = ConsentViewController() -extension AppDelegate: RootContainerViewControllerDelegate { + consentViewController.completionHandler = { (consentViewController) in + Account.shared.agreeToTermsOfService() + completion(consentViewController) + } - func rootContainerViewControllerShouldShowSettings(_ controller: RootContainerViewController, navigateTo route: SettingsNavigationRoute?, animated: Bool) { - let settingsController = SettingsViewController(style: .grouped) - settingsController.settingsDelegate = self + return consentViewController + } - let navController = SettingsNavigationController(navigationBarClass: CustomNavigationBar.self, toolbarClass: nil) - navController.pushViewController(settingsController, animated: false) + private func makeLoginController() -> LoginViewController { + let controller = LoginViewController() + controller.delegate = self + + return controller + } + + private func makeSettingsNavigationController(route: SettingsNavigationRoute?) -> SettingsNavigationController { + let navController = SettingsNavigationController() + navController.settingsDelegate = self + navController.presentationController?.delegate = navController if let route = route { - settingsController.navigate(to: route) + navController.navigate(to: route, animated: false) } - controller.present(navController, animated: animated) + return navController + } + + private func showAccountSettingsControllerIfAccountExpired() { + guard let accountExpiry = Account.shared.expiry, AccountExpiry(date: accountExpiry).isExpired else { return } + + rootContainer?.showSettings(navigateTo: .account, animated: true) + } + + private func startPaymentQueueHandling() { + let paymentManager = AppStorePaymentManager.shared + paymentManager.delegate = self + paymentManager.startPaymentQueueMonitoring() + + Account.shared.startPaymentMonitoring(with: paymentManager) + } + +} + +// MARK: - RootContainerViewControllerDelegate + +extension AppDelegate: RootContainerViewControllerDelegate { + func rootContainerViewControllerShouldShowSettings(_ controller: RootContainerViewController, navigateTo route: SettingsNavigationRoute?, animated: Bool) { + let navController = makeSettingsNavigationController(route: route) + + // On iPad the login controller can be presented modally above the root container. + // in that case we have to use the presented controller to present the next modal. + if let presentedController = controller.presentedViewController { + presentedController.present(navController, animated: true) + } else { + controller.present(navController, animated: true) + } } func rootContainerViewSupportedInterfaceOrientations(_ controller: RootContainerViewController) -> UIInterfaceOrientationMask { - switch self.window?.traitCollection.userInterfaceIdiom { + switch UIDevice.current.userInterfaceIdiom { case .pad: return [.landscape, .portrait] case .phone: return [.portrait] default: - fatalError("Not supported") + return controller.supportedInterfaceOrientations } } } +// MARK: - LoginViewControllerDelegate + extension AppDelegate: LoginViewControllerDelegate { + func loginViewController(_ controller: LoginViewController, loginWithAccountToken accountToken: String, completion: @escaping (Result<AccountResponse, Account.Error>) -> Void) { + self.rootContainer?.setEnableSettingsButton(false) + + Account.shared.login(with: accountToken) { (result) in + switch result { + case .success: + self.logger?.debug("Logged in with existing token") + // RootContainer's settings button will be re-enabled in `loginViewControllerDidLogin` + + case .failure(let error): + self.logger?.error(chainedError: error, message: "Failed to log in with existing account") + self.rootContainer?.setEnableSettingsButton(true) + } + + completion(result) + } + } + + func loginViewControllerLoginWithNewAccount(_ controller: LoginViewController, completion: @escaping (Result<AccountResponse, Account.Error>) -> Void) { + self.rootContainer?.setEnableSettingsButton(false) + + Account.shared.loginWithNewAccount { (result) in + switch result { + case .success: + self.logger?.debug("Logged in with new account token") + // RootContainer's settings button will be re-enabled in `loginViewControllerDidLogin` + + case .failure(let error): + self.logger?.error(chainedError: error, message: "Failed to log in with new account") + self.rootContainer?.setEnableSettingsButton(true) + } + + completion(result) + } + } + func loginViewControllerDidLogin(_ controller: LoginViewController) { - rootContainer?.pushViewController(ConnectViewController(), animated: true) + self.window?.isUserInteractionEnabled = false + + TunnelManager.shared.getRelayConstraints { [weak self] (result) in + guard let self = self else { return } + + DispatchQueue.main.async { + switch result { + case .success(let relayConstraints): + self.relayConstraints = relayConstraints + self.selectLocationViewController?.setSelectedRelayLocation(relayConstraints.location.value, animated: false, scrollPosition: .middle) + + case .failure(let error): + self.logger?.error(chainedError: error, message: "Failed to load relay constraints after log in") + } + + let connectController = self.makeConnectViewController() + self.rootContainer?.pushViewController(connectController, animated: true) { + self.showAccountSettingsControllerIfAccountExpired() + } + self.connectController = connectController + + self.window?.isUserInteractionEnabled = true + self.rootContainer?.setEnableSettingsButton(true) + } + } } } -extension AppDelegate: SettingsViewControllerDelegate { +// MARK: - SettingsNavigationControllerDelegate - func settingsViewController(_ controller: SettingsViewController, didFinishWithReason reason: SettingsDismissReason) { +extension AppDelegate: SettingsNavigationControllerDelegate { + + func settingsNavigationController(_ controller: SettingsNavigationController, didFinishWithReason reason: SettingsDismissReason) { if case .userLoggedOut = reason { rootContainer?.popToRootViewController(animated: false) @@ -178,6 +336,130 @@ extension AppDelegate: SettingsViewControllerDelegate { } +// MARK: - ConnectViewControllerDelegate + +extension AppDelegate: ConnectViewControllerDelegate { + + func connectViewControllerShouldShowSelectLocationPicker(_ controller: ConnectViewController) { + let contentController = makeSelectLocationController() + contentController.navigationItem.title = NSLocalizedString("Select location", comment: "Navigation title") + contentController.navigationItem.largeTitleDisplayMode = .never + contentController.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(handleDismissSelectLocationController(_:))) + + let navController = SelectLocationNavigationController(contentController: contentController) + self.rootContainer?.present(navController, animated: true) + self.selectLocationViewController = contentController + } + + func connectViewControllerShouldConnectTunnel(_ controller: ConnectViewController) { + connectTunnel() + } + + func connectViewControllerShouldDisconnectTunnel(_ controller: ConnectViewController) { + disconnectTunnel() + } + + func connectViewControllerShouldReconnectTunnel(_ controller: ConnectViewController) { + TunnelManager.shared.reconnectTunnel { + self.logger?.debug("Re-connected VPN tunnel") + } + } + + @objc private func handleDismissSelectLocationController(_ sender: Any) { + self.selectLocationViewController?.dismiss(animated: true) + } + + private func connectTunnel() { + TunnelManager.shared.startTunnel { (result) in + DispatchQueue.main.async { + switch result { + case .success: + self.logger?.debug("Connected VPN tunnel") + + case .failure(let error): + self.logger?.error(chainedError: error, message: "Failed to start the VPN tunnel") + self.presentTunnelError(error, alertTitle: NSLocalizedString("Failed to start the VPN tunnel", comment: "")) + } + } + } + } + + private func disconnectTunnel() { + TunnelManager.shared.stopTunnel { (result) in + switch result { + case .success: + self.logger?.debug("Disconnected VPN tunnel") + + case .failure(let error): + self.logger?.error(chainedError: error, message: "Failed to stop the VPN tunnel") + self.presentTunnelError(error, alertTitle: NSLocalizedString("Failed to stop the VPN tunnel", comment: "")) + } + } + } + + private func presentTunnelError(_ error: TunnelManager.Error, alertTitle: String) { + let alertController = UIAlertController( + title: alertTitle, + message: error.errorChainDescription, + preferredStyle: .alert + ) + alertController.addAction( + UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .cancel) + ) + + self.alertPresenter.enqueue(alertController, presentingController: self.rootContainer!) + } +} + +// MARK: - SelectLocationViewControllerDelegate + +extension AppDelegate: SelectLocationViewControllerDelegate { + func selectLocationViewController(_ controller: SelectLocationViewController, didSelectRelayLocation relayLocation: RelayLocation) { + self.window?.isUserInteractionEnabled = false + DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(250)) { + self.window?.isUserInteractionEnabled = true + controller.dismiss(animated: true) { + self.selectLocationControllerDidSelectRelayLocation(relayLocation) + } + } + } + + private func selectLocationControllerDidSelectRelayLocation(_ relayLocation: RelayLocation) { + let relayConstraints = RelayConstraints(location: .only(relayLocation)) + + TunnelManager.shared.setRelayConstraints(relayConstraints) { [weak self] (result) in + guard let self = self else { return } + + DispatchQueue.main.async { + self.relayConstraints = relayConstraints + + switch result { + case .success: + self.logger?.debug("Updated relay constraints: \(relayConstraints)") + self.connectTunnel() + + case .failure(let error): + self.logger?.error(chainedError: error, message: "Failed to update relay constraints") + } + } + } + } +} + +// MARK: - RelayCacheObserver + +extension AppDelegate: RelayCacheObserver { + + func relayCache(_ relayCache: RelayCache, didUpdateCachedRelays cachedRelays: CachedRelays) { + DispatchQueue.main.async { + self.cachedRelays = cachedRelays + } + } + +} + +// MARK: - AppStorePaymentManagerDelegate + extension AppDelegate: AppStorePaymentManagerDelegate { func appStorePaymentManager(_ manager: AppStorePaymentManager, @@ -187,4 +469,5 @@ extension AppDelegate: AppStorePaymentManagerDelegate { // app launches, we assume that all successful purchases belong to the active account token. return Account.shared.token } + } diff --git a/ios/MullvadVPN/ConnectViewController.swift b/ios/MullvadVPN/ConnectViewController.swift index 90c6c5f205..8dd31f3346 100644 --- a/ios/MullvadVPN/ConnectViewController.swift +++ b/ios/MullvadVPN/ConnectViewController.swift @@ -7,21 +7,27 @@ // import UIKit -import NetworkExtension import Logging +protocol ConnectViewControllerDelegate: class { + func connectViewControllerShouldShowSelectLocationPicker(_ controller: ConnectViewController) + func connectViewControllerShouldConnectTunnel(_ controller: ConnectViewController) + func connectViewControllerShouldDisconnectTunnel(_ controller: ConnectViewController) + func connectViewControllerShouldReconnectTunnel(_ controller: ConnectViewController) +} + class ConnectViewController: UIViewController, RootContainment, TunnelObserver { - private lazy var mainContentView: ConnectMainContentView = { + weak var delegate: ConnectViewControllerDelegate? + + private let mainContentView: ConnectMainContentView = { let view = ConnectMainContentView(frame: UIScreen.main.bounds) view.translatesAutoresizingMaskIntoConstraints = false return view }() - private var relayConstraints: RelayConstraints? - private let logger = Logger(label: "ConnectViewController") - private let alertPresenter = AlertPresenter() + override var preferredStatusBarStyle: UIStatusBarStyle { return .lightContent @@ -49,8 +55,6 @@ class ConnectViewController: UIViewController, RootContainment, TunnelObserver } } - private var showedAccountView = false - override func viewDidLoad() { super.viewDidLoad() @@ -61,24 +65,20 @@ class ConnectViewController: UIViewController, RootContainment, TunnelObserver mainContentView.selectLocationButton.addTarget(self, action: #selector(handleSelectLocation(_:)), for: .touchUpInside) + TunnelManager.shared.addObserver(self) + self.tunnelState = TunnelManager.shared.tunnelState + + addSubviews() + } + + private func addSubviews() { view.addSubview(mainContentView) NSLayoutConstraint.activate([ mainContentView.topAnchor.constraint(equalTo: view.topAnchor), mainContentView.leadingAnchor.constraint(equalTo: view.leadingAnchor), mainContentView.trailingAnchor.constraint(equalTo: view.trailingAnchor), - mainContentView.bottomAnchor.constraint(equalTo: view.bottomAnchor), + mainContentView.bottomAnchor.constraint(equalTo: view.bottomAnchor) ]) - - TunnelManager.shared.addObserver(self) - self.tunnelState = TunnelManager.shared.tunnelState - - fetchRelayConstraints() - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - - showAccountViewForExpiredAccount() } // MARK: - TunnelObserver @@ -135,137 +135,6 @@ class ConnectViewController: UIViewController, RootContainment, TunnelObserver } } - private func connectTunnel() { - TunnelManager.shared.startTunnel { (result) in - DispatchQueue.main.async { - switch result { - case .success: - break - - case .failure(let error): - self.logger.error(chainedError: error, message: "Failed to start the VPN tunnel") - - let alertController = UIAlertController( - title: NSLocalizedString("Failed to start the VPN tunnel", comment: ""), - message: error.errorChainDescription, - preferredStyle: .alert - ) - alertController.addAction( - UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .cancel) - ) - - self.alertPresenter.enqueue(alertController, presentingController: self) - } - } - } - } - - private func disconnectTunnel() { - TunnelManager.shared.stopTunnel { (result) in - if case .failure(let error) = result { - self.logger.error(chainedError: error, message: "Failed to stop the VPN tunnel") - - let alertController = UIAlertController( - title: NSLocalizedString("Failed to stop the VPN tunnel", comment: ""), - message: error.errorChainDescription, - preferredStyle: .alert - ) - alertController.addAction( - UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .cancel) - ) - - self.alertPresenter.enqueue(alertController, presentingController: self) - } - } - } - - private func reconnectTunnel() { - TunnelManager.shared.reconnectTunnel(completionHandler: nil) - } - - private func showAccountViewForExpiredAccount() { - guard !showedAccountView else { return } - - showedAccountView = true - - if let accountExpiry = Account.shared.expiry, AccountExpiry(date: accountExpiry).isExpired { - rootContainerController?.showSettings(navigateTo: .account, animated: true) - } - } - - private func showSelectLocationModal() { - let contentController = SelectLocationViewController() - contentController.navigationItem.title = NSLocalizedString("Select location", comment: "Navigation title") - contentController.navigationItem.largeTitleDisplayMode = .never - contentController.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(handleDismissSelectLocationController(_:))) - - contentController.didSelectRelayLocation = { [weak self] (controller, relayLocation) in - controller.view.isUserInteractionEnabled = false - DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(250)) { - controller.view.isUserInteractionEnabled = true - controller.dismiss(animated: true) { - self?.selectLocationControllerDidSelectRelayLocation(relayLocation) - } - } - } - - let navController = SelectLocationNavigationController(contentController: contentController) - - view.isUserInteractionEnabled = false - contentController.setSelectedRelayLocation(self.relayConstraints?.location.value, animated: false, scrollPosition: .none) - contentController.prefetchData { (error) in - if let error = error { - self.logger.error(chainedError: error, message: "Failed to prefetch the relays for SelectLocationViewController") - } - - self.present(navController, animated: true) { - self.view.isUserInteractionEnabled = true - } - } - } - - private func fetchRelayConstraints() { - TunnelManager.shared.getRelayConstraints { (result) in - DispatchQueue.main.async { - switch result { - case .success(let relayConstraints): - self.relayConstraints = relayConstraints - - case .failure(let error): - self.logger.error(chainedError: error) - } - } - } - } - - private func selectLocationControllerDidSelectRelayLocation(_ relayLocation: RelayLocation) { - let relayConstraints = makeRelayConstraints(relayLocation) - - self.setTunnelRelayConstraints(relayConstraints) - self.relayConstraints = relayConstraints - } - - private func makeRelayConstraints(_ location: RelayLocation) -> RelayConstraints { - return RelayConstraints(location: .only(location)) - } - - private func setTunnelRelayConstraints(_ relayConstraints: RelayConstraints) { - TunnelManager.shared.setRelayConstraints(relayConstraints) { [weak self] (result) in - guard let self = self else { return } - - DispatchQueue.main.async { - switch result { - case .success: - self.logger.debug("Updated relay constraints: \(relayConstraints)") - self.connectTunnel() - - case .failure(let error): - self.logger.error(chainedError: error, message: "Failed to update relay constraints") - } - } - } - } - // MARK: - Actions @objc func handleConnectionPanelButton(_ sender: Any) { @@ -273,25 +142,20 @@ class ConnectViewController: UIViewController, RootContainment, TunnelObserver } @objc func handleConnect(_ sender: Any) { - connectTunnel() + delegate?.connectViewControllerShouldConnectTunnel(self) } @objc func handleDisconnect(_ sender: Any) { - disconnectTunnel() + delegate?.connectViewControllerShouldDisconnectTunnel(self) } @objc func handleReconnect(_ sender: Any) { - reconnectTunnel() + delegate?.connectViewControllerShouldReconnectTunnel(self) } @objc func handleSelectLocation(_ sender: Any) { - showSelectLocationModal() - } - - @objc func handleDismissSelectLocationController(_ sender: Any) { - self.presentedViewController?.dismiss(animated: true) + delegate?.connectViewControllerShouldShowSelectLocationPicker(self) } - } private extension TunnelState { diff --git a/ios/MullvadVPN/ConsentViewController.swift b/ios/MullvadVPN/ConsentViewController.swift index ab029ed1ec..33beab0f54 100644 --- a/ios/MullvadVPN/ConsentViewController.swift +++ b/ios/MullvadVPN/ConsentViewController.swift @@ -13,14 +13,14 @@ private let kPrivacyPolicyURL = URL(string: "https://mullvad.net/en/help/privacy class ConsentViewController: UIViewController, RootContainment, SFSafariViewControllerDelegate { - var completionHandler: (() -> Void)? + var completionHandler: ((UIViewController) -> Void)? override var preferredStatusBarStyle: UIStatusBarStyle { return .lightContent } - var preferredHeaderBarPresentation: HeaderBarPresentation { - return HeaderBarPresentation(style: .default, showsDivider: false) + var preferredHeaderBarStyle: HeaderBarStyle { + return .default } var prefersHeaderBarHidden: Bool { diff --git a/ios/MullvadVPN/RelayCache.swift b/ios/MullvadVPN/RelayCache.swift index c561303e49..2d15f9a1fd 100644 --- a/ios/MullvadVPN/RelayCache.swift +++ b/ios/MullvadVPN/RelayCache.swift @@ -195,7 +195,7 @@ class RelayCache { } case .failure(let readError): - self.logger.error(chainedError: readError, message: "Failed to read the relay cache") + self.logger.error(chainedError: readError, message: "Failed to read the relay cache to determine if it needs to be updated") if Self.shouldDownloadRelaysOnReadFailure(readError) { self.downloadRelays() diff --git a/ios/MullvadVPN/RootContainerViewController.swift b/ios/MullvadVPN/RootContainerViewController.swift index 9a3fa411fa..3057266315 100644 --- a/ios/MullvadVPN/RootContainerViewController.swift +++ b/ios/MullvadVPN/RootContainerViewController.swift @@ -169,16 +169,16 @@ class RootContainerViewController: UIViewController { ) } - func pushViewController(_ viewController: UIViewController, animated: Bool) { + func pushViewController(_ viewController: UIViewController, animated: Bool, completion: CompletionHandler? = nil) { var newViewControllers = viewControllers.filter({ $0 != viewController }) newViewControllers.append(viewController) - setViewControllersInternal(newViewControllers, isUnwinding: false, animated: animated) + setViewControllersInternal(newViewControllers, isUnwinding: false, animated: animated, completion: completion) } - func popToRootViewController(animated: Bool) { + func popToRootViewController(animated: Bool, completion: CompletionHandler? = nil) { if let rootController = self.viewControllers.first, self.viewControllers.count > 1 { - setViewControllersInternal([rootController], isUnwinding: true, animated: animated) + setViewControllersInternal([rootController], isUnwinding: true, animated: animated, completion: completion) } } |
