From 03ecb984bff8fab512fb3da7a98e30b611597c12 Mon Sep 17 00:00:00 2001 From: Mattermost Build Date: Thu, 18 Jun 2020 03:26:43 +0200 Subject: [PATCH] MM-25955 Refactor backoff/retry logic for failed id-loaded notifications (#4446) Triggered per [this conversation](https://github.com/mattermost/mattermost-mobile/pull/4302#discussion_r426426887) Raised question https://stackoverflow.com/questions/62293185/recursive-function-with-delay-in-between-calls?noredirect=1#comment110172449_62293185 which resolved the recursion. Co-authored-by: Amit Uttam --- .../NotificationService.swift | 88 ++++++++++--------- 1 file changed, 46 insertions(+), 42 deletions(-) diff --git a/ios/NotificationService/NotificationService.swift b/ios/NotificationService/NotificationService.swift index e233ea4e8..1051541df 100644 --- a/ios/NotificationService/NotificationService.swift +++ b/ios/NotificationService/NotificationService.swift @@ -5,57 +5,62 @@ class NotificationService: UNNotificationServiceExtension { var contentHandler: ((UNNotificationContent) -> Void)? var bestAttemptContent: UNMutableNotificationContent? - var sendFailed = false; + + var retryIndex = 0 override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { self.contentHandler = contentHandler - bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) - if let bestAttemptContent = bestAttemptContent { - let ackId = bestAttemptContent.userInfo["ack_id"] - let type = bestAttemptContent.userInfo["type"] - let postId = bestAttemptContent.userInfo["post_id"] - let idLoaded = (bestAttemptContent.userInfo["id_loaded"] ?? false) as! Bool + + let fibonacciBackoffsInSeconds = [1.0, 2.0, 3.0, 5.0, 8.0] + + func fetchReceipt(notificationId: String, receivedAt: Int, type: String, postId: String, idLoaded: Bool ) -> Void { + if (self.retryIndex >= fibonacciBackoffsInSeconds.count) { + return + } UploadSession.shared.notificationReceipt( + notificationId: notificationId, + receivedAt: receivedAt, + type: type, + postId: postId, + idLoaded: idLoaded) { data, error in + guard let data = data, error == nil else { + if (idLoaded) { + // Receipt retrieval failed. Kick off retries. + let backoffInSeconds = fibonacciBackoffsInSeconds[self.retryIndex] + + DispatchQueue.main.asyncAfter(deadline: .now() + backoffInSeconds, execute: { + fetchReceipt( + notificationId: notificationId, + receivedAt: Date().millisecondsSince1970, + type: type, + postId: postId, + idLoaded: idLoaded + ) + }) + + self.retryIndex += 1 + } + return + } + self.processResponse(data: data, bestAttemptContent: self.bestAttemptContent!, contentHandler: contentHandler) + } + } + + bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) + if let bestAttemptContent = bestAttemptContent { + let ackId = bestAttemptContent.userInfo["ack_id"] as! String + let type = bestAttemptContent.userInfo["type"] as! String + let postId = bestAttemptContent.userInfo["post_id"] as! String + let idLoaded = (bestAttemptContent.userInfo["id_loaded"] ?? false) as! Bool + + fetchReceipt( notificationId: ackId, receivedAt: Date().millisecondsSince1970, type: type, postId: postId, idLoaded: idLoaded - ) { data, error in - guard let data = data, error == nil else { - if (idLoaded) { - self.sendFailed = true; - let fibonacciBackoffsInSeconds = [1.0, 2.0, 3.0, 5.0, 8.0] - for backoffInSeconds in fibonacciBackoffsInSeconds { - let timer = Timer.scheduledTimer(withTimeInterval: backoffInSeconds, repeats: false) { timer in - UploadSession.shared.notificationReceipt( - notificationId: ackId, - receivedAt: Date().millisecondsSince1970, - type: type, - postId: postId, - idLoaded: idLoaded - ) { data, error in - guard let data = data, error == nil else { - self.sendFailed = true; - return - } - self.processResponse(data: data, bestAttemptContent: bestAttemptContent, contentHandler: contentHandler) - } - } - - if (self.sendFailed) { - self.sendFailed = false - timer.fire() - } else { - break - } - } - } - return - } - self.processResponse(data: data, bestAttemptContent: bestAttemptContent, contentHandler: contentHandler) - } + ) } } @@ -88,7 +93,6 @@ class NotificationService: UNNotificationServiceExtension { contentHandler(bestAttemptContent) } } - } extension Date {