import Sybil import SwiftUI import UIKit import UserNotifications @main struct SybilApp: App { @UIApplicationDelegateAdaptor(SybilAppDelegate.self) private var appDelegate var body: some Scene { WindowGroup(id: SybilCommands.workspaceWindowID) { SplitView() } .commands { SybilCommands() } } } @MainActor final class SybilAppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate { func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil ) -> Bool { UNUserNotificationCenter.current().delegate = self SybilPushNotifications.launchFixtureIfRequested() SybilHomeScreenQuickActionHandler.configureQuickActions() #if targetEnvironment(macCatalyst) SybilMacQuickQuestionController.shared.start() #endif return true } func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { SybilPushNotifications.didRegister(deviceToken: deviceToken) } func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { SybilPushNotifications.didFailToRegister() } func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { SybilPushNotifications.receiveBackground(userInfo: userInfo) { result in completionHandler(result) } } nonisolated func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification) async -> UNNotificationPresentationOptions { let event = SybilPushNotifications.Event(userInfo: notification.request.content.userInfo) return await SybilPushNotifications.presentationOptions(event: event) } nonisolated func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async { if response.actionIdentifier == UNNotificationDefaultActionIdentifier { let event = SybilPushNotifications.Event(userInfo: response.notification.request.content.userInfo) await SybilPushNotifications.didRespond(event: event, identifier: response.notification.request.identifier) } } #if targetEnvironment(macCatalyst) func applicationWillTerminate(_ application: UIApplication) { SybilMacQuickQuestionController.shared.stop() } #endif func application( _ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions ) -> UISceneConfiguration { let configuration = UISceneConfiguration( name: "Default Configuration", sessionRole: connectingSceneSession.role ) configuration.delegateClass = SybilSceneDelegate.self return configuration } func application( _ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void ) { completionHandler(SybilHomeScreenQuickActionHandler.handle(shortcutItem)) } } @MainActor final class SybilSceneDelegate: NSObject, UIWindowSceneDelegate { func scene( _ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions ) { #if targetEnvironment(macCatalyst) if scene is UIWindowScene, session.role == .windowApplication { SybilMacWindowCommands.shared.windowConnected(sessionID: session.persistentIdentifier) } #endif if let shortcutItem = connectionOptions.shortcutItem { _ = SybilHomeScreenQuickActionHandler.handle(shortcutItem) } if let response = connectionOptions.notificationResponse, response.actionIdentifier == UNNotificationDefaultActionIdentifier { SybilPushNotifications.didRespond(userInfo: response.notification.request.content.userInfo, identifier: response.notification.request.identifier) } } #if targetEnvironment(macCatalyst) func sceneDidDisconnect(_ scene: UIScene) { SybilMacWindowCommands.shared.windowDisconnected(sessionID: scene.session.persistentIdentifier) } #endif func windowScene( _ windowScene: UIWindowScene, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void ) { completionHandler(SybilHomeScreenQuickActionHandler.handle(shortcutItem)) } func sceneWillResignActive(_ scene: UIScene) { SybilHomeScreenQuickActionHandler.configureQuickActions() } } @MainActor private enum SybilHomeScreenQuickActionHandler { static func configureQuickActions() { // The quick question action is static in Info.plist so it is available before first launch. UIApplication.shared.shortcutItems = [] } static func handle(_ shortcutItem: UIApplicationShortcutItem) -> Bool { guard shortcutItem.type == SybilHomeScreenQuickAction.quickQuestionType else { return false } Task { @MainActor in SybilQuickActionRouter.shared.requestQuickQuestionPresentation() } return true } }