Rich push notifications

With rich push, you can do more than just send a simple notification; you can send an image, open a deep link when someone taps your message, and more!

This page is part of a setup flow for the SDK. Before you continue, make sure you've implemented previous features—i.e. you can't identify people before you initialize the SDK!

graph LR getting-started(Install SDK) -->B(Initialize SDK) B --> identify(identify people) identify -.-> track-events(Send events) identify -.-> register-token(Register
Device Token) register-token -.-> push(Receive push) register-token -.-> rich-push(Receive Rich Push) track-events --> test-support(Write tests) push --> test-support rich-push --> test-support identify -.-> in-app(Receive in-app) in-app --> test-support click getting-started href "/docs/sdk/ios/getting-started" click B href "/docs/sdk/ios/getting-started/#initialize-the-sdk" click identify href "/docs/sdk/ios/identify" click track-events href "/docs/sdk/ios/track-events/" click register-token href "/docs/sdk/ios/push" click push href "/docs/sdk/ios/push" click rich-push href "/docs/sdk/ios/rich-push" click in-app href "/docs/sdk/ios/in-app" click test-support href "/docs/sdk/ios/test-support" style rich-push fill:#B5FFEF,stroke:#007069

Before you begin

You should set up APN or FCM push notifications and make sure that you can send yourself a test push notification before you start implementing rich push.

While rich push generally entails a number of features, our SDK only supports deep links and images right now. If you want to include action buttons or other rich push features, you need to add your own custom code.

When writing your own custom code, we recommend that you use our SDK as it is much easier to extend than writing your own code from scratch. Read below for tips on how to extend the functionality of the SDK with features we do not yet support.

 Check out our sample app!

Our Remote Habits app, provides a real-world example and can act as a template to help you implement the SDK.

Set up rich push

  1. Add a Service App Extension to your project in Xcode.

  2. You should now see a new file added to your Xcode project. The file is probably named NotificationService and looks similar to this.

    import UserNotifications
    
    class NotificationService: UNNotificationServiceExtension {
    
     override func didReceive(
         _ request: UNNotificationRequest,
         withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
     ) {
    
     }
    
     override func serviceExtensionTimeWillExpire() {
    
     }
    }
  3. Modify this file by selecting the push package you want to import and calling the appropriate Customer.io functions. Your code changes if:

    • Customer.io is your only push/rich push provider
    • Customer.io is not your only provider
    • You want to take advantage of push features outside the Customer.io, like action buttons; in this case, you’ll need to set your own completion handler.
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
    // Keep the import for your push provider—FCM or APN, and
    // remove the other import statement
    import CioMessagingPushFCM
    import CioMessagingPushAPN
    import UserNotifications
    import CioTracking
       
    class NotificationService: UNNotificationServiceExtension {
       
        override func didReceive(
            _ request: UNNotificationRequest,
            withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
        ) {
            // Because of the behavior of Notification Service Extensions in iOS, you need to 
            // initialize the Customer.io SDK in your host app and in your Notification Service. 
            CustomerIO.initialize(siteId: "YOUR SITE ID", apiKey: "YOUR API KEY", region: Region.EU)
         
            MessagingPush.shared.didReceive(request, withContentHandler: contentHandler)   
        }
       
        override func serviceExtensionTimeWillExpire() {
            MessagingPush.shared.serviceExtensionTimeWillExpire()
        }
    }
       
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    
    // Keep the import for your push provider—FCM or APN, and
    // remove the other import statement
    import CioMessagingPushFCM
    import CioMessagingPushAPN
    import UserNotifications
    import CioTracking
    
    class NotificationService: UNNotificationServiceExtension {
    
     override func didReceive(
         _ request: UNNotificationRequest,
         withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
     ) {
         // Because of the behavior of Notification Service Extensions in iOS, you need to 
         // initialize the Customer.io SDK in your host app and in your Notification Service. 
         CustomerIO.initialize(siteId: "YOUR SITE ID", apiKey: "YOUR API KEY", region: Region.EU)
    
         // If you use a service other than Customer.io to send rich push,
         // you can check if the SDK handled the rich push for you. If it did not, you
         // know that the push was *not* sent by Customer.io and you can try another way.
         let handled = MessagingPush.shared.didReceive(request, withContentHandler: contentHandler)
         if !handled {
             // Rich push was *not* sent by Customer.io. Handle the rich push in another way.
         }
     }
    
     override func serviceExtensionTimeWillExpire() {
         MessagingPush.shared.serviceExtensionTimeWillExpire()
     }
    }
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    
    // Keep the import for your push provider—FCM or APN, and
    // remove the other import statement
    import CioMessagingPushFCM
    import CioMessagingPushAPN
    import UserNotifications
    import CioTracking
    
    class NotificationService: UNNotificationServiceExtension {
    
     override func didReceive(
         _ request: UNNotificationRequest,
         withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
     ) {
         // Because of the behavior of Notification Service Extensions in iOS, you need to 
         // initialize the Customer.io SDK in your host app and in your Notification Service. 
         CustomerIO.initialize(siteId: "YOUR SITE ID", apiKey: "YOUR API KEY", region: Region.EU)
    
         // If you need to add features, like showing action buttons in your push, 
         // you can set your own completion handler.
         MessagingPush.shared.didReceive(request) { notificationContent in
             if let mutableContent = notificationContent.mutableCopy() as? UNMutableNotificationContent {
                 // Modify the push notification like adding action buttons!
             }
             contentHandler(notificationContent)
         }
     }
    
     override func serviceExtensionTimeWillExpire() {
         MessagingPush.shared.serviceExtensionTimeWillExpire()
     }
    }

Your app can now display rich push notifications in your app, including images, etc. However, if you want to enable deep links, you should continue to the Deep links section below.

After you set up rich push notifications you can enable deep links in rich push notifications.

  1. Modify your AppDelegate with the following information. This enables your app to launch a deep link URL when someone taps a notification.

    import CioMessagingPushAPN
    import CioTracking
    import Foundation
    import UIKit
    
    class AppDelegate: NSObject, UIApplicationDelegate {
     func application(
         _ application: UIApplication,
         didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
     ) -> Bool {
         CustomerIO.initialize(siteId: "YOUR SITE ID", apiKey: "YOUR API KEY", region: Region.US)
    
         // Must call this function in order for `UNUserNotificationCenterDelegate` functions
         // to be called.
         UNUserNotificationCenter.current().delegate = self
    
         // It's good practice to always register for remote push when the app starts.
         // This asserts that the Customer.io SDK always has a valid APN device token to use.
         UIApplication.shared.registerForRemoteNotifications()
    
         return true
     }
    }
    
    extension AppDelegate: UNUserNotificationCenterDelegate {
     func userNotificationCenter(
         _ center: UNUserNotificationCenter,
         didReceive response: UNNotificationResponse,
         withCompletionHandler completionHandler: @escaping () -> Void
     ) {
         let handled = MessagingPush.shared.userNotificationCenter(center, didReceive: response,
                                                                  withCompletionHandler: completionHandler)
    
         // If the Customer.io SDK does not handle the push, it's up to you to handle it and call the
         // completion handler. If the SDK did handle it, it called the completion handler for you.
         if !handled {
             completionHandler()
         }
     }
    
     // OPTIONAL: If you want your push UI to show even with the app in the foreground, override this function and call
     // the completion handler.
     @available(iOS 10.0, *)
     func userNotificationCenter(
         _ center: UNUserNotificationCenter,
         willPresent notification: UNNotification,
         withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
     ) {
         completionHandler([.list, .banner, .badge, .sound])
     }
    }
  2. Setup deep linking in your app. There are two ways to do this, and you can do both if you choose.

    • Universal Links: universal links are great if you want to open your mobile app instead of a web browser when your goes to a page on your website. However, universal links take more time to setup. Follow this guide to setup Universal Links in your app.
    • App scheme: app scheme deep links are quick and easy to setup. However, they do not work if the mobile app is not installed, which is where universal links provide an advantage. To enable app scheme deep links. See the section below to enable app scheme deep links.

App scheme deep links only work if your audience has your mobile app installed. If you want to support cases where your audience might not already have your app, you can set up universal links.

  1. Open your Xcode project and go to your project’s settings. Select your app Target, click the Info tab, and then click URL Types > to create a new URL Type.

    visual of the typed instructions in the sentence above to create a new URL type
    visual of the typed instructions in the sentence above to create a new URL type

  2. Enter a unique value for your app for URL Schemes.

    visual of the typed instructions in the sentence above to enter a unique value for URL scheme
    visual of the typed instructions in the sentence above to enter a unique value for URL scheme

Test Rich Push

After you set up rich push, you should test your implementation. Use the payloads below to send a push in the Customer.io web app with a Custom Payload.

In both of the test payloads below, you should:

  • Set the link to the deep link URL that you want to open when your tester taps your notification.
  • Set the image to the URL of an image you want to show in your notification. It’s important that the image URL starts with https:// and not http:// or the image might not show up.
{
    "CIO": {
        "push": {
            "link": "remote-habits://deep?message=hello&message2=world",
            "image": "https://thumbs.dreamstime.com/b/bee-flower-27533578.jpg"
        }
    },
    "aps": {
        "mutable-content": 1,
        "alert": {
            "title": "Title of your push goes here!",
            "body": "Body of your push goes here!"
        }
    }
}
{
    "message": {
        "apns": {
            "payload": {
                "CIO": {
                    "push": {
                        "link": "remote-habits://deep?message=hello&message2=world",
                        "image": "https://thumbs.dreamstime.com/b/bee-flower-27533578.jpg"
                    }
                },
                "aps": {
                    "mutable-content": 1,
                    "alert": {
                        "title": "Title of your push goes here!",
                        "body": "Body of your push goes here!"
                    }
                }
            }
        }
    }
}

Rich push payloads

To send a rich push in Customer.io, you need to use our Custom Payload editor, which takes a JSON structure. In the editor, you’ll select the type of device you want to send your message to: you can have separate payloads for Android and iOS. In our case, you’ll click iOS.

The custom payload editor for a push notification
The custom payload editor for a push notification

The top level of the payload changes slightly depending on your push provider, APNS or FCM. Otherwise, your JSON is split into two major objects:

  • an aps object, which contains the standard aspects of a push—the alert.title and alert.body of your message—and Apple’s push options.
  • a CIO object containing the rich aspects of your message that the SDK will interpret. At present, it contains link and image strings.
{
    "aps": {
        // basic iOS message and options go here
        "mutable-content": 1,
        "alert": {
            "title": "string", //(optional) The title of the notification.
            "body": "string" //(optional) The message you want to send.
        }
    },
    "CIO": {
        "push": {
            "link": "string", //generally a deep link, i.e. my-app:://...
            "image": "string" //HTTPS URL of your image, including file extension
        }
    }
}
      • image string
        The URL of an HTTPS image that you want to use for your message.
      • link string
        A deep link (to a page in your app), or a link to a web page.
    • alert One of:
      string
      A simple alert message.
    • badge integer
      The number you want to display on your app’s icon. Set to 0 to remove the current badge, if any.
    • category string
      The notification’s type. This string must correspond to the identifier of one of the UNNotificationCategory objects you register at launch time.
    • content-available integer
      The background notification flag. Use 1 without an alert to perform a silent update. 0 indicates a normal push notification.
    • interruption-level string
      Indicates the importance and delivery timing of a notification.

      Accepted values:passive,active,time-sensitive,critical

    • mutable-content integer
      The notification service app extension flag. If the value is 1, your notification is passed to your notification service app extension before delivery. Use your extension to modify the notification’s content.
    • relevance-score number
      A number between 0 and 1. The highest score is considered the “most relevant” and is featured in the notification summary.
    • sound One of:
      string
      The name of a sound file in your app’s main bundle or in the Library/Sounds folder of your app’s container directory. Use “default” to play the system sound. For critical alerts, you’ll pass an object instead.
    • target-content-id string
      The identifier of the window brought forward.
    • thread-id string
      An identifier to group related notifications.
  • Custom key-value pairs* any type
    Additional properties that you've set up your app to interpret outside of the Customer.io SDK.
{
  "message": {
    "apns": {
      "payload": {
        "aps": {
          // basic iOS message and options go here
          "mutable_content": 1,
          "alert": {
            "title": "string", //(optional) The title of the notification.
            "body": "string" //(optional) The message you want to send.
           }
        },
        "CIO": {
          "push": {
            "link": "string", //generally a deep link, i.e. my-app://... or https://yourwebsite.com/...
            "image": "string" //HTTPS URL of your image, including file extension
          }
        }
      },
      "headers": {
        // (optional) headers to send to the Apple Push Notification Service.
        "apns-priority": 10
      }
    } 
  }
}
            • body string
              The body of your push notification.
            • image string
              The URL of an HTTPS image that you want to use for your message.
            • link string
              A deep link (to a page in your app), or a link to a web page.
            • title string
              The title of your push notification.
          • alert One of:
            string
            A simple alert message.
          • badge integer
            The number you want to display on your app’s icon. Set to 0 to remove the current badge, if any.
          • category string
            The notification’s type. This string must correspond to the identifier of one of the UNNotificationCategory objects you register at launch time.
          • content-available integer
            The background notification flag. Use 1 without an alert to perform a silent update. 0 indicates a normal push notification.
          • interruption-level string
            Indicates the importance and delivery timing of a notification.

            Accepted values:passive,active,time-sensitive,critical

          • mutable-content integer
            The notification service app extension flag. If the value is 1, your notification is passed to your notification service app extension before delivery. Use your extension to modify the notification’s content.
          • relevance-score number
            A number between 0 and 1. The highest score is considered the “most relevant” and is featured in the notification summary.
          • sound One of:
            string
            The name of a sound file in your app’s main bundle or in the Library/Sounds folder of your app’s container directory. Use “default” to play the system sound. For critical alerts, you’ll pass an object instead.
          • target-content-id string
            The identifier of the window brought forward.
          • thread-id string
            An identifier to group related notifications.
        • Custom key-value pairs* any type
          Additional properties that you've set up your app to interpret outside of the Customer.io SDK.
Copied to clipboard!
Latest release
 1.2.6
Is this page helpful?