Search code examples
iosswiftpush-notificationapple-push-notificationsapn

Sometimes empty content image from remote notification using UNUserNotificationCenter


I'm using a feature which was announced by Apple in iOS 10 last year. I have the issue that the image in my notification is sometimes empty.

This is my UNNotificationServiceExtension.. I'm not really sure what I'm doing wrong. The images have a small size of max 1 MB. My payload from the server are correctly.

class NotificationService: UNNotificationServiceExtension {

    var contentHandler: ((UNNotificationContent) -> Void)?
    var bestAttemptContent: UNMutableNotificationContent?

    override func didReceive(_ request: UNNotificationRequest, withContentHandler  contentHandler: @escaping (UNNotificationContent) -> Void) {
        self.contentHandler = contentHandler
        bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)

        if let bestAttemptContent = bestAttemptContent {
            // Setting the category associated with the notification
            if let category = bestAttemptContent.userInfo["category"] as? String {
                bestAttemptContent.categoryIdentifier = category
            }

            // Fetching luubra if available
            if let attachmentString = bestAttemptContent.userInfo["image"] as? String,
                let attachmentUrl = URL(string: attachmentString) {

                let session = URLSession(configuration: URLSessionConfiguration.default)
                let attachmentDownloadTask = session.downloadTask(with: attachmentUrl,
                                                                  completionHandler: { url, _, error in
                        if let error = error {
                            print("Error downloading notification image: \(error)")
                        } else if let url = url {
                            do {
                                let attachment = try UNNotificationAttachment(identifier: attachmentString,
                                                                              url: url,
                                                                              options: [UNNotificationAttachmentOptionsTypeHintKey: kUTTypeJPEG])
                                bestAttemptContent.attachments = [attachment]
                            } catch let e {
                                print("Error creating NotificationAttachment: \(e)")
                            }
                        }
                        print("Remote notification content: \(bestAttemptContent)")
                        contentHandler(bestAttemptContent)
                })
                attachmentDownloadTask.resume()

            }
        }
    }

    override func serviceExtensionTimeWillExpire() {
        // Called just before the extension will be terminated by the system.
        // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
        if let contentHandler = contentHandler,
            let bestAttemptContent =  bestAttemptContent {
            contentHandler(bestAttemptContent)
        }
    }

}

Solution

  • It seems Apple will set the category associated directly like the title or body content. It's important to save the media temporary to the disk. Because the iOS need some time to download the media file. iOS will handle the rest.

    This works know awesome for me.

    class NotificationService: UNNotificationServiceExtension {
    
        var contentHandler: ((UNNotificationContent) -> Void)?
        var bestAttemptContent: UNMutableNotificationContent?
    
        override func didReceive(_ request: UNNotificationRequest, withContentHandler  contentHandler: @escaping (UNNotificationContent) -> Void) {
    
            self.contentHandler = contentHandler
            self.bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
    
            func failed() {
                contentHandler(request.content)
            }
    
            guard let contentHandler = self.contentHandler, let bestAttemptContent = self.bestAttemptContent else {
                return failed()
            }
    
            // Get the image from the User Payload
            guard let imageURLString = request.content.userInfo["image"] as? String else {
                return failed()
            }
    
            guard let imageURL = URL(string: imageURLString) else {
                return failed()
            }
    
            // Download the Image Async
            URLSession.shared.downloadTask(with: imageURL) { (path, _, error) in
    
                if let error = error {
                    print(error.localizedDescription)
                }
    
                if let path = path {
    
                    // Save the image temporary to the disk
                    let tmpDirectory = NSTemporaryDirectory()
                    let tmpFile = "file://".appending(tmpDirectory).appending(imageURL.lastPathComponent)
                    guard let tmpURL = URL(string: tmpFile) else { return }
                    try? FileManager.default.moveItem(at: path, to: tmpURL)
    
                    // Add the attachment to the notification content
                    if let attachment = try? UNNotificationAttachment(identifier: "", url: tmpURL) {
                        bestAttemptContent.attachments = [attachment]
                    }
                }
    
                // Serve the notification content
                contentHandler(bestAttemptContent)
    
            }.resume()
    
        }
    
        override func serviceExtensionTimeWillExpire() {
            // Called just before the extension will be terminated by the system.
            // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
            if let contentHandler = contentHandler,
                let bestAttemptContent =  bestAttemptContent {
                contentHandler(bestAttemptContent)
            }
        }
    }