mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 21:33:26 +00:00
Cleanup following the AppMediator introduction (#2723)
- stop using multiple background task, the appCoordinator sync one is enough for the whole app - move the AppMeditor to the MainActor - expose the WindowManager through the AppMediator - hide sensitive WindowManager API behind a different protocol - remove the now unnecessary `BackgroundTaskService`
This commit is contained in:
@@ -20,7 +20,7 @@ import MatrixRustSDK
|
||||
import SwiftUI
|
||||
import Version
|
||||
|
||||
class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDelegate, NotificationManagerDelegate, WindowManagerDelegate {
|
||||
class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDelegate, NotificationManagerDelegate, SecureWindowManagerDelegate {
|
||||
private let stateMachine: AppCoordinatorStateMachine
|
||||
private let navigationRootCoordinator: NavigationRootCoordinator
|
||||
private let userSessionStore: UserSessionStoreProtocol
|
||||
@@ -29,7 +29,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
||||
private let appDelegate: AppDelegate
|
||||
|
||||
/// Common background task to continue long-running tasks in the background.
|
||||
private var backgroundTask: BackgroundTaskProtocol?
|
||||
private var backgroundTask: UIBackgroundTaskIdentifier?
|
||||
|
||||
private var isSuspended = false
|
||||
|
||||
@@ -50,15 +50,12 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
||||
private var appLockSetupFlowCoordinator: AppLockSetupFlowCoordinator?
|
||||
private var userSessionFlowCoordinator: UserSessionFlowCoordinator?
|
||||
private var softLogoutCoordinator: SoftLogoutScreenCoordinator?
|
||||
|
||||
private let backgroundTaskService: BackgroundTaskServiceProtocol
|
||||
|
||||
private var appDelegateObserver: AnyCancellable?
|
||||
private var userSessionObserver: AnyCancellable?
|
||||
private var clientProxyObserver: AnyCancellable?
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
let windowManager: WindowManagerProtocol
|
||||
let windowManager: SecureWindowManagerProtocol
|
||||
let notificationManager: NotificationManagerProtocol
|
||||
|
||||
private let appRouteURLParser: AppRouteURLParser
|
||||
@@ -103,12 +100,9 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
||||
|
||||
navigationRootCoordinator.setRootCoordinator(SplashScreenCoordinator())
|
||||
|
||||
backgroundTaskService = UIKitBackgroundTaskService(appMediator: appMediator)
|
||||
|
||||
let keychainController = KeychainController(service: .sessions,
|
||||
accessGroup: InfoPlistReader.main.keychainAccessGroupIdentifier)
|
||||
userSessionStore = UserSessionStore(keychainController: keychainController,
|
||||
backgroundTaskService: backgroundTaskService)
|
||||
userSessionStore = UserSessionStore(keychainController: keychainController)
|
||||
|
||||
let appLockService = AppLockService(keychainController: keychainController, appSettings: appSettings)
|
||||
let appLockNavigationCoordinator = NavigationRootCoordinator()
|
||||
@@ -227,7 +221,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
||||
|
||||
// MARK: - WindowManagerDelegate
|
||||
|
||||
func windowManagerDidConfigureWindows(_ windowManager: WindowManagerProtocol) {
|
||||
func windowManagerDidConfigureWindows(_ windowManager: SecureWindowManagerProtocol) {
|
||||
windowManager.alternateWindow.rootViewController = UIHostingController(rootView: appLockFlowCoordinator.toPresentable())
|
||||
ServiceLocator.shared.userIndicatorController.window = windowManager.overlayWindow
|
||||
}
|
||||
@@ -413,8 +407,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
||||
appMediator: appMediator,
|
||||
appSettings: appSettings,
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
orientationManager: windowManager)
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
||||
authenticationFlowCoordinator?.delegate = self
|
||||
|
||||
authenticationFlowCoordinator?.start()
|
||||
@@ -469,7 +462,6 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
||||
|
||||
let userSessionFlowCoordinator = UserSessionFlowCoordinator(userSession: userSession,
|
||||
navigationRootCoordinator: navigationRootCoordinator,
|
||||
windowManager: windowManager,
|
||||
appLockService: appLockFlowCoordinator.appLockService,
|
||||
bugReportService: ServiceLocator.shared.bugReportService,
|
||||
roomTimelineControllerFactory: RoomTimelineControllerFactory(),
|
||||
@@ -770,14 +762,16 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
||||
guard backgroundTask == nil else {
|
||||
return
|
||||
}
|
||||
|
||||
backgroundTask = backgroundTaskService.startBackgroundTask(withName: "SuspendApp: \(UUID().uuidString)") { [weak self] in
|
||||
|
||||
backgroundTask = appMediator.beginBackgroundTask { [weak self] in
|
||||
guard let self else { return }
|
||||
|
||||
stopSync()
|
||||
|
||||
backgroundTask?.stop()
|
||||
backgroundTask = nil
|
||||
if let backgroundTask {
|
||||
appMediator.endBackgroundTask(backgroundTask)
|
||||
self.backgroundTask = nil
|
||||
}
|
||||
}
|
||||
|
||||
isSuspended = true
|
||||
@@ -791,8 +785,10 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
||||
private func applicationDidBecomeActive() {
|
||||
MXLog.info("Application did become active")
|
||||
|
||||
backgroundTask?.stop()
|
||||
backgroundTask = nil
|
||||
if let backgroundTask {
|
||||
appMediator.endBackgroundTask(backgroundTask)
|
||||
self.backgroundTask = nil
|
||||
}
|
||||
|
||||
if isSuspended {
|
||||
startSync()
|
||||
|
||||
@@ -17,6 +17,6 @@
|
||||
import Foundation
|
||||
|
||||
protocol AppCoordinatorProtocol: CoordinatorProtocol {
|
||||
var windowManager: WindowManagerProtocol { get }
|
||||
var windowManager: SecureWindowManagerProtocol { get }
|
||||
@discardableResult func handleDeepLink(_ url: URL, isExternalURL: Bool) -> Bool
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
import UIKit
|
||||
|
||||
class AppMediator: AppMediatorProtocol {
|
||||
private let windowManager: WindowManagerProtocol
|
||||
let windowManager: WindowManagerProtocol
|
||||
|
||||
init(windowManager: WindowManagerProtocol) {
|
||||
self.windowManager = windowManager
|
||||
@@ -28,7 +28,6 @@ class AppMediator: AppMediatorProtocol {
|
||||
UIApplication.shared
|
||||
}
|
||||
|
||||
@MainActor
|
||||
var appState: UIApplication.State {
|
||||
switch application.applicationState {
|
||||
case .active:
|
||||
@@ -42,12 +41,8 @@ class AppMediator: AppMediatorProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
var backgroundTimeRemaining: TimeInterval {
|
||||
application.backgroundTimeRemaining
|
||||
}
|
||||
|
||||
func beginBackgroundTask(withName taskName: String?, expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier {
|
||||
application.beginBackgroundTask(withName: taskName, expirationHandler: handler)
|
||||
func beginBackgroundTask(expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier {
|
||||
application.beginBackgroundTask(expirationHandler: handler)
|
||||
}
|
||||
|
||||
func endBackgroundTask(_ identifier: UIBackgroundTaskIdentifier) {
|
||||
|
||||
@@ -18,12 +18,13 @@ import Foundation
|
||||
import UIKit
|
||||
|
||||
// sourcery: AutoMockable
|
||||
@MainActor
|
||||
protocol AppMediatorProtocol {
|
||||
var windowManager: WindowManagerProtocol { get }
|
||||
|
||||
var appState: UIApplication.State { get }
|
||||
|
||||
var backgroundTimeRemaining: TimeInterval { get }
|
||||
|
||||
func beginBackgroundTask(withName taskName: String?, expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier
|
||||
func beginBackgroundTask(expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier
|
||||
|
||||
func endBackgroundTask(_ identifier: UIBackgroundTaskIdentifier)
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import SwiftUI
|
||||
///
|
||||
/// We don't support multiple scenes right now, so the implementation is pretty basic.
|
||||
class SceneDelegate: NSObject, UIWindowSceneDelegate {
|
||||
weak static var windowManager: WindowManagerProtocol!
|
||||
weak static var windowManager: SecureWindowManagerProtocol!
|
||||
|
||||
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
|
||||
guard let windowScene = scene as? UIWindowScene else { return }
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
class WindowManager: WindowManagerProtocol {
|
||||
class WindowManager: SecureWindowManagerProtocol {
|
||||
private let appDelegate: AppDelegate
|
||||
weak var windowScene: UIWindowScene?
|
||||
weak var delegate: WindowManagerDelegate?
|
||||
weak var delegate: SecureWindowManagerDelegate?
|
||||
|
||||
private(set) var mainWindow: UIWindow!
|
||||
private(set) var overlayWindow: UIWindow!
|
||||
|
||||
@@ -16,18 +16,29 @@
|
||||
|
||||
import SwiftUI
|
||||
|
||||
protocol WindowManagerDelegate: AnyObject {
|
||||
protocol SecureWindowManagerDelegate: AnyObject {
|
||||
/// The window manager has configured its windows.
|
||||
func windowManagerDidConfigureWindows(_ windowManager: WindowManagerProtocol)
|
||||
func windowManagerDidConfigureWindows(_ windowManager: SecureWindowManagerProtocol)
|
||||
}
|
||||
|
||||
@MainActor
|
||||
protocol SecureWindowManagerProtocol: WindowManagerProtocol {
|
||||
var delegate: SecureWindowManagerDelegate? { get set }
|
||||
|
||||
/// Configures the window manager to operate on the supplied scene.
|
||||
func configure(with windowScene: UIWindowScene)
|
||||
|
||||
/// Shows the main and overlay window combo, hiding the alternate window.
|
||||
func switchToMain()
|
||||
|
||||
/// Shows the alternate window, hiding the main and overlay combo.
|
||||
func switchToAlternate()
|
||||
}
|
||||
|
||||
/// A window manager that supports switching between a main app window with an overlay and
|
||||
/// an alternate window to switch contexts whilst also preserving the main view hierarchy.
|
||||
/// Heavily inspired by https://www.fivestars.blog/articles/swiftui-windows/
|
||||
protocol WindowManagerProtocol: AnyObject, OrientationManagerProtocol {
|
||||
var delegate: WindowManagerDelegate? { get set }
|
||||
|
||||
/// The app's main window (we only support a single scene).
|
||||
var mainWindow: UIWindow! { get }
|
||||
/// Presented on top of the main window, to display e.g. user indicators.
|
||||
@@ -40,15 +51,6 @@ protocol WindowManagerProtocol: AnyObject, OrientationManagerProtocol {
|
||||
/// All the windows being managed
|
||||
var windows: [UIWindow] { get }
|
||||
|
||||
/// Configures the window manager to operate on the supplied scene.
|
||||
func configure(with windowScene: UIWindowScene)
|
||||
|
||||
/// Shows the main and overlay window combo, hiding the alternate window.
|
||||
func switchToMain()
|
||||
|
||||
/// Shows the alternate window, hiding the main and overlay combo.
|
||||
func switchToAlternate()
|
||||
|
||||
/// Makes the global search window key. Used to get automatic text field focus.
|
||||
func showGlobalSearch()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user