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

Bruno Muniz's avatar
Bruno Muniz committed
import Defaults
Ahmed Shehata's avatar
Ahmed Shehata committed
import PushFeature
import LaunchFeature
Bruno Muniz's avatar
Bruno Muniz committed
import CrashReporting
Bruno Muniz's avatar
Bruno Muniz committed
import DI
Bruno Muniz's avatar
Bruno Muniz committed

Bruno Muniz's avatar
Bruno Muniz committed
import XXModels
import XXLogger
import XXClient
Bruno Muniz's avatar
Bruno Muniz committed
import Navigation
Bruno Muniz's avatar
Bruno Muniz committed
import XXMessengerClient
import CloudFiles
import CloudFilesDrive
import CloudFilesICloud
import CloudFilesDropbox

Bruno Muniz's avatar
Bruno Muniz committed
public class AppDelegate: UIResponder, UIApplicationDelegate {
  @Dependency var navigator: Navigator
Bruno Muniz's avatar
Bruno Muniz committed
  @Dependency var pushRouter: PushRouter
  @Dependency var pushHandler: PushHandling
  @Dependency var crashReporter: CrashReporter
Bruno Muniz's avatar
Bruno Muniz committed

  @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 {
    UNUserNotificationCenter.current().delegate = self
    setupCloudFilesManagers()
    setupCrashReporting()
    setupLogging()

    let navController = UINavigationController(rootViewController: LaunchController())
    let rootViewController = RootViewController(navController)
    window = UIWindow(frame: UIScreen.main.bounds)
    window?.rootViewController = rootViewController
    window?.makeKeyAndVisible()

    DependencyRegistrator.registerNavigators(navController)
Bruno Muniz's avatar
Bruno Muniz committed
    DI.Container.shared.register(
Bruno Muniz's avatar
Bruno Muniz committed
      PushRouter.live(navigationController: navController)
    )
Bruno Muniz's avatar
Bruno Muniz committed
    return true
  }

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

  public func applicationDidEnterBackground(_ application: UIApplication) {
Bruno Muniz's avatar
Bruno Muniz committed
    if let messenger = try? DI.Container.shared.resolve() as Messenger,
       let database = try? DI.Container.shared.resolve() as Database,
Bruno Muniz's avatar
Bruno Muniz committed
       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
            let query = Message.Query(status: [.sending])
            let assignment = Message.Assignments(status: .sendingFailed)
            _ = try? database.bulkUpdateMessages(query, assignment)
          }
          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) {
Bruno Muniz's avatar
Bruno Muniz committed
    if let messenger = try? DI.Container.shared.resolve() as Messenger {
Bruno Muniz's avatar
Bruno Muniz committed
      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? DI.Container.shared.resolve() as Messenger,
Bruno Muniz's avatar
Bruno Muniz committed
       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 {
    handleRedirectURL(url)
Bruno Muniz's avatar
Bruno Muniz committed
  }

  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),
Bruno Muniz's avatar
Bruno Muniz committed
          let router = try? DI.Container.shared.resolve() as PushRouter else {
Bruno Muniz's avatar
Bruno Muniz committed
      return false
    }

    router.navigateTo(.search(username: username), {})
    return true
  }
Bruno Muniz's avatar
Bruno Muniz committed
}

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
Bruno Muniz's avatar
Bruno Muniz committed
    pushHandler.handleAction(pushRouter, userInfo, completionHandler)
Bruno Muniz's avatar
Bruno Muniz committed
  }

  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
}

extension AppDelegate {
  private func setupCrashReporting() {
    crashReporter.configure()
    crashReporter.setEnabled(isCrashReportingEnabled)
  }

  private func setupCloudFilesManagers() {
    CloudFilesManager.all[.icloud] = .iCloud(fileName: "backup.xxm")
    CloudFilesManager.all[.dropbox] = .dropbox(appKey: "ppx0de5f16p9aq2", path: "/backup/backup.xxm")
    CloudFilesManager.all[.drive] = .drive(
      apiKey: "AIzaSyCbI2yQ7pbuVSRvraqanjGcS9CDrjD7lNU",
      clientId: "662236151640-herpu89qikpfs9m4kvbi9bs5fpdji5de.apps.googleusercontent.com",
      fileName: "backup.xxm"
    )
  }

  private func setupLogging() {
    if recordingLogs {
      XXLogger.start()
    }
  }
}

func getUsernameFromInvitationDeepLink(_ url: URL) -> String? {
  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
}