Attempt to simplify the background app refresh flows and always clean up the task while at the same time make sure the task is always cleaned up (#930)

This commit is contained in:
Stefan Ceriu
2023-05-19 18:22:27 +03:00
committed by GitHub
parent 4e46c01945
commit 35d7f1f893
@@ -27,6 +27,10 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate,
/// Common background task to resume long-running tasks in the background. /// 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. /// When this task expiring, we'll try to suspend the state machine by `suspend` event.
private var backgroundTask: BackgroundTaskProtocol? private var backgroundTask: BackgroundTaskProtocol?
/// Task used while processing background app refreshes
private var backgroundAppRefreshTask: BGAppRefreshTask?
private var isSuspended = false private var isSuspended = false
private var userSession: UserSessionProtocol? { private var userSession: UserSessionProtocol? {
@@ -500,14 +504,20 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate,
private func stopSync() { private func stopSync() {
userSession?.clientProxy.stopSync() userSession?.clientProxy.stopSync()
backgroundAppRefreshTask?.setTaskCompleted(success: true)
backgroundAppRefreshTask = nil
} }
private func startSync() { private func startSync() {
// We don't fatal error here because background app refreshes might be scheduled before the session is setup guard let userSession else {
guard let userSession else { return } fatalError("User session not setup")
}
userSession.clientProxy.startSync() userSession.clientProxy.startSync()
installBackgroundAppRefreshMonitor()
let identifier = "StaleDataIndicator" let identifier = "StaleDataIndicator"
ServiceLocator.shared.userIndicatorController.submitIndicator(.init(id: identifier, type: .toast, title: L10n.commonLoading, persistent: true)) ServiceLocator.shared.userIndicatorController.submitIndicator(.init(id: identifier, type: .toast, title: L10n.commonLoading, persistent: true))
@@ -542,13 +552,15 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate,
} }
backgroundTask = backgroundTaskService.startBackgroundTask(withName: "SuspendApp: \(UUID().uuidString)") { [weak self] in backgroundTask = backgroundTaskService.startBackgroundTask(withName: "SuspendApp: \(UUID().uuidString)") { [weak self] in
guard let self else { guard let self else { return }
return
}
userSession?.clientProxy.stopSync { userSession?.clientProxy.stopSync {
// No need to weakify self, this is a non escaping closure // No need to weakify self, this is a non escaping closure
self.backgroundTask?.stop() self.backgroundTask?.stop()
self.backgroundTask = nil self.backgroundTask = nil
self.backgroundAppRefreshTask?.setTaskCompleted(success: true)
self.backgroundAppRefreshTask = nil
} }
} }
@@ -605,20 +617,35 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate,
private func handleBackgroundAppRefresh(_ task: BGAppRefreshTask) { private func handleBackgroundAppRefresh(_ task: BGAppRefreshTask) {
MXLog.info("Started background app refresh") MXLog.info("Started background app refresh")
backgroundAppRefreshTask = task
// This is important for the app to keep refreshing in the background // This is important for the app to keep refreshing in the background
scheduleBackgroundAppRefresh() scheduleBackgroundAppRefresh()
task.expirationHandler = { [weak self] in task.expirationHandler = { [weak self] in
MXLog.info("Background app refresh task expired") MXLog.info("Background app refresh task expired")
self?.stopSync() self?.stopSync()
task.setTaskCompleted(success: true) }
guard let userSession, !userSession.clientProxy.isSyncing else {
return
} }
startSync() startSync()
}
private func installBackgroundAppRefreshMonitor() {
guard let userSession else {
fatalError("User session not setup")
}
guard backgroundAppRefreshTask != nil else {
return
}
// Be a good citizen, run for a max of 10 SS responses or 10 seconds // Be a good citizen, run for a max of 10 SS responses or 10 seconds
// An SS request will time out after 30 seconds if no new data is available // An SS request will time out after 30 seconds if no new data is available
backgroundRefreshSyncObserver = userSession?.clientProxy backgroundRefreshSyncObserver = userSession.clientProxy
.callbacks .callbacks
.filter(\.isSyncUpdate) .filter(\.isSyncUpdate)
.collect(.byTimeOrCount(DispatchQueue.main, .seconds(10), 10)) .collect(.byTimeOrCount(DispatchQueue.main, .seconds(10), 10))
@@ -626,7 +653,6 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationCoordinatorDelegate,
MXLog.info("Background app refresh finished") MXLog.info("Background app refresh finished")
self?.backgroundRefreshSyncObserver?.cancel() self?.backgroundRefreshSyncObserver?.cancel()
self?.stopSync() self?.stopSync()
task.setTaskCompleted(success: true)
}) })
} }
} }