mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 21:33:26 +00:00
Fix state machine crashes & background tasks (#343)
* Fix `UIApplication.shared` after moving to SwiftUI app * Do not autoplay videos on background * Move app state changes into the app coordinator * Add application background task, move into the suspended state more accurately * Add changelog * Fix most of the linter errors * Strip suspended state from state machine * Fix build * Clear audio session warning * Update AppCoordinator.swift * Update AppCoordinator.swift * Swift format
This commit is contained in:
@@ -22,6 +22,14 @@ class AppCoordinator: AppCoordinatorProtocol {
|
||||
private let stateMachine: AppCoordinatorStateMachine
|
||||
private let navigationController: NavigationController
|
||||
private let userSessionStore: UserSessionStoreProtocol
|
||||
/// Common background task to resume long-running tasks in the background.
|
||||
/// When this task expiring, we'll try to suspend the state machine by `suspend` event.
|
||||
private var backgroundTask: BackgroundTaskProtocol?
|
||||
private var isSuspended = false {
|
||||
didSet {
|
||||
MXLog.debug("didSet to: \(isSuspended)")
|
||||
}
|
||||
}
|
||||
|
||||
private var userSession: UserSessionProtocol! {
|
||||
didSet {
|
||||
@@ -52,7 +60,9 @@ class AppCoordinator: AppCoordinatorProtocol {
|
||||
|
||||
ServiceLocator.shared.register(userNotificationController: UserNotificationController(rootCoordinator: navigationController))
|
||||
|
||||
backgroundTaskService = UIKitBackgroundTaskService(withApplication: UIApplication.shared)
|
||||
backgroundTaskService = UIKitBackgroundTaskService {
|
||||
UIApplication.shared
|
||||
}
|
||||
|
||||
userSessionStore = UserSessionStore(backgroundTaskService: backgroundTaskService)
|
||||
|
||||
@@ -61,6 +71,8 @@ class AppCoordinator: AppCoordinatorProtocol {
|
||||
setupLogging()
|
||||
|
||||
Bundle.elementFallbackLanguage = "en"
|
||||
|
||||
startObservingApplicationState()
|
||||
|
||||
// Benchmark.trackingEnabled = true
|
||||
}
|
||||
@@ -324,6 +336,52 @@ class AppCoordinator: AppCoordinatorProtocol {
|
||||
private func showLoginErrorToast() {
|
||||
ServiceLocator.shared.userNotificationController.submitNotification(UserNotification(title: "Failed logging in"))
|
||||
}
|
||||
|
||||
// MARK: - Application State
|
||||
|
||||
private func pause() {
|
||||
userSession?.clientProxy.stopSync()
|
||||
}
|
||||
|
||||
private func resume() {
|
||||
userSession?.clientProxy.startSync()
|
||||
}
|
||||
|
||||
private func startObservingApplicationState() {
|
||||
NotificationCenter.default.addObserver(self,
|
||||
selector: #selector(applicationWillResignActive),
|
||||
name: UIApplication.willResignActiveNotification,
|
||||
object: nil)
|
||||
NotificationCenter.default.addObserver(self,
|
||||
selector: #selector(applicationDidBecomeActive),
|
||||
name: UIApplication.didBecomeActiveNotification,
|
||||
object: nil)
|
||||
}
|
||||
|
||||
@objc
|
||||
private func applicationWillResignActive() {
|
||||
guard backgroundTask == nil else {
|
||||
return
|
||||
}
|
||||
|
||||
backgroundTask = backgroundTaskService.startBackgroundTask(withName: "SuspendApp: \(UUID().uuidString)") { [weak self] in
|
||||
self?.pause()
|
||||
|
||||
self?.backgroundTask = nil
|
||||
self?.isSuspended = true
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
private func applicationDidBecomeActive() {
|
||||
backgroundTask?.stop()
|
||||
backgroundTask = nil
|
||||
|
||||
if isSuspended {
|
||||
isSuspended = false
|
||||
resume()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - AuthenticationCoordinatorDelegate
|
||||
|
||||
@@ -62,27 +62,38 @@ class AppCoordinatorStateMachine {
|
||||
private let stateMachine: StateMachine<State, Event>
|
||||
|
||||
init() {
|
||||
stateMachine = StateMachine(state: .initial) { machine in
|
||||
machine.addRoutes(event: .startWithAuthentication, transitions: [.initial => .signedOut])
|
||||
machine.addRoutes(event: .succeededSigningIn, transitions: [.signedOut => .signedIn])
|
||||
|
||||
machine.addRoutes(event: .startWithExistingSession, transitions: [.initial => .restoringSession])
|
||||
machine.addRoutes(event: .succeededRestoringSession, transitions: [.restoringSession => .signedIn])
|
||||
machine.addRoutes(event: .failedRestoringSession, transitions: [.restoringSession => .signedOut])
|
||||
|
||||
machine.addRoutes(event: .signOut, transitions: [.any => .signingOut])
|
||||
machine.addRoutes(event: .completedSigningOut, transitions: [.signingOut => .signedOut])
|
||||
|
||||
// Transitions with associated values need to be handled through `addRouteMapping`
|
||||
machine.addRouteMapping { event, fromState, _ in
|
||||
switch (event, fromState) {
|
||||
case (.remoteSignOut(let isSoft), _):
|
||||
return .remoteSigningOut(isSoft: isSoft)
|
||||
case (.completedSigningOut, .remoteSigningOut):
|
||||
return .signedOut
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
stateMachine = StateMachine(state: .initial)
|
||||
configure()
|
||||
}
|
||||
|
||||
private func configure() {
|
||||
stateMachine.addRoutes(event: .startWithAuthentication, transitions: [.initial => .signedOut])
|
||||
stateMachine.addRoutes(event: .succeededSigningIn, transitions: [.signedOut => .signedIn])
|
||||
|
||||
stateMachine.addRoutes(event: .startWithExistingSession, transitions: [.initial => .restoringSession])
|
||||
stateMachine.addRoutes(event: .succeededRestoringSession, transitions: [.restoringSession => .signedIn])
|
||||
stateMachine.addRoutes(event: .failedRestoringSession, transitions: [.restoringSession => .signedOut])
|
||||
|
||||
stateMachine.addRoutes(event: .signOut, transitions: [.any => .signingOut])
|
||||
stateMachine.addRoutes(event: .completedSigningOut, transitions: [.signingOut => .signedOut])
|
||||
|
||||
// Transitions with associated values need to be handled through `addRouteMapping`
|
||||
stateMachine.addRouteMapping { event, fromState, _ in
|
||||
switch (event, fromState) {
|
||||
case (.remoteSignOut(let isSoft), _):
|
||||
return .remoteSigningOut(isSoft: isSoft)
|
||||
case (.completedSigningOut, .remoteSigningOut):
|
||||
return .signedOut
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
addTransitionHandler { context in
|
||||
if let event = context.event {
|
||||
MXLog.info("Transitioning from `\(context.fromState)` to `\(context.toState)` with event `\(event)`")
|
||||
} else {
|
||||
MXLog.info("Transitioning from \(context.fromState)` to `\(context.toState)`")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user