// // AppDelegate.swift // Tusker // // Created by Shadowfacts on 8/15/18. // Copyright © 2018 Shadowfacts. All rights reserved. // import UIKit import CrashReporter import CoreData import OSLog let stateRestorationLogger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "StateRestoration") @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { static private(set) var crashReporter: PLCrashReporter! static var pendingCrashReport: PLCrashReport? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { #if !DEBUG setupCrashReporter() #endif AppShortcutItem.createItems(for: application) DispatchQueue.global(qos: .userInitiated).async { AudioSessionHelper.disable() AudioSessionHelper.setDefault() } if let oldSavedData = SavedDataManager.load() { do { for account in oldSavedData.accountIDs { guard let account = LocalData.shared.getAccount(id: account) else { continue } let controller = MastodonController.getForAccount(account) try oldSavedData.migrateToCoreData(accountID: account.id, context: controller.persistentContainer.viewContext) if controller.persistentContainer.viewContext.hasChanges { try controller.persistentContainer.viewContext.save() } } try SavedDataManager.destroy() } catch { // no-op } } return true } private func setupCrashReporter() { let config = PLCrashReporterConfig(signalHandlerType: .BSD, symbolicationStrategy: .all) AppDelegate.crashReporter = PLCrashReporter(configuration: config) let callbacksPtr = UnsafeMutablePointer.allocate(capacity: 1) callbacksPtr.pointee = PLCrashReporterCallbacks(version: 0, context: nil, handleSignal: crashCallback) // setCrashCallbacks, it's poorly imported into Swift AppDelegate.crashReporter.setCrash(callbacksPtr) if AppDelegate.crashReporter.hasPendingCrashReport(), let data = try? AppDelegate.crashReporter.loadPendingCrashReportDataAndReturnError(), let report = try? PLCrashReport(data: data) { AppDelegate.crashReporter.purgePendingCrashReport() AppDelegate.pendingCrashReport = report } AppDelegate.crashReporter.enable() } override func buildMenu(with builder: UIMenuBuilder) { if builder.system == .main { MenuController.buildMainMenu(builder: builder) } } func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { let name = getSceneNameForActivity(options.userActivities.first) return UISceneConfiguration(name: name, sessionRole: connectingSceneSession.role) } private func getSceneNameForActivity(_ activity: NSUserActivity?) -> String { guard let activity = activity, let type = UserActivityType(rawValue: activity.activityType) else { return "main-scene" } switch type { case .mainScene: return "main-scene" case .showConversation, .showTimeline, .checkNotifications, .search, .bookmarks, .myProfile, .showProfile: if activity.displaysAuxiliaryScene { stateRestorationLogger.info("Using auxiliary scene for \(type.rawValue, privacy: .public)") return "auxiliary" } else { return "main-scene" } case .newPost: return "compose" } } @objc func closeWindow() { guard let scene = UIApplication.shared.connectedScenes.first(where: { $0.activationState == .foregroundActive }) else { return } UIApplication.shared.requestSceneSessionDestruction(scene.session, options: nil) } } private func crashCallback(info: UnsafeMutablePointer!, uap: UnsafeMutablePointer!, context: UnsafeMutableRawPointer?) { // read logs on the main queue in order to block further user interaction while we're crashing // since getting the log data can take upwards of a second, even for very few entries // synchronously dispatch because the callback can't return until we're ready to abort DispatchQueue.main.sync { Logging.writeDataForCrash() } }