Build scalable realtime features


Programmatic push notifications



Read the docs to learn how to use our products


Explore our tutorials to build apps with Pusher products


Reach out to our support team for help and advice

Sign in
Sign up

Rich notifications with Vapor - Part 2: iOS Application

  • Christopher Batin

March 18th, 2019
You will need Xcode 10+, Vapor 3+, Cocoapods, and some understanding of Vapor.


In this part of the tutorial we will be building an iOS application that handles receiving a push notification with rich content. The application will only show a notification in the notification center. We won’t be handling the notification when we have the application open.

Sometimes sending notifications with only text just isn’t quite enough. This is where rich notifications come in. We can add images and videos to our notification to give the user a richer experience directly from there notification center.


  • Make sure you have completed part one of this tutorial.
  • A basic understanding of Vapor - please complete my “Getting started with Vapor” Part One and Part Two.
  • Xcode 10+
  • MacOS
  • Vapor 3.0 - Install instructions here.
  • An iOS device for testing notifications.
  • An understanding of iOS development and Xcode environment.
  • Cocoapods - Install instructions here.
  • A REST client such as Postman and a basic understanding of how to use it.

Handling basic notifications

Adding our dependency

Start by creating a new single view application and name it whatever you wish. Open the terminal and go to the working directory of the newly created project and run the following command.

    $ pod init

Open the newly created Podfile and add the following pod:

    pod 'PushNotifications'

In the terminal run:

    $ pod install

Make sure you close your Xcode project and reopen the newly created Xcode Workspace (YOUR-APP.xcworkspace) before continuing.

Set up notifications

Within your project capabilities make sure you have switched on the Push Notifications capability. Also turn on the Background Modes capability and tick the box for Remote Notifications.

Open your AppDelegate.swift file and replace its contents with the following. Remembering to replace the instance ID with your own.

    // AppDelegate.swift
    import UIKit
    import PushNotifications

    class AppDelegate: UIResponder, UIApplicationDelegate {
        var window: UIWindow?
        let pushNotifications = PushNotifications.shared

        func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
            self.pushNotifications.start(instanceId: "YOUR_INSTANCE_ID")
            try? self.pushNotifications.subscribe(interest: "general")

            return true

        func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {

        func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
            self.pushNotifications.handleNotification(userInfo: userInfo)


You can now run the application on your iOS device and accept to receive notifications. The SDK will then manage registering our interest in “general”. This is the same interest that we have defined in our server that we are pushing to.

If we were to send a notification now we would see a notification on the lock, however it would not show any rich content.

Handling rich notifications

Create a new target

Rich notifications are handled by a separate target within our application. To add our new target go to File → New → Target. Select the Notification Service Extension.

Note: If you wish to customize how a notification will look on the lock screen. You can use the Notification Content Extension to do this. You may find this tutorial useful if you wish to learn more about this.

On the next screen give your target a sensible name and make sure it is set to the correct project and embed in the correct application. Once you have confirmed this click Finish. You might be asked to activate your target, select Activate.

Handle download

Your new target will create a new NotificationService.swift file. This file will handle the notification you have received and attempt to show it.

    // NotificationService.swift
    import UserNotifications

    class NotificationService: UNNotificationServiceExtension {

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

        override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
            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: Any] {
                // Grab the attachment
                if let urlString = notificationData["attachment-url"] as? String,
                    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

        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 {

It may feel like a lot is going on here but in fact it is all quite simple. We simply check to see if we have a URL within our payload. We then use this URL to download the content to a temporary file and attempt to display this.

Note: Rich notifications have a timeout called by the system, if the original content has not been downloaded then the original payload without the rich content will be displayed. It is therefore considered best practice to make sure the notification makes sense without the rich content.

Testing your integration

Now we can begin testing our integration. If you have closed your Vapor project reopen it in the usual way in Xcode and hit the run button. You should see a message in the console saying it has started and is listening on http://localhost:8080. To begin our first make sure the iPhone is on the lock screen. Our test requires us to use Postman (or another REST client) to pass data through. Open postman or your equivalent REST client and set it up so that it looks like this.

This will show a notification like below. You can force touch to see the full image like the image on the right.

You can change the URL in the post request to contain anyone of the types discussed in the first part of this tutorial. For example you may which to show a video, gif or audio clip.


In this part of the tutorial we have learnt how to handle both text notifications and rich notifications in our iOS application. We also learnt how we can test the notifications using postman and change the data that we are sending.

The source code for this project can be found here.

Clone the project repository
  • Beams
  • iOS
  • Swift
  • Beams


  • Channels
  • Beams

© 2020 Pusher Ltd. All rights reserved.

Pusher Limited is a company registered in England and Wales (No. 07489873) whose registered office is at 160 Old Street, London, EC1V 9BW.