Search code examples
iosswiftuilocalnotification

Attach image to notification given image URL


I want to attach an image to my local notifications given an image URL. This is the extension to create an attachment:

import UserNotifications

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)
            guard let imageData = UIImagePNGRepresentation(image) else {
                return nil
            }
            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
    }
}

When I schedule a new notification, I use it like this:

// url of the image such as http://www.unsplash.com/image.png
let data = try? Data(contentsOf: url) 
guard let myImage = UIImage(data: data!) else { return }

if let attachment = UNNotificationAttachment.create(identifier: key, image: myImage, options: nil) {
    content.attachments = [attachment]
}

enter image description here

Creating a notification like this freezes the application for a few seconds because the app downloads the image synchronously. I have also tried to use DispatchQueue but it didn't change anything. What did I do wrong?


Solution

  • Your code downloads an image, parses it to create a UIImage, converts the image back to a block of PNG data, then writes this data to a temporary file.

    You can skip the step where you create the UIImage and convert it back to a file.

    Try using URLSession and URLDataTask:

    let fileURL = ...
    let task = URLSession.shared.dataTask(with: url) { (data, _, _) in
        do {
            try imageData.write(to: fileURL)
            let attachment = UNNotificationAttachment.create(identifier: key, image: myImage, options: nil)
            // call closure to call back with attachment and/or error
        }
        catch let ex {
            // call closure with error
        }
    }
    task.resume()
    

    I've left out some error handling and other details, but this should give you the general idea of what's required to do it asynchronously. URLSessions use GCD to perform asynchronous networking.