// // AppDelegate.swift // Reader // // Created by Shadowfacts on 10/29/21. // import UIKit import WebKit import OSLog import Combine import Persistence import BackgroundTasks @main class AppDelegate: UIResponder, UIApplicationDelegate { private var cancellables = Set() #if targetEnvironment(macCatalyst) private var readerMac: NSObject! #endif func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { LocalData.migrateIfNecessary() Preferences.shared.objectWillChange .debounce(for: .milliseconds(250), scheduler: RunLoop.main) .sink { _ in Preferences.save() } .store(in: &cancellables) #if targetEnvironment(macCatalyst) let macBundleURL = Bundle.main.builtInPlugInsURL!.appendingPathComponent("ReaderMac.bundle") let bundle = Bundle(url: macBundleURL)! do { try bundle.loadAndReturnError() let clazz = NSClassFromString("ReaderMac.ReaderMac")! as! NSObject.Type readerMac = clazz.init() readerMac.perform(Selector(("setup"))) } catch { print("Unable to load ReaderMac bundle: \(error)") } #endif NotificationCenter.default.addObserver(self, selector: #selector(updateAppearance), name: .appearanceChanged, object: nil) updateAppearance() BGTaskScheduler.shared.register(forTaskWithIdentifier: BackgroundManager.refreshIdentifier, using: nil) { task in Task { BackgroundManager.shared.handleBackgroundRefresh(task: task as! BGAppRefreshTask) } } return true } func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) { if identifier == BackgroundManager.refreshIdentifier { BackgroundManager.shared.doRefresh() } else { fatalError("Unknown background url session identifier: \(identifier)") } } // MARK: UISceneSession Lifecycle func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { let name: String #if targetEnvironment(macCatalyst) if options.userActivities.first?.activityType == NSUserActivity.preferencesType { name = "prefs" } else { name = "main" } #else name = "main" #endif return UISceneConfiguration(name: name, sessionRole: connectingSceneSession.role) } func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { // Called when the user discards a scene session. // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. // Use this method to release any resources that were specific to the discarded scenes, as they will not return. } override func buildMenu(with builder: UIMenuBuilder) { if builder.system == .main { builder.insertSibling(UIMenu(options: .displayInline, children: [ UIKeyCommand(title: "Preferences…", action: #selector(showPreferences), input: ",", modifierFlags: .command) ]), afterMenu: .about) var children = [UIMenuElement]() let accounts: [UIMenuElement] = LocalData.accounts.map { account in var title = account.instanceURL.host! if let port = account.instanceURL.port, port != 80 && port != 443 { title += ":\(port)" } let state: UIAction.State if let activeScene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }), let sceneDelegate = activeScene.delegate as? SceneDelegate, sceneDelegate.fervorController?.account?.id == account.id { state = .on } else { state = .off } return UIAction(title: title, attributes: [], state: state) { _ in let activity = NSUserActivity.activateAccount(account) let options = UIScene.ActivationRequestOptions() #if targetEnvironment(macCatalyst) options.collectionJoinBehavior = .disallowed #endif UIApplication.shared.requestSceneSessionActivation(nil, userActivity: activity, options: options, errorHandler: nil) } } children.append(UIMenu(options: .displayInline, children: accounts)) children.append(UIAction(title: "Add Account...", handler: { _ in let activity = NSUserActivity.addAccount() let options = UIScene.ActivationRequestOptions() #if targetEnvironment(macCatalyst) options.collectionJoinBehavior = .disallowed #endif UIApplication.shared.requestSceneSessionActivation(nil, userActivity: activity, options: options, errorHandler: nil) })) let account = UIMenu(title: "Account", image: nil, identifier: nil, options: [], children: children) builder.insertSibling(account, afterMenu: .file) } } @objc private func showPreferences() { let existing = UIApplication.shared.connectedScenes.first { $0.session.configuration.name == "prefs" } UIApplication.shared.requestSceneSessionActivation(existing?.session, userActivity: .preferences(), options: nil, errorHandler: nil) } @objc private func updateAppearance() { #if targetEnvironment(macCatalyst) readerMac.perform(Selector(("updateAppearance:")), with: Preferences.shared.appearance.rawValue) #endif } }