diff --git a/Session/Meta/AppDelegate.swift b/Session/Meta/AppDelegate.swift index 9e79ae3cf..fca92e62e 100644 --- a/Session/Meta/AppDelegate.swift +++ b/Session/Meta/AppDelegate.swift @@ -195,11 +195,43 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD // Resume database NotificationCenter.default.post(name: Database.resumeNotification, object: self) + // Background tasks only last for a certain amount of time (which can result in a crash and a + // prompt appearing for the user), we want to avoid this and need to make sure to suspend the + // database again before the background task ends so we start a timer that expires 1 second + // before the background task is due to expire in order to do so + let cancelTimer: Timer = Timer.scheduledTimerOnMainThread( + withTimeInterval: (application.backgroundTimeRemaining - 1), + repeats: false + ) { timer in + timer.invalidate() + + guard BackgroundPoller.isValid else { return } + + BackgroundPoller.isValid = false + + // Suspend database + NotificationCenter.default.post(name: Database.suspendNotification, object: self) + + SNLog("Background poll failed due to manual timeout") + completionHandler(.failed) + } + + // Flag the background poller as valid first and then trigger it to poll once the app is + // ready (we do this here rather than in `BackgroundPoller.poll` to avoid the rare edge-case + // that could happen when the timeout triggers before the app becomes ready which would have + // incorrectly set this 'isValid' flag to true after it should have timed out) + BackgroundPoller.isValid = true + AppReadiness.runNowOrWhenAppDidBecomeReady { BackgroundPoller.poll { result in + guard BackgroundPoller.isValid else { return } + + BackgroundPoller.isValid = false + // Suspend database NotificationCenter.default.post(name: Database.suspendNotification, object: self) + cancelTimer.invalidate() completionHandler(result) } } diff --git a/Session/Utilities/BackgroundPoller.swift b/Session/Utilities/BackgroundPoller.swift index 03f575c48..3ae9d0a03 100644 --- a/Session/Utilities/BackgroundPoller.swift +++ b/Session/Utilities/BackgroundPoller.swift @@ -9,11 +9,9 @@ import SessionUtilitiesKit public final class BackgroundPoller { private static var promises: [Promise] = [] - private static var isValid: Bool = false + public static var isValid: Bool = false public static func poll(completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { - BackgroundPoller.isValid = true - promises = [] .appending(pollForMessages()) .appending(contentsOf: pollForClosedGroupMessages()) @@ -43,33 +41,18 @@ public final class BackgroundPoller { } ) - // Background tasks will automatically be terminated after 30 seconds (which results in a crash - // and a prompt to appear for the user) we want to avoid this so we start a timer which expires - // after 25 seconds allowing us to cancel all pending promises - let cancelTimer: Timer = Timer.scheduledTimerOnMainThread(withTimeInterval: 25, repeats: false) { timer in - timer.invalidate() - BackgroundPoller.isValid = false - - guard promises.contains(where: { !$0.isResolved }) else { return } - - SNLog("Background poll failed due to manual timeout") - completionHandler(.failed) - } - when(resolved: promises) .done { _ in // If we have already invalidated the timer then do nothing (we essentially timed out) - guard cancelTimer.isValid else { return } + guard BackgroundPoller.isValid else { return } - cancelTimer.invalidate() completionHandler(.newData) } .catch { error in // If we have already invalidated the timer then do nothing (we essentially timed out) - guard cancelTimer.isValid else { return } + guard BackgroundPoller.isValid else { return } SNLog("Background poll failed due to error: \(error)") - cancelTimer.invalidate() completionHandler(.failed) } } diff --git a/SessionMessagingKit/Sending & Receiving/Pollers/OpenGroupPoller.swift b/SessionMessagingKit/Sending & Receiving/Pollers/OpenGroupPoller.swift index fc4b15eb1..7f270a4c4 100644 --- a/SessionMessagingKit/Sending & Receiving/Pollers/OpenGroupPoller.swift +++ b/SessionMessagingKit/Sending & Receiving/Pollers/OpenGroupPoller.swift @@ -202,7 +202,7 @@ extension OpenGroupAPI { case .httpRequestFailedAtDestination(let statusCode, let data, _) = error, statusCode == 400, let dataString: String = String(data: data, encoding: .utf8), - dataString.contains("Invalid authentication: this server requires the use of blinded idse") + dataString.contains("Invalid authentication: this server requires the use of blinded ids") else { return Promise.value(false) } let (promise, seal) = Promise.pending()