Rich notifications with images or GIFs with iOS 10

Rich notifications with iOS 10 brings us some nice new features to add to our apps. It’s possible to create rich views within the detail view of a notification, which you can create with so called Notification Content Extensions. To simply display an image or GIF you can use the Notification Service Extension which I will discuss in this blogpost.

Rich notifications containing images or GIFs

Notification in iOS 10 can be modified before they’re shown to the user. You can send a push notification from a server with title “New messages arrived”, which you can edit before it’s shown to the user. An example of the edit could be “New messages arrived (3 new messages)”.

Changing the title is an option, but you can also add so called notification attachments. This enables you to link to an local assets or to download a resource first, save it to disk and add it as an attachment. An good example is to show an GIF image of the rainclouds in Buienradar:

To enable this, you’ll have to create a Notification Service Extension:

Notification Service Extension

Notification Service Extension

To make sure the callback is executed, the notification JSON send from the server needs to include a new key called mutable-content. To test my implementation, I downloaded a mac tool from GitHub called Pusher. This is the JSON I posted to create the above notification:

{
    "aps": {
        "alert": {
            "body": "Het gaat over 15 minuten regenen in Alkmaar",
            "title": "Buienalert"
        },
        "mutable-content": 1
    },
    "attachment-url": "https://api.buienradar.nl/Image/1.0/RadarMapNL"
}

The attachment-url is a self chosen key, which could be anything. The important part is to use the same key in the final code.
Because of the mutable-content key, the notification is not shown directly to the user. Instead, we’ve got 30 seconds to execute some code, download resources and edit the notification.

Steps we need to do for showing the GIF:

  1. Download the GIF
  2. Save the GIF to disk
  3. Add the path of the GIF to a UNNotificationAttachment
  4. Send back the edited notification

The code for this is quite simple:

import UserNotifications
import UIKit

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

And at the end we’ve got a working notification with a GIF image. For normal images you just need to create a PNG representation of the UIImage and save that to disk.

 

Antoine van der Lee

Dutch iOS developer at Triple. Developed apps like Buienradar, Videoland and Pop the Dots.

 
Follow on Feedly