// SPDX-License-Identifier: MIT // Copyright © 2018-2023 WireGuard LLC. All Rights Reserved. import UIKit import os.log import AppIntents @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? var mainVC: MainViewController? var isLaunchedForSpecificAction = false var tunnelsManager: TunnelsManager? static let tunnelsManagerReadyNotificationName: Notification.Name = Notification.Name(rawValue: "com.wireguard.ios.tunnelsManagerReadyNotification") func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { Logger.configureGlobal(tagged: "APP", withFilePath: FileManager.logFileURL?.path) if let launchOptions = launchOptions { if launchOptions[.url] != nil || launchOptions[.shortcutItem] != nil { isLaunchedForSpecificAction = true } } let window = UIWindow(frame: UIScreen.main.bounds) self.window = window let mainVC = MainViewController() window.rootViewController = mainVC window.makeKeyAndVisible() self.mainVC = mainVC // Create the tunnels manager, and when it's ready, inform tunnelsListVC TunnelsManager.create { [weak self] result in guard let self = self else { return } switch result { case .failure(let error): ErrorPresenter.showErrorAlert(error: error, from: self.mainVC) case .success(let tunnelsManager): self.tunnelsManager = tunnelsManager self.mainVC?.tunnelsListVC?.setTunnelsManager(tunnelsManager: tunnelsManager) tunnelsManager.activationDelegate = self.mainVC if #available(iOS 16.0, *) { AppDependencyManager.shared.add(dependency: tunnelsManager) } NotificationCenter.default.post(name: AppDelegate.tunnelsManagerReadyNotificationName, object: self, userInfo: nil) } } return true } func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { mainVC?.importFromDisposableFile(url: url) return true } func applicationDidBecomeActive(_ application: UIApplication) { mainVC?.refreshTunnelConnectionStatuses() } func applicationWillResignActive(_ application: UIApplication) { guard let allTunnelNames = mainVC?.allTunnelNames() else { return } application.shortcutItems = QuickActionItem.createItems(allTunnelNames: allTunnelNames) } func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) { guard shortcutItem.type == QuickActionItem.type else { completionHandler(false) return } let tunnelName = shortcutItem.localizedTitle mainVC?.showTunnelDetailForTunnel(named: tunnelName, animated: false, shouldToggleStatus: true) completionHandler(true) } } extension AppDelegate { func application(_ application: UIApplication, shouldSaveApplicationState coder: NSCoder) -> Bool { return true } func application(_ application: UIApplication, shouldRestoreApplicationState coder: NSCoder) -> Bool { return !self.isLaunchedForSpecificAction } func application(_ application: UIApplication, viewControllerWithRestorationIdentifierPath identifierComponents: [String], coder: NSCoder) -> UIViewController? { guard let vcIdentifier = identifierComponents.last else { return nil } if vcIdentifier.hasPrefix("TunnelDetailVC:") { let tunnelName = String(vcIdentifier.suffix(vcIdentifier.count - "TunnelDetailVC:".count)) if let tunnelsManager = mainVC?.tunnelsManager { if let tunnel = tunnelsManager.tunnel(named: tunnelName) { return TunnelDetailTableViewController(tunnelsManager: tunnelsManager, tunnel: tunnel) } } else { // Show it when tunnelsManager is available mainVC?.showTunnelDetailForTunnel(named: tunnelName, animated: false, shouldToggleStatus: false) } } return nil } }