// // AppDelegate.swift // Tusker // // Created by Shadowfacts on 8/15/18. // Copyright © 2018 Shadowfacts. All rights reserved. // import UIKit import CoreData import OSLog import Sentry let stateRestorationLogger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "StateRestoration") @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { configureSentry() swizzleStatusBar() 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 configureSentry() { guard let dsn = Bundle.main.object(forInfoDictionaryKey: "SentryDSN") as? String, !dsn.isEmpty else { return } SentrySDK.start { options in #if DEBUG options.debug = true options.environment = "dev" #endif // the '//' in the full url can't be escaped, so we have to add the scheme back options.dsn = "https://\(dsn)" options.enableSwizzling = false // required to support releases/release health options.enableAutoSessionTracking = true options.enableOutOfMemoryTracking = false options.enableAutoPerformanceTracking = false options.enableNetworkTracking = false options.enableAppHangTracking = false options.enableCoreDataTracking = false // we don't care about events like battery, keyboard show/hide options.enableAutoBreadcrumbTracking = false options.beforeSend = { event in // just no, why would anyone need this information event.context?.removeValue(forKey: "culture") return Preferences.shared.reportErrorsAutomatically ? event : nil } } } 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 swizzleStatusBar() { let selector = Selector(("handleTapAction:")) var originalIMP: IMP? let imp = imp_implementationWithBlock({ (self: UIStatusBarManager, sender: AnyObject) in let original = unsafeBitCast(originalIMP!, to: (@convention(c) (UIStatusBarManager, Selector, AnyObject) -> Void).self) guard let windowScene = self.perform(Selector(("windowScene"))).takeUnretainedValue() as? UIWindowScene, let xPosition = sender.value(forKey: "xPosition") as? CGFloat, let delegate = windowScene.delegate as? TuskerSceneDelegate else { original(self, selector, sender) return } switch delegate.handleStatusBarTapped(xPosition: xPosition) { case .stop: return case .continue: original(self, selector, sender) } } as @convention(block) (UIStatusBarManager, AnyObject) -> Void) originalIMP = class_replaceMethod(UIStatusBarManager.self, selector, imp, "v@:@") if originalIMP == nil { Logging.general.error("Unable to swizzle status bar manager") } } }