Skip to content
Snippets Groups Projects
AppDelegate.swift 6.59 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
import Integration
Ahmed Shehata's avatar
Ahmed Shehata committed
import PushFeature
import ToastFeature
import SwiftyDropbox
Ahmed Shehata's avatar
Ahmed Shehata committed
import LaunchFeature
import DropboxFeature
Bruno Muniz's avatar
Bruno Muniz committed
import CrashReporting
import DependencyInjection

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

    @KeyObject(.invitation, defaultValue: nil) var invitation: String?
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 {
        #if DEBUG
        DependencyRegistrator.registerForMock()
        #else
        DependencyRegistrator.registerForLive()
        #endif

        if recordingLogs {
            XXLogger.start()
        }

        crashReporter.configure()
        crashReporter.setEnabled(isCrashReportingEnabled)

        UNUserNotificationCenter.current().delegate = self

Ahmed Shehata's avatar
Ahmed Shehata committed
        let window = Window()
        let navController = UINavigationController(rootViewController: LaunchController())
        window.rootViewController = StatusBarViewController(ToastViewController(navController))
        window.backgroundColor = UIColor.white
        window.makeKeyAndVisible()
        self.window = window
Bruno Muniz's avatar
Bruno Muniz committed

Ahmed Shehata's avatar
Ahmed Shehata committed
        DependencyInjection.Container.shared.register(
            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) {
        if let session = try? DependencyInjection.Container.shared.resolve() as SessionType {
Ahmed Shehata's avatar
Ahmed Shehata committed
            let backgroundTask = application.beginBackgroundTask(withName: "xx.stop.network") {}
Bruno Muniz's avatar
Bruno Muniz committed

            // An option here would be: create async completion closure

            backgroundTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { timer in
                guard UIApplication.shared.backgroundTimeRemaining > 8 else {
                    if !self.calledStopNetwork {
                        self.calledStopNetwork = true
                        session.stop()
                    } else {
                        if session.hasRunningTasks == false {
                            application.endBackgroundTask(backgroundTask)
                            timer.invalidate()
                        }
                    }

                    return
                }

                guard UIApplication.shared.backgroundTimeRemaining > 9 else {
                    if !self.forceFailedPendingMessages {
                        self.forceFailedPendingMessages = true
Bruno Muniz's avatar
Bruno Muniz committed
                        let query = Message.Query(status: [.sending])
                        let assignment = Message.Assignments(status: .sendingFailed)
                        _ = try? session.dbManager.bulkUpdateMessages(query, assignment)
Bruno Muniz's avatar
Bruno Muniz committed
                    }

                    return
                }
            }
        }
    }

    public func applicationWillResignActive(_ application: UIApplication) {
        if hideAppList {
            coverView?.removeFromSuperview()
            coverView = UIVisualEffectView(effect: UIBlurEffect(style: .regular))
            coverView?.frame = window?.bounds ?? .zero
            window?.addSubview(coverView!)
        }
    }
    
    public func applicationWillTerminate(_ application: UIApplication) {
        if let session = try? DependencyInjection.Container.shared.resolve() as SessionType {
            session.stop()
        }
    }

    public func applicationWillEnterForeground(_ application: UIApplication) {
        if backgroundTimer != nil {
            backgroundTimer?.invalidate()
            backgroundTimer = nil
        }

        if let session = try? DependencyInjection.Container.shared.resolve() as SessionType {
Ahmed Shehata's avatar
Ahmed Shehata committed
            guard self.calledStopNetwork == true else { return }
Bruno Muniz's avatar
Bruno Muniz committed
            session.start()
            self.calledStopNetwork = false
        }
    }

    public func applicationDidBecomeActive(_ application: UIApplication) {
        application.applicationIconBadgeNumber = 0
        coverView?.removeFromSuperview()
    }
Ahmed Shehata's avatar
Ahmed Shehata committed
    public func application(
        _ app: UIApplication,
        open url: URL,
        options: [UIApplication.OpenURLOptionsKey : Any] = [:]
    ) -> Bool {
        if let username = getUsernameFromInvitationDeepLink(url),
           let router = try? DependencyInjection.Container.shared.resolve() as PushRouter {
            invitation = username
            router.navigateTo(.search, {})

            return true
        } else {
            return dropboxService.handleOpenUrl(url)
func getUsernameFromInvitationDeepLink(_ url: URL) -> String? {
    if let components = URLComponents(url: url, resolvingAgainstBaseURL: false),
       components.scheme == "xxnetwork",
       components.host == "messenger",
       let queryItem = components.queryItems?.first(where: { $0.name == "invitation" }),
       let username = queryItem.value {
        return username
    }

    return nil
}

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

extension AppDelegate: UNUserNotificationCenterDelegate {
Ahmed Shehata's avatar
Ahmed Shehata 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)
    }

Bruno Muniz's avatar
Bruno Muniz committed
    public func application(
Ahmed Shehata's avatar
Ahmed Shehata committed
        _ application: UIApplication,
Bruno Muniz's avatar
Bruno Muniz committed
        didReceiveRemoteNotification notification: [AnyHashable: Any],
        fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
    ) {
Ahmed Shehata's avatar
Ahmed Shehata committed
        pushHandler.handlePush(notification, completionHandler)
Bruno Muniz's avatar
Bruno Muniz committed
    }

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