Skip to content
Snippets Groups Projects
AppDelegate.swift 6.58 KiB
Newer Older
Bruno Muniz's avatar
Bruno Muniz committed
import UIKit
import BackgroundTasks

import Theme
Bruno Muniz's avatar
Bruno Muniz committed
import XXModels
Bruno Muniz's avatar
Bruno Muniz committed
import XXLogger
import Defaults
Ahmed Shehata's avatar
Ahmed Shehata committed
import PushFeature
import ToastFeature
Ahmed Shehata's avatar
Ahmed Shehata committed
import LaunchFeature
Bruno Muniz's avatar
Bruno Muniz committed
import CrashReporting
import DependencyInjection

import XXClient
Bruno Muniz's avatar
Bruno Muniz committed
import XXMessengerClient
Bruno Muniz's avatar
Bruno Muniz committed
public class AppDelegate: UIResponder, UIApplicationDelegate {
Bruno Muniz's avatar
Bruno Muniz committed
  @Dependency private var pushRouter: PushRouter
  @Dependency private var pushHandler: PushHandling
  @Dependency private var crashReporter: CrashReporter
  @Dependency private var dropboxService: DropboxInterface

  @KeyObject(.hideAppList, defaultValue: false) var hideAppList: Bool
  @KeyObject(.recordingLogs, defaultValue: true) var recordingLogs: Bool
  @KeyObject(.crashReporting, defaultValue: true) var isCrashReportingEnabled: Bool

  var calledStopNetwork = false
  var forceFailedPendingMessages = false

  var coverView: UIView?
  var backgroundTimer: Timer?
  public var window: UIWindow?

  public func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
#if DEBUG
    DependencyRegistrator.registerForMock()
#else
    DependencyRegistrator.registerForLive()
#endif

    if recordingLogs {
      XXLogger.start()
Bruno Muniz's avatar
Bruno Muniz committed
    }

Bruno Muniz's avatar
Bruno Muniz committed
    crashReporter.configure()
    crashReporter.setEnabled(isCrashReportingEnabled)

    UNUserNotificationCenter.current().delegate = self

    let window = Window()
    let navController = UINavigationController(rootViewController: LaunchController())
    window.rootViewController = StatusBarViewController(ToastViewController(navController))
    window.backgroundColor = UIColor.white
    window.makeKeyAndVisible()
    self.window = window

    DependencyInjection.Container.shared.register(
      PushRouter.live(navigationController: navController)
    )

    return true
  }

  public func application(application: UIApplication, shouldAllowExtensionPointIdentifier: String) -> Bool {
    false
  }

  public func applicationDidEnterBackground(_ application: UIApplication) {
    if let messenger = try? DependencyInjection.Container.shared.resolve() as Messenger,
       let database = try? DependencyInjection.Container.shared.resolve() as Database,
       let cMix = try? messenger.cMix.tryGet() {
      let backgroundTask = application.beginBackgroundTask(withName: "xx.stop.network") {}

      backgroundTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { timer in
        print(">>> .backgroundTimeRemaining: \(UIApplication.shared.backgroundTimeRemaining)")

        guard UIApplication.shared.backgroundTimeRemaining > 8 else {
          if !self.calledStopNetwork {
            self.calledStopNetwork = true
            try! messenger.stop()
            print(">>> Called stopNetworkFollower")
          } else {
            if cMix.hasRunningProcesses() == false {
              application.endBackgroundTask(backgroundTask)
              timer.invalidate()
            }
          }

          return
Bruno Muniz's avatar
Bruno Muniz committed
        }

Bruno Muniz's avatar
Bruno Muniz committed
        guard UIApplication.shared.backgroundTimeRemaining > 9 else {
          if !self.forceFailedPendingMessages {
            self.forceFailedPendingMessages = true
Bruno Muniz's avatar
Bruno Muniz committed

Bruno Muniz's avatar
Bruno Muniz committed
            let query = Message.Query(status: [.sending])
            let assignment = Message.Assignments(status: .sendingFailed)
            _ = try? database.bulkUpdateMessages(query, assignment)
          }
Bruno Muniz's avatar
Bruno Muniz committed

Bruno Muniz's avatar
Bruno Muniz committed
          return
Bruno Muniz's avatar
Bruno Muniz committed
        }
Bruno Muniz's avatar
Bruno Muniz committed
      })
Bruno Muniz's avatar
Bruno Muniz committed
    }
Bruno Muniz's avatar
Bruno Muniz committed
  }

  public func applicationWillResignActive(_ application: UIApplication) {
    if hideAppList {
      coverView?.removeFromSuperview()
      coverView = UIVisualEffectView(effect: UIBlurEffect(style: .regular))
      coverView?.frame = window?.bounds ?? .zero
      window?.addSubview(coverView!)
Bruno Muniz's avatar
Bruno Muniz committed
    }
Bruno Muniz's avatar
Bruno Muniz committed
  }
Bruno Muniz's avatar
Bruno Muniz committed
  public func applicationWillTerminate(_ application: UIApplication) {
    if let messenger = try? DependencyInjection.Container.shared.resolve() as Messenger {
      try? messenger.stop()
Bruno Muniz's avatar
Bruno Muniz committed
  }
Bruno Muniz's avatar
Bruno Muniz committed
  public func applicationWillEnterForeground(_ application: UIApplication) {
    if backgroundTimer != nil {
      backgroundTimer?.invalidate()
      backgroundTimer = nil
      print(">>> Invalidated background timer")
    }
Bruno Muniz's avatar
Bruno Muniz committed
    if let messenger = try? DependencyInjection.Container.shared.resolve() as Messenger,
       let cMix = messenger.cMix.get() {
      guard self.calledStopNetwork == true else { return }
      try? cMix.startNetworkFollower(timeoutMS: 10_000)
      print(">>> Called startNetworkFollower")
      self.calledStopNetwork = false
Bruno Muniz's avatar
Bruno Muniz committed
  }

  public func applicationDidBecomeActive(_ application: UIApplication) {
    application.applicationIconBadgeNumber = 0
    coverView?.removeFromSuperview()
  }

  public func application(
    _ app: UIApplication,
    open url: URL,
    options: [UIApplication.OpenURLOptionsKey : Any] = [:]
  ) -> Bool {
    dropboxService.handleOpenUrl(url)
  }

  public func application(
    _ application: UIApplication,
    continue userActivity: NSUserActivity,
    restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
  ) -> Bool {
    guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
          let incomingURL = userActivity.webpageURL,
          let username = getUsernameFromInvitationDeepLink(incomingURL),
          let router = try? DependencyInjection.Container.shared.resolve() as PushRouter else {
      return false
    }

    router.navigateTo(.search(username: username), {})
    return true
  }
func getUsernameFromInvitationDeepLink(_ url: URL) -> String? {
Bruno Muniz's avatar
Bruno Muniz committed
  if let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
     components.scheme == "https",
     components.host == "elixxir.io",
     components.path == "/connect",
     let queryItem = components.queryItems?.first(where: { $0.name == "username" }),
     let username = queryItem.value {
    return username
  }

  return nil
Bruno Muniz's avatar
Bruno Muniz committed
// MARK: Notifications

extension AppDelegate: UNUserNotificationCenterDelegate {
Bruno Muniz's avatar
Bruno Muniz committed
  public func userNotificationCenter(
    _ center: UNUserNotificationCenter,
    didReceive response: UNNotificationResponse,
    withCompletionHandler completionHandler: @escaping () -> Void
  ) {
    let userInfo = response.notification.request.content.userInfo
    pushHandler.handleAction(pushRouter, userInfo, completionHandler)
  }

  public func application(
    _ application: UIApplication,
    didReceiveRemoteNotification notification: [AnyHashable: Any],
    fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
  ) {
    pushHandler.handlePush(notification, completionHandler)
  }

  public func application(
    _: UIApplication,
    didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
  ) {
    pushHandler.registerToken(deviceToken)
  }
Bruno Muniz's avatar
Bruno Muniz committed
}