Parcourir la source

Merge pull request #1219 from deltachat/registering_background_task-tweaks

background task tweaks
cyBerta il y a 4 ans
Parent
commit
1ba4fc4fe4
2 fichiers modifiés avec 59 ajouts et 79 suppressions
  1. 14 22
      deltachat-ios/AppDelegate.swift
  2. 45 57
      deltachat-ios/Helper/NotificationManager.swift

+ 14 - 22
deltachat-ios/AppDelegate.swift

@@ -16,7 +16,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     var locationManager: LocationManager!
     var notificationManager: NotificationManager!
     private var backgroundTask: UIBackgroundTaskIdentifier = .invalid
-    private var fetchBackgroundTasks = [Double: UIBackgroundTaskIdentifier]()
     var reachability = Reachability()!
     var window: UIWindow?
     var notifyToken: String?
@@ -346,24 +345,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         increaseDebugCounter("notify-local-wakeup")
         performFetch(completionHandler: completionHandler)
     }
-    
-    private func unregisterFetchBackgroundTask(timestamp: Double) {
-        let backgroundTask = fetchBackgroundTasks[timestamp]
-        if let backgroundTask = backgroundTask, backgroundTask != .invalid {
-            UIApplication.shared.endBackgroundTask(backgroundTask)
-            fetchBackgroundTasks.removeValue(forKey: timestamp)
-            logger.info("⬅️ fetch background task ended and removed: \(timestamp)")
-        }
-    }
-    
-    private func registerFetchBackgroundTask(timestamp: Double) {
-        logger.info("⬅️ registering fetch background task: \(timestamp)")
-        let backgroundTask = UIApplication.shared.beginBackgroundTask { [weak self] in
-            logger.info("⬅️ fetch background expirationHandler called")
-            self?.unregisterFetchBackgroundTask(timestamp: timestamp)
-        }
-        fetchBackgroundTasks[timestamp] = backgroundTask
-    }
 
     private func performFetch(completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
         // `didReceiveRemoteNotification` as well as `performFetchWithCompletionHandler` might be called if we're in foreground,
@@ -389,7 +370,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
             return
         }
         bgIoTimestamp = nowTimestamp
-        registerFetchBackgroundTask(timestamp: nowTimestamp)
+
+        // make sure to balance each call to `beginBackgroundTask` with `endBackgroundTask`
+        let backgroundTask = UIApplication.shared.beginBackgroundTask {
+            // TODO: currently, this should be okay, we are not using too much background time,
+            // so that handler should never be called.
+            // the plan is to listen to an event as DC_EVENT_ENTER_IDLE and stop fetch from there.
+            // if we have such an event, we could imprive this handler and fire the event.
+            logger.info("fetch background task will end soon")
+        }
 
         // we're in background, run IO for a little time
         dcContext.maybeStartIo()
@@ -408,10 +397,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
             // to avoid 0xdead10cc exceptions, scheduled jobs need to be done before we get suspended;
             // we increase the probabilty that this happens by waiting a moment before calling completionHandler()
-            DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [weak self] in
+            DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                 logger.info("⬅️ fetch done")
-                self?.unregisterFetchBackgroundTask(timestamp: nowTimestamp)
                 completionHandler(.newData)
+
+                // this line should always be reached after a background task is started
+                // and balances the call to `beginBackgroundTask` above.
+                UIApplication.shared.endBackgroundTask(backgroundTask)
             }
         }
     }

+ 45 - 57
deltachat-ios/Helper/NotificationManager.swift

@@ -7,7 +7,6 @@ public class NotificationManager {
     
     var incomingMsgObserver: NSObjectProtocol?
     var msgsNoticedObserver: NSObjectProtocol?
-    private var notificationBackgroundTasks = [Int: UIBackgroundTaskIdentifier]()
 
 
     init() {
@@ -39,31 +38,20 @@ public class NotificationManager {
             NotificationManager.updateApplicationIconBadge(reset: false)
         }
     }
-    
-    private func unregisterNotificationBackgroundTask(msgId: Int) {
-        let backgroundTask = notificationBackgroundTasks[msgId]
-        if let backgroundTask = backgroundTask, backgroundTask != .invalid {
-            UIApplication.shared.endBackgroundTask(backgroundTask)
-            notificationBackgroundTasks.removeValue(forKey: msgId)
-            logger.info("⬅️ notification background task ended and removed: \(msgId)")
-        }
-    }
-    
-    private func registerNotificationBackgroundTask(msgId: Int) {
-        logger.info("⬅️ notification notification background task: \(msgId)")
-        let backgroundTask = UIApplication.shared.beginBackgroundTask { [weak self] in
-            logger.info("⬅️ notification background expirationHandler called")
-            self?.unregisterNotificationBackgroundTask(msgId: msgId)
-        }
-        notificationBackgroundTasks[msgId] = backgroundTask
-    }
 
     private func initIncomingMsgsObserver() {
         incomingMsgObserver = NotificationCenter.default.addObserver(
             forName: dcNotificationIncoming,
             object: nil, queue: OperationQueue.main
         ) { notification in
-            DispatchQueue.global(qos: .background).async { [weak self] in
+            // make sure to balance each call to `beginBackgroundTask` with `endBackgroundTask`
+            let backgroundTask = UIApplication.shared.beginBackgroundTask {
+                // we cannot easily stop the task,
+                // however, this handler should not be called as adding the notification should not take 30 seconds.
+                logger.info("notification background task will end soon")
+            }
+
+            DispatchQueue.global(qos: .background).async {
                 if let ui = notification.userInfo,
                    let chatId = ui["chat_id"] as? Int,
                    let messageId = ui["message_id"] as? Int,
@@ -72,47 +60,47 @@ public class NotificationManager {
                     NotificationManager.updateApplicationIconBadge(reset: false)
 
                     let chat = DcContext.shared.getChat(chatId: chatId)
-                    if chat.isMuted {
-                        return
-                    }
-                    
-                    self?.registerNotificationBackgroundTask(msgId: messageId)
-                    let content = UNMutableNotificationContent()
-                    let msg = DcMsg(id: messageId)
-                    content.title = chat.isGroup ? chat.name : msg.getSenderName(msg.fromContact)
-                    content.body =  msg.summary(chars: 80) ?? ""
-                    content.subtitle = chat.isGroup ?  msg.getSenderName(msg.fromContact) : ""
-                    content.userInfo = ui
-                    content.sound = .default
-
-                    if msg.type == DC_MSG_IMAGE || msg.type == DC_MSG_GIF,
-                       let url = msg.fileURL {
-                        do {
-                            // make a copy of the file first since UNNotificationAttachment will move attached files into the attachment data store
-                            // so that they can be accessed by all of the appropriate processes
-                            let tempUrl = url.deletingLastPathComponent()
-                                .appendingPathComponent("notification_tmp")
-                                .appendingPathExtension(url.pathExtension)
-                            try FileManager.default.copyItem(at: url, to: tempUrl)
-                            if let attachment = try? UNNotificationAttachment(identifier: Constants.notificationIdentifier, url: tempUrl, options: nil) {
-                                content.attachments = [attachment]
+                    if !chat.isMuted {
+                        let content = UNMutableNotificationContent()
+                        let msg = DcMsg(id: messageId)
+                        content.title = chat.isGroup ? chat.name : msg.getSenderName(msg.fromContact)
+                        content.body =  msg.summary(chars: 80) ?? ""
+                        content.subtitle = chat.isGroup ?  msg.getSenderName(msg.fromContact) : ""
+                        content.userInfo = ui
+                        content.sound = .default
+
+                        if msg.type == DC_MSG_IMAGE || msg.type == DC_MSG_GIF,
+                           let url = msg.fileURL {
+                            do {
+                                // make a copy of the file first since UNNotificationAttachment will move attached files into the attachment data store
+                                // so that they can be accessed by all of the appropriate processes
+                                let tempUrl = url.deletingLastPathComponent()
+                                    .appendingPathComponent("notification_tmp")
+                                    .appendingPathExtension(url.pathExtension)
+                                try FileManager.default.copyItem(at: url, to: tempUrl)
+                                if let attachment = try? UNNotificationAttachment(identifier: Constants.notificationIdentifier, url: tempUrl, options: nil) {
+                                    content.attachments = [attachment]
+                                }
+                            } catch let error {
+                                logger.error("Failed to copy file \(url) for notification preview generation: \(error)")
                             }
-                        } catch let error {
-                            logger.error("Failed to copy file \(url) for notification preview generation: \(error)")
                         }
+                        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.1, repeats: false)
+                        let accountEmail = DcContact(id: Int(DC_CONTACT_ID_SELF)).email
+                        if #available(iOS 12.0, *) {
+                            content.threadIdentifier = "\(accountEmail)\(chatId)"
+                        }
+                        let request = UNNotificationRequest(identifier: "\(Constants.notificationIdentifier).\(accountEmail).\(chatId).\(msg.messageId)",
+                                                            content: content,
+                                                            trigger: trigger)
+                        UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
+                        logger.info("notifications: added \(content.title) \(content.body) \(content.userInfo)")
                     }
-                    let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 0.1, repeats: false)
-                    let accountEmail = DcContact(id: Int(DC_CONTACT_ID_SELF)).email
-                    if #available(iOS 12.0, *) {
-                        content.threadIdentifier = "\(accountEmail)\(chatId)"
-                    }
-                    let request = UNNotificationRequest(identifier: "\(Constants.notificationIdentifier).\(accountEmail).\(chatId).\(msg.messageId)",
-                                                        content: content,
-                                                        trigger: trigger)
-                    UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
-                    logger.info("notifications: added \(content.title) \(content.body) \(content.userInfo)")
-                    self?.unregisterNotificationBackgroundTask(msgId: messageId)
                 }
+
+                // this line should always be reached
+                // and balances the call to `beginBackgroundTask` above.
+                UIApplication.shared.endBackgroundTask(backgroundTask)
             }
         }
     }