Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UNNotificationAttachment with UIImage or Remote URL

In my Notification Service Extension I am downloading an image from a URL to show as UNNotificationAttachment in a notification.

So I have this image as UIImage and don't see the need to write it in my app directory / group container on disc just to set up the notification.

Is there a good way create an UNNotificationAttachment with a UIImage ? (should be appliable to local and remote notifications)

like image 987
MarkHim Avatar asked Aug 23 '16 13:08

MarkHim


2 Answers

  1. create directory in tmp folder
  2. write the NSData representation of the UIImage into the newly created directory
  3. create the UNNotificationAttachment with url to the file in tmp folder
  4. clean up tmp folder

I wrote an extension on UINotificationAttachment

extension UNNotificationAttachment {

    static func create(identifier: String, image: UIImage, options: [NSObject : AnyObject]?) -> UNNotificationAttachment? {
        let fileManager = FileManager.default
        let tmpSubFolderName = ProcessInfo.processInfo.globallyUniqueString
        let tmpSubFolderURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(tmpSubFolderName, isDirectory: true)
        do {
            try fileManager.createDirectory(at: tmpSubFolderURL, withIntermediateDirectories: true, attributes: nil)
            let imageFileIdentifier = identifier+".png"
            let fileURL = tmpSubFolderURL.appendingPathComponent(imageFileIdentifier)
            let imageData = UIImage.pngData(image)
            try imageData()?.write(to: fileURL)
            let imageAttachment = try UNNotificationAttachment.init(identifier: imageFileIdentifier, url: fileURL, options: options)
            return imageAttachment
        } catch {
            print("error " + error.localizedDescription)
        }
        return nil
    }
}

So to create UNUserNotificationRequest with UNUserNotificationAttachment from a UIImage you can simply do sth like this

let identifier = ProcessInfo.processInfo.globallyUniqueString
let content = UNMutableNotificationContent()
content.title = "Hello"
content.body = "World"
if let attachment = UNNotificationAttachment.create(identifier: identifier, image: myImage, options: nil) {
    // where myImage is any UIImage
    content.attachments = [attachment] 
}
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 120.0, repeats: false)
let request = UNNotificationRequest.init(identifier: identifier, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request) { (error) in
    // handle error
}

This should work since UNNotificationAttachment will copy the image file to an own location.

like image 197
MarkHim Avatar answered Oct 29 '22 15:10

MarkHim


I've created a blogpost on this subject, focused on GIF images. But it should be easy to rewrite my code for simply images.

You need to create a Notification Service Extension: Notification Service Extension

And include this code:

final class NotificationService: UNNotificationServiceExtension {

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

    override internal func didReceiveNotificationRequest(request: UNNotificationRequest, withContentHandler contentHandler: (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()
        }

        guard let imageData = NSData(contentsOfURL:NSURL(string: attachmentURL)!) else { return failEarly() }
        guard let attachment = UNNotificationAttachment.create("image.gif", data: imageData, options: nil) else { return failEarly() }

        content.attachments = [attachment]
        contentHandler(content.copy() as! UNNotificationContent)
    }

    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: NSData, options: [NSObject : AnyObject]?) -> UNNotificationAttachment? {
        let fileManager = NSFileManager.defaultManager()
        let tmpSubFolderName = NSProcessInfo.processInfo().globallyUniqueString
        let tmpSubFolderURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).URLByAppendingPathComponent(tmpSubFolderName, isDirectory: true)

        do {
            try fileManager.createDirectoryAtURL(tmpSubFolderURL!, withIntermediateDirectories: true, attributes: nil)
            let fileURL = tmpSubFolderURL?.URLByAppendingPathComponent(imageFileIdentifier)
            try data.writeToURL(fileURL!, options: [])
            let imageAttachment = try UNNotificationAttachment.init(identifier: imageFileIdentifier, URL: fileURL!, options: options)
            return imageAttachment
        } catch let error {
            print("error \(error)")
        }

        return nil
    }
}

For more information you can check my blogpost here: http://www.avanderlee.com/ios-10/rich-notifications-ios-10/

like image 40
Antoine Avatar answered Oct 29 '22 16:10

Antoine