// // SceneDelegate.swift // Delta // // Created by Riley Testut on 6/6/22. // Copyright © 2022 Riley Testut. All rights reserved. // import UIKit import DeltaCore import Harmony @objc(SceneDelegate) @available(iOS 13, *) class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? { get { if _window == nil { _window = GameWindow() } return _window } set { _window = newValue as? GameWindow } } private var _window: GameWindow? private let deepLinkController = DeepLinkController() func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). self.window?.tintColor = .deltaPurple if let context = connectionOptions.urlContexts.first { self.handle(.url(context.url)) } if let shortcutItem = connectionOptions.shortcutItem { self.handle(.shortcut(shortcutItem)) } self.window?.makeKeyAndVisible() } func sceneDidDisconnect(_ scene: UIScene) { // Called as the scene is being released by the system. // This occurs shortly after the scene enters the background, or when its session is discarded. // Release any resources associated with this scene that can be re-created the next time the scene connects. // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). } func sceneDidBecomeActive(_ scene: UIScene) { // Called when the scene has moved from an inactive state to an active state. // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. } func sceneWillResignActive(_ scene: UIScene) { // Called when the scene will move from an active state to an inactive state. // This may occur due to temporary interruptions (ex. an incoming phone call). } func sceneWillEnterForeground(_ scene: UIScene) { // Called as the scene transitions from the background to the foreground. // Use this method to undo the changes made on entering the background. } func sceneDidEnterBackground(_ scene: UIScene) { // Called as the scene transitions from the foreground to the background. // Use this method to save data, release shared resources, and store enough scene-specific state information // to restore the scene back to its current state. } } @available(iOS 13, *) extension SceneDelegate { func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { guard let context = URLContexts.first else { return } self.handle(.url(context.url)) } func windowScene(_ windowScene: UIWindowScene, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) { self.handle(.shortcut(shortcutItem)) completionHandler(true) } } @available(iOS 13, *) private extension SceneDelegate { func handle(_ deepLink: DeepLink) { guard DatabaseManager.shared.isStarted else { // Wait until DatabaseManager is ready before handling deep link. // NotificationCenter.default.notifications requires iOS 15 or later :( // _ = await NotificationCenter.default.notifications(named: DatabaseManager.didStartNotification).first(where: { _ in true }) var observer: NSObjectProtocol? observer = NotificationCenter.default.addObserver(forName: DatabaseManager.didStartNotification, object: DatabaseManager.shared, queue: .main) { [weak observer] _ in observer.map { NotificationCenter.default.removeObserver($0) } self.handle(deepLink) } return } DispatchQueue.main.async { // DeepLinkController expects to be called from main thread. switch deepLink { case .shortcut: _ = self.deepLinkController.handle(deepLink) case .url(let url): if url.isFileURL { if GameType(fileExtension: url.pathExtension) != nil || url.pathExtension.lowercased() == "zip" { self.importGame(at: url) } else if url.pathExtension.lowercased() == "deltaskin" { self.importControllerSkin(at: url) } } else if url.scheme?.hasPrefix("db-") == true { _ = DropboxService.shared.handleDropboxURL(url) } else if url.scheme?.lowercased() == "delta" { _ = self.deepLinkController.handle(deepLink) } } } } func importGame(at url: URL) { DatabaseManager.shared.importGames(at: [url]) { (games, errors) in if errors.count > 0 { let alertController = UIAlertController.alertController(for: .games, with: errors) self.present(alertController) } } } func importControllerSkin(at url: URL) { DatabaseManager.shared.importControllerSkins(at: [url]) { (games, errors) in if errors.count > 0 { let alertController = UIAlertController.alertController(for: .controllerSkins, with: errors) self.present(alertController) } } } func present(_ alertController: UIAlertController) { var rootViewController = self.window?.rootViewController while rootViewController?.presentedViewController != nil { rootViewController = rootViewController?.presentedViewController } rootViewController?.present(alertController, animated: true, completion: nil) } }