Search code examples
iosapple-push-notificationsios-app-extensionunnotificationserviceextensionserviceextension

Logging from Notification Service Extension


So currently I'm doing this:

override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
    logger = AnalyticsManager.shared
    self.contentHandler = contentHandler
    bestAttemptContent = (request.content.mutableCopy() as! UNMutableNotificationContent)
    if let notificationContent = bestAttemptContent {
        extensionHandler = ServiceExtensionHandler(notificationContent: notificationContent, logger: AnalyticsManager.shared)
        extensionHandler?.handleNotificationRequest { modifiedNotificationContent in
            guard let modifiedNotificationContent = modifiedNotificationContent else {
                contentHandler(notificationContent)
                return
            }
            contentHandler(modifiedNotificationContent)
        }
    } else {
        contentHandler(request.content)
        return
    }
}

In my ServiceExtensionHandler if image download succeeds, I'll return the modified notification. So far so good.

But once the image download succeeds, I want to log an event. Problem is if I return the contentHandler then the OS will kill the extension and I won't have time to complete my log.

Shortly after the app extension performs its task (or starts a background session to perform it), the system terminates the extension.

Docs: An App Extension’s Life Cycle

At the moment it's working but that extra network call isn't guaranteed to work.

I could only return that completionHandler if the log returns, but then well the log could timeout in 60 seconds and that's another problem in itself as well.

Has anyone thought of any solutions for this?


Solution

  • You're on the right track.

    You should return the contentHandler ultimately from a callback that is derived off of the logging's network callback. Just with a minor change:

    For your log's network call, set its URLSessionConfiguration s timeoutIntervalForRequest to something lower than 5 seconds. This way you don't wait to long for it to complete.

    This is pseudo code, but for the object that does the logging do something like:

    if let imageData = data {
        self?.log("downloadSuccess") {
            completionHandler(imageData, nil)
       }
    }
    

    It either succeeds/fails in less than 5 seconds or if it's going to timeout, it won't take any longer than 5 seconds.