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)
}
}
}
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)
}
}
}