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:
Stefan Ceriu
2024-04-22 18:10:24 +03:00
committed by GitHub
parent a3ec0b8505
commit 8ba544bc44
34 changed files with 117 additions and 929 deletions
@@ -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()