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)
}
}
}
Related
import UserNotifications
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 {
// Modify the notification content here...
bestAttemptContent.title = "\(bestAttemptContent.title) [modified]"
var urlString:String? = nil
if let urlImageString = request.content.userInfo["urlImageString"] as? String {
urlString = urlImageString
}
if urlString != nil, let fileUrl = URL(string: urlString!) {
print("fileUrl: \(fileUrl)")
guard let imageData = NSData(contentsOf: fileUrl) else {
contentHandler(bestAttemptContent)
return
}
guard let attachment = UNNotificationAttachment.saveImageToDisk(fileIdentifier: "image.jpg", data: imageData, options: nil) else {
print("error in UNNotificationAttachment.saveImageToDisk()")
contentHandler(bestAttemptContent)
return
}
bestAttemptContent.attachments = [ attachment ]
}
contentHandler(bestAttemptContent)
}
}
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)
}
}
}
#available(iOSApplicationExtension 10.0, *)
extension UNNotificationAttachment {
static func saveImageToDisk(fileIdentifier: String, data: NSData, options: [NSObject : AnyObject]?) -> UNNotificationAttachment? {
let fileManager = FileManager.default
let folderName = ProcessInfo.processInfo.globallyUniqueString
let folderURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(folderName, isDirectory: true)
do {
try fileManager.createDirectory(at: folderURL!, withIntermediateDirectories: true, attributes: nil)
let fileURL = folderURL?.appendingPathComponent(fileIdentifier)
try data.write(to: fileURL!, options: [])
let attachment = try UNNotificationAttachment(identifier: fileIdentifier, url: fileURL!, options: options)
return attachment
} catch let error {
print("error \(error)")
}
return nil
}
}
I tried using more than one source, but the result is the same, the notification appears without a picture. What is the reason here? How can I solve the problem?
I am trying to create iOS Rich Push notifications with Xcode, Swift3.
I already make sure about push notifications (subject, body) with curl command of php but I can't create Rich Push Notifications referred to in this document.
I added Notification Service Extension like this: 「 File 」→「 New 」→「 Target... 」→「 Notification Service Extension 」 and also I added in 「'mutable_content': True」 curl command.
Then run but not call 「class NotificationService: UNNotificationServiceExtension」 so can't view push notifications image.
The following my code
import UserNotifications
class NotificationService: UNNotificationServiceExtension {
let imageKey = AnyHashable("gcm.notification.image_url")
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 imageUrl = request.content.userInfo[imageKey] as? String {
let session = URLSession(configuration: URLSessionConfiguration.default)
let task = session.dataTask(with: URL(string: imageUrl)!, completionHandler: { [weak self] (data, response, error) in
if let data = data {
do {
let writePath = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("push.png")
try data.write(to: writePath)
guard let wself = self else {
return
}
if let bestAttemptContent = wself.bestAttemptContent {
let attachment = try UNNotificationAttachment(identifier: "nnsnodnb_demo", url: writePath, options: nil)
bestAttemptContent.attachments = [attachment]
contentHandler(bestAttemptContent)
}
} catch let error as NSError {
print(error.localizedDescription)
guard let wself = self else {
return
}
if let bestAttemptContent = wself.bestAttemptContent {
contentHandler(bestAttemptContent)
}
}
} else if let error = error {
print(error.localizedDescription)
}
})
task.resume()
} else {
if let bestAttemptContent = bestAttemptContent {
contentHandler(bestAttemptContent)
}
}
}
override func serviceExtensionTimeWillExpire() {
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
contentHandler(bestAttemptContent)
}
}
}
REF : https://www.pluralsight.com/guides/swift/creating-ios-rich-push-notifications
I have done like this hope it helps for GIF images you can change extension to .png.
Make sure that in APNS payload attachment-url is coming for image.
Check for App transport security key in case of image url start from http://...
Your image should be under ~200px. For me it not works beyond(HIT and TRIAL).
Code:
import UserNotifications
import SDWebImage
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)
func failEarly() {
contentHandler(request.content)
}
guard let content = (request.content.mutableCopy() as? UNMutableNotificationContent) else {
return failEarly()
}
guard let attachmentURL = content.userInfo["attachment-url"] as? String else {
return failEarly()
}
SDWebImageDownloader.shared().downloadImage(with: URL(string: attachmentURL)!,
options: SDWebImageDownloaderOptions.continueInBackground,
progress: nil) { (image, data, error, flag) in
guard let attachment = UNNotificationAttachment.create(imageFileIdentifier: "image.gif",
data: data! as NSData,
options: nil) else { return failEarly() }
content.attachments = [attachment]
contentHandler(content.copy() as! UNNotificationContent)
if let bestAttemptContent = self.bestAttemptContent {
bestAttemptContent.title = "\(bestAttemptContent.title) [modified]"
contentHandler(bestAttemptContent)
}
}
}
override func serviceExtensionTimeWillExpire() {
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
contentHandler(bestAttemptContent)
}
}
}
extension UNNotificationAttachment {
static func create(imageFileIdentifier: String, data: NSData, options: [NSObject : AnyObject]?) -> UNNotificationAttachment? {
let fileManager = FileManager.default
let tmpSubFolderName = ProcessInfo.processInfo.globallyUniqueString
let tmpSubFolderURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(tmpSubFolderName, isDirectory: true)
do {
try fileManager.createDirectory(at: tmpSubFolderURL!, withIntermediateDirectories: true, attributes: nil)
let fileURL = tmpSubFolderURL?.appendingPathComponent(imageFileIdentifier)
try data.write(to: fileURL!, options: [])
let imageAttachment = try UNNotificationAttachment(identifier: imageFileIdentifier, url: fileURL!, options: options)
return imageAttachment
} catch let error {
print("error \(error)")
}
return nil
}
}
I'm trying to display rich notifications on my app with an expanded image like this. I have used Notification Service extension to implement this in the app.
But I'm only getting a thumbnail when I receive a notification, which looks something like this. The expanded image appears when when I long press on a 3D-touch capable phone, otherwise it just displays a thumbnail on phones which doesn't have 3D-touch.
I wasn't able to find any documentation or any questions on SO which explains how to do this if it is possible. I would like to know if it is possible to do this on iOS, if not is there any possible workaround to accomplish this? Here is my NotificationSerivce extension. Any help is much appreciated! Thanks!
class NotificationService: UNNotificationServiceExtension {
let fileManager = FileManager()
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 {
// Modify the notification content here...
guard let content = (request.content.mutableCopy() as? UNMutableNotificationContent) else {
return self.contentHandler = contentHandler
}
bestAttemptContent.title = "\(bestAttemptContent.title) [modified]"
guard let attachmentURL = content.userInfo["attachment-url"] as? String else {
return self.contentHandler = contentHandler
}
guard let fileName = attachmentURL.components(separatedBy: "/").last else {
return self.contentHandler = contentHandler
}
guard let imageData = try? Data(contentsOf: URL(string: attachmentURL)!) else {
return self.contentHandler = contentHandler
}
if let thumbnailAttachment = UNNotificationAttachment.create(imageFileIdentifier: fileName, data: imageData, options: nil) {
bestAttemptContent.attachments = [thumbnailAttachment]
}
contentHandler(bestAttemptContent)
}
}
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)
}
}
}
extension UNNotificationAttachment {
/// Save the image to disk
static func create(imageFileIdentifier: String, data: Data, options: [AnyHashable: Any]?) -> UNNotificationAttachment? {
let fileManager = FileManager.default
let tmpSubFolderName = ProcessInfo.processInfo.globallyUniqueString
let tmpSubFolderURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(tmpSubFolderName, isDirectory: true)
do {
try fileManager.createDirectory(at: tmpSubFolderURL!, withIntermediateDirectories: true, attributes: nil)
let fileURL = tmpSubFolderURL?.appendingPathComponent(imageFileIdentifier)
try data.write(to: fileURL!, options: [])
let imageAttachment = try UNNotificationAttachment(identifier: imageFileIdentifier, url: fileURL!, options: options)
return imageAttachment
} catch let error {
print("error \(error)")
}
return nil
}
}
It is a very old question and I guess you'd have discovered by now, what you are trying to achieve is not technically feasible.
The notifications appear in their collapsed form and only if the user 3d presses (or long-presses in case of devices without 3d-touch) on the notification will they be shown in the expanded form.
I make rich notification to show image in notification but whenever i send simple message then i get notification. Last 2 day i am trying to show image in notification but it was not done. Please help me to do this.
Thank you in advance
This is my code.
In Notification Service Extension
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: #escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
// Get the custom data from the notification payload
if let data = request.content.userInfo["data"] as? [String: String] {
// Grab the attachment
if let urlString = data["attachment-url"], let fileUrl = URL(string: urlString) {
// Download the attachment
URLSession.shared.downloadTask(with: fileUrl) { (location, response, error) in
if let location = location {
// Move temporary file to remove .tmp extension
let tmpDirectory = NSTemporaryDirectory()
let tmpFile = "file://".appending(tmpDirectory).appending(fileUrl.lastPathComponent)
let tmpUrl = URL(string: tmpFile)!
try! FileManager.default.moveItem(at: location, to: tmpUrl)
// Add the attachment to the notification content
if let attachment = try? UNNotificationAttachment(identifier: "", url: tmpUrl) {
self.bestAttemptContent?.attachments = [attachment]
}
}
// Serve the notification content
self.contentHandler!(self.bestAttemptContent!)
}.resume()
}
}
}
This is my notification structure
{
"aps" : {
"alert" : {
"title" : "Push Remote Rich Notifications",
"subtitle" : "iOS 10 - New API",
"body" : "Media Image Rich notification"
},
"mutable-content" : 1,
"category" : "imageIdentifier"
},
"data" : {
"attachment-url": "https://raw.githubusercontent.com/Sweefties/iOS10-NewAPI-UserNotifications-Example/master/source/iOS10-NewAPI-UserNotifications-Example.jpg"
}
}
Use this following code
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)
guard let bestAttemptContent = bestAttemptContent else {
return
}
guard let attachmentUrlString = request.content.userInfo["pic_url"] as? String else {
return
}
guard let url = URL(string: attachmentUrlString) else {
return
}
URLSession.shared.downloadTask(with: url, completionHandler: { (optLocation: URL?, optResponse: URLResponse?, error: Error?) -> Void in
if error != nil {
print("Download file error: \(String(describing: error))")
return
}
guard let location = optLocation else {
return
}
guard let response = optResponse else {
return
}
do {
let lastPathComponent = response.url?.lastPathComponent ?? ""
var attachmentID = UUID.init().uuidString + lastPathComponent
if response.suggestedFilename != nil {
attachmentID = UUID.init().uuidString + response.suggestedFilename!
}
let tempDict = NSTemporaryDirectory()
let tempFilePath = tempDict + attachmentID
try FileManager.default.moveItem(atPath: location.path, toPath: tempFilePath)
let attachment = try UNNotificationAttachment.init(identifier: attachmentID, url: URL.init(fileURLWithPath: tempFilePath))
bestAttemptContent.attachments.append(attachment)
}
catch {
print("Download file error: \(String(describing: error))")
}
OperationQueue.main.addOperation({() -> Void in
self.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)
}
}
also the following code in info.plist
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
add this key in NSExtension NSDictionary in Info.plist
<key>NSExtensionAttributes</key>
<dict/>
I am trying to implement the rich remote notifications in ios 10. I have implemented this code. The control after receiving the notification is going here, but I don't know how to download the image and display in the notifications. Thanks in advance.
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 {
// Modify the notification content here...
//print("title for image = \(bestAttemptContent.title)")
bestAttemptContent.title = "\(bestAttemptContent.title) [modified]"
contentHandler(bestAttemptContent)
}
}
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)
}
}
}
you will get an attachment in your notificationData like this
"attachment-url": "https://yourimage.png"
and this is how you can use it
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
// Get the custom data from the notification payload
if let notificationData = request.content.userInfo["data"] as? [String: String] {
// Grab the attachment
if let urlString = notificationData["attachment-url"], let fileUrl = URL(string: urlString) {
// Download the attachment
URLSession.shared.downloadTask(with: fileUrl) { (location, response, error) in
if let location = location {
// Move temporary file to remove .tmp extension
let tmpDirectory = NSTemporaryDirectory()
let tmpFile = "file://".appending(tmpDirectory).appending(fileUrl.lastPathComponent)
let tmpUrl = URL(string: tmpFile)!
try! FileManager.default.moveItem(at: location, to: tmpUrl)
// Add the attachment to the notification content
if let attachment = try? UNNotificationAttachment(identifier: "", url: tmpUrl) {
self.bestAttemptContent?.attachments = [attachment]
}
}
// Serve the notification content
self.contentHandler!(self.bestAttemptContent!)
}.resume()
}
}
Referenced from here
Finally its working. The issue here was that I had to add
NSAppTransportSecurity
tag in plist of the extension. After adding this tag, it started displaying the images. Hope it helps someone.