December 2017: This tutorial is out of date. We recommend using our new Push Notifications SDKs. You can get started by going to Beta products dashboard. You can also read the docs. We will be updating our push notifications tutorials and creating new ones in the New Year.

It’s that time of year again. A new iPhone, a controversial hardware innovation (no headphone jack? Oh no they didn’t!), and a new version of iOS. With iOS 10, there are new features to make Notifications more useful, such as rich media and interactive actions. You can start sending push notifications with all of the new iOS 10 Notification features using our Mobile Push Notifications API.

What’s new in iOS 10 Notifications?

Images, GIFs, Audio and Video

You can now send rich media in a push notification, including images, GIFs, audio, and video clips. Including media in your push notification can increase the open rate and drive more engagement to your app.

video-attachment

Continue reading for a detailed tutorial…

Expanded detail view with 3D Touch

Push notifications can include an expanded view upon 3D Touch that displays applets, such as a map or a calendar. The user no longer has to open an app to see important contextual information such as a car arriving on a map or the date of a calendar invite.

Notification Actions

Once a push notification is expanded, users can take immediate actions. With a messaging app, you could reply directly without opening the app. With an events app, you could accept a calendar invite within the notification. Notification Actions empower the developer to deliver a push notifications experience tailor made for their app.

content-extension

How do I send rich notifications in iOS 10 using the Push Notifications API?

Before you continue reading, you should already have our Push Notifications API up and running and you should be receiving notifications on your iOS client. If you haven’t already, you should go ahead and do that here.

Adding media attachments to Push Notifications in iOS 10 is a huge update for developers, and it will transform the way that iOS users engage with applications.

OK, so first things first, you need to update your version of the PusherSwift CocoaPod to version 3.0.0.

pod 'PusherSwift'

Go ahead and open your existing Xcode project. Assuming that the project isn’t currently written in Swift 3, which it probably isn’t, Xcode 8 will ask you to convert your code to Swift 3 code. You should do this, as we experienced issues trying to use legacy languages (as Apple call anything older than Swift 3!).

Better still, if you are just looking to play around with rich notifications in iOS 10, we’d recommend creating a new Xcode project from scratch, setting up our Push Notifications API in just a few minutes, and then continuing with this tutorial.

First, head to your application target. Choose Capabilities and ensure that ‘Push Notifications’ is enabled and that ‘Remote notifications’ is selected under Background Modes.

enable-push-notifications-capabilities

remote-notifications-background-modes

Swift 3 introduces some changes to the methods for registering your applications for remote notifications. The syntax is also a little different in places.

Head to AppDelegate.swift (where you should already be registering for remote notifications if you’re using an existing project).

Import UserNotifications and replace the code you are already using in didFinishLaunchingWithOptions with this code:

let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound]) { (granted, error) in
// actions based on whether notifications were authorized or not
}
application.registerForRemoteNotifications()

You’ll also need to replace your code in didRegisterForRemoteNotificationsWithDeviceToken with:

pusher.nativePusher().register(deviceToken: deviceToken)
pusher.nativePusher().subscribe(interestName: "donuts")

Now, this is where all the really brand new shiny stuff begins! Choose File > New > Target and select Notification Service Extension.

add-nse-target

A Notification Service Extension essentially intercepts certain remote notifications (we’ll find out how later) and allows you to handle the contents of the payload, including downloading and displaying remote media attachments, before displaying the notification to the user.

Once you’ve added the new target, you’ll have a new file called NotificationService.swift.

Open this up, and you’ll see a couple of delegate methods – one for receiving a notification request and one for handling the expiration of the service extension. We’ll be focussing on the first one, didReceiveRequestWithContentHandler.

The code in this method should look like this:

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

It may seem like a lot is happening here, but it’s straightforward really. Initially, we are exploring the notification’s payload for the dictionary named ‘data’ that contains the key ‘attachment-url’. If this key exists, we download the media at the URL specified in the key’s value. See, easy!

The URLSession downloads the media to temporary storage and appends a .tmp file extension, which we need to remove so that the application can infer the file type and display it. So, to do this we need to move the file to the application’s local File Manager and that’s it – our media is ready to be attached to the notification content, and served to the user.

This is all the code needed in your Xcode project to display rich notifications in your application.

Now, you just need to make a few adjustments to the server that triggers Push Notifications to your application using our API.

Earlier, we mentioned that only certain notifications were intercepted by the Service Extension. So, how does iOS know which notifications to intercept? Well, you just add a mutable-content tag to your payload and set it to 1.

The final thing you need to do is to add the media attachment URL to the payload in its own dictionary that we’ve named ‘data’, but you can name it anything. Simply add a key-value pair to the dictionary, and as long as the key you’ve used in your Service Extension is the same as the key here, you’re done!

The following example is using a Node JS server.

pusher.notify(['donuts'], {
  apns: {
    aps: { 
      alert: { 
        title: "Pusher's Native Push Notifications API", 
        subtitle: "Bringing you iOS 10 support!", 
        body: "Now add more content to your Push Notifications!"
        }, 
        "mutable-content": 1,
        category: "pusher"
      },
    data: {
      "attachment-url": "https://pusher.com/static_logos/320x320.png"
    } 
  },
  webhook_url: "https://example.com/endpoint",
  webhook_level: "INFO"
});

That’s it! You can now send images, videos, audio, and GIFs in your iOS 10 Push Notifications! Simply change the URL to point to another media file, and see the results for yourself.

And of course; you could remove the media attachment and just send text, but where’s the fun in that?!

Text only
text-only

Image attachment
image-attachment

GIF attachment
gif-attachment

Video attachment
video-attachment

How do I add a custom UI to my notifications in iOS 10?

Now that you’ve sent a bunch of GIFs of animals doing cute, crazy, and ridiculous things in your push notifications, you can also start to design your own UI within your push notifications.

We do this using Notification Content Extensions. These are essentially used to draw your own UI to display when your application receives certain push notifications.

Using the same Xcode project you used to add the Notification Service Extension, you now need to add a Notification Content Extension. It’s the same process, File > New > Target.

add-nce-target

The Content Extension uses a NotificationViewController, which is a subclass of UIViewController, so it’ll feel very familiar!

When you created the Content Extension, Xcode created the NotificationViewController.swift file, a MainInterface.storyboard file and an Info.plist file.

First, go to Info.plist. This part is really important, as it determines which notifications will be given this Content Extension.

Expand the NSExtension and NSExtensionAttributes dictionaries.

The UNNotificationExtensionCategory key value is the same as the value you may have noticed we set in our Node JS server example. This tells iOS that the push notification you’re triggering is of a certain category, and should be handled in a certain way. In this case, with a Content Extension. Make sure this matches the category set in the push notification payload, otherwise you’ll just be getting goat GIFs in your push notification rather than a custom UI (is that a bad thing…?)!

The key UNNotificationExtensionInitialContentSizeRatio relates to the size of the view that is displayed when your push notification is expanded. By default, this is 1. We’re going to change it to 0.7, but you should experiment with this to get the best results for your UI. You’ll notice the view loading in the wrong size, and then either shrinking or expanding to the correct size if this value isn’t correct. Play around with it!

You can add a 3rd key here: UNNotificationExtensionDefaultContentHidden. This key is a boolean, and either hides or displays the push notification’s text, i.e. it’s title and body. We’ll set this to YES for now and hide it.

nce-info-plist

Next, head to the Storyboard file. This is where you’ll create the UI that your push notifications will display, just like any other UIViewController you’ve ever created!

Apple have been really nice and created a UILabel for you already, which you can either keep or replace.

For this tutorial, we’ve made a really simple view that displays a local UIImage (not taken from the push notification payload), and displays the title and subtitle of the push notification in 2 UILabels.

nce-ui

Finally, head to the NotificationViewController.swift file. You’ll see the ever-present viewDidLoad method, as well as a didReceiveNotification method. You’ll also see the IBOutlet for the label that was already created for you, and some code that takes the body of the push notification and displays it in the label. So all we need to do here is add another IBOutlet for our 2nd label, be sure to hook it up in the Storyboard file, and reuse the existing code to display what we want in the label. Here’s what ours looks like:

    @IBOutlet var titleLabel: UILabel?
    @IBOutlet var subtitleLabel: UILabel?

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any required interface initialization here.
    }

    func didReceive(_ notification: UNNotification) {
        titleLabel?.text = notification.request.content.title
        subtitleLabel?.text = notification.request.content.subtitle
    }

And that’s it, you’re good to go!

Trigger a push notification using our API, making sure the category matches, and you’ll see your custom UI when you expand the push notification. Cool, huh?

Check out our example:

content-extension

How do I add custom actions to my push notifications in iOS 10?

The eagle-eyed amongst you would have noticed the custom actions in our example above. These are incredibly easy to set up.

In your AppDelegate.swift file, add a new function:

func setActions() {
        let pusherWeb = UNNotificationAction(
            identifier: "pusher-web",
            title: "Take me to Pusher.com ?"
        )
        let dismiss = UNNotificationAction(
            identifier: "dismiss",
            title: "Dismiss",
            options: [.destructive]
        )
        let category = UNNotificationCategory(
            identifier: "pusher",
            actions: [pusherWeb, dismiss],
            intentIdentifiers: []
        )

        UNUserNotificationCenter.current().setNotificationCategories([category])
    }

Then simply call setActions() from the didFinishLaunchingWithOptions method.

This adds these actions to make them available for any future push notification that is received matching the UNNotificationCategory with identifier ‘pusher’.

You can find the repository for this tutorial here on GitHub.

Still haven’t set up our Push Notifications API? Check out the links below to get started!

An Overview to Our Push Notification API Documentation for iOS
Push Notifications API for iOS
Push Notifications API for Android