Article Image
read

Siri, notify me please.

The new UserNotifications framework is a huge improvement over the existing API, but with every new fresh bit comes a steep learning curve. Fortunately, Apple provided us with great documentation this year, which makes getting started is really easy.

Disclaimer:

  • For the new actions and custom content to be displayed, you must use a 3D Touch enabled device or simulator. Apple is still working on how the interaction should look like on non-3D Touch devices, but this should be fixed in Beta 2;
  • iOS 10 simulator shipped with Xcode 8 Beta 1 has the old lock screen. While you can see the new notifications on Springboard, you should be using the real device for 100% accurate results.

Getting Started

Unlike in the old API, which used UIApplication to schedule a local notification and UIApplicationDelegate to notify us of users' actions, in the new one Apple decided to employ a separate class that will handle all our notifications needs, whether it is local or remote.

let center = UNUserNotificationCenter.current()

UNUserNotificationCenter should be used as a singleton and you should not create instances of this class directly. It has a fairly simple interface that is mostly self-explanatory. The getNotificationSettings method is worth noting - it accepts a callback which will be called with an instance of UNNotificationSettings containing the user's current notification permissions granted to your app.

First of all, we need to request permissions from the user to show them the notifications, so let's start with that by calling requestAuthorization method on the notification center.

center.requestAuthorization([.sound, .alert]) {
    (granted, error) in
    // We can register for remote notifications here too!
}

We are going to use the add(_ request: UNNotificationRequest, withCompletionHandler completionHandler: ((NSError?) -> Swift.Void)? = nil) method to present the notification to the user, but before that we need to create some extra objects.

We start by creating an instance of UNMutableNotificationContent:

let content = UNMutableNotificationContent()
content.title = "Will it rain?"
content.body = "It never rains in (Southern) California!"
content.sound = UNNotificationSound.default()

As you can see from the add method signature, this is not enough to get our notification displayed. The add method takes a UNNotificationRequest as an argument to UNNotificationContent. To build a request, we will also need a trigger - so let's start with that.

let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)

Great! Now let's combine that with the content to build the request.

let request = UNNotificationRequest(identifier: "local_notification", content: content, trigger: trigger)

Let's add this to the center:

center.add(request)

5, 4, 3, 2, 1, 🚀!

Voila 🎉

Are you attached to this idea?

Obviously, Apple didn't go to all that trouble only because they wanted us to use a different (and obviously better) API. The UserNotifications framework actually gives us access to many new, long-awaited features. One of them is the possibility to add an attachment to notifications - an image, video or audio file (AND YES, GIFs ARE SUPPORTED 😍)

To do so, we need to set an extra field on our notification:

let content = UNMutableNotificationContent()
content.title = "Will it rain?"
content.body = "It never rains in (Southern) California!"
content.sound = UNNotificationSound.default()

let url = URL(string: "https://67.media.tumblr.com/ab04c028a5244377a0ab96e73915e584/tumblr_nfn3ztLjxk1tq4of6o1_400.gif")
let attachment = try! UNNotificationAttachment(identifier: "image", url: url!, options: nil)

content.attachments = [attachment] // You can pass more than one attachment!

I'm not going to post the screenshot for this one - it's an animated gif, go test it out yourself!

Usage with remote notifications:

We have to use Notification Service to add attachments to remote notifications. What's that? Let me just quote Apple's documentation here, as they put it quite clearly:

Notification Service app extension lets you process the payload of a remote (sometimes called push) notification before it is delivered to the user.

First of all, we will need to make some changes to our APNS payload:

{
    "aps": {
        "alert": "Remotely pushing.",
        "mutable-content": 1
    },
    "attachement-url": "https://67.media.tumblr.com/ab04c028a5244377a0ab96e73915e584/tumblr_nfn3ztLjxk1tq4of6o1_400.gif"
}

So let's try this then:

So many extensions 😮

After creating the extension target, we end up with a subclass of a UNNotificationServiceExtension that implements didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler:(UNNotificationContent) -> Void) method. iOS will call this method every time it receives a push notification that has an alert key and mutable-content key set to 1

Please note that your extension has a limited time to call the contentHandler callback (no longer than 30 seconds).

To attach the attachment, we could do something like this:

override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler:(UNNotificationContent) -> Void) {
    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()
    }

    let url = URL(string: attachmentURL)
    guard let attachment = try? UNNotificationAttachment(identifier: "image", url: url!, options: nil) else {
        return failEarly()
    }

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

Obviously, to save space in our APNS payload we could just provide it with an identifier that allows us to rebuild the url.

More to come!

This is just the tip of the new API that we have seen in the iOS10 and I will try to cover them all in the upcoming days. Next time, we will create a custom content view for our notifications and attach some custom actions to it, stay tuned! 🤓