Push notifications

Our iOS SDK supports push notifications over APN or FCM. Use this page to get started with either service.

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 receive push notifications before you identify people!

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/#install" 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 push fill:#B5FFEF,stroke:#007069 style register-token fill:#B5FFEF,stroke:#007069

Before you begin

This page explains how to receive push notifications using our SDK. However, before you can send push notifications to your audience, you need to enable Customer.io to send push notifications through your preferred service: Apple Push Notification Service (APNs) or Firebase Cloud Messaging (FCM).

This process lets you receive basic push notifications in your app—a title and a message body. To send a more complicated push, complete the process on this page, and then see our rich push documentation.

 Check out our sample apps!

Our repository provides real-world examples that you can help you see how the SDK works and implement it in your own apps.

Install the push package

Before you can receive push notifications, you need to install the push package supporting the push service you use—APNs or FCM.

  1. Use Swift Package Manager to install your push package. See our Getting Started page for installation instructions.

    • APNs: install MessagingPushAPN
    • FCM: install MessagingPushFCM
  2. Enable the Push Notifications capability in XCode.

Register for push notifications

The instructions in this section set you up to receive simple push notifications with a body and title. After you follow these instructions, you’ll need to do a bit more work to support rich push notifications.

To receive a push notification, you have to register for a device token and identify the user, which associates the device token with a person in Customer.io. You can do these things in any order—it doesn’t matter whether you register for a token before or after you identify the current user.

  1. After you initialize the SDK, register for remote push to receive a device token from your push service. Your code changes slightly depending on the push service you use.

    import CioTracking
    import CioMessagingPushAPN
    
    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")
    
         // It's a good practice to register for remote push when the app starts.
         // This asserts that the Customer.io SDK always has a valid device token.
         UIApplication.shared.registerForRemoteNotifications()
    
         return true
     }
     
     func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
         MessagingPush.shared.application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
     }
     
     func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
         MessagingPush.shared.application(application, didFailToRegisterForRemoteNotificationsWithError: error)
     }
    }
    
    import CioMessagingPushFCM
    import CioTracking
    import Firebase
    import FirebaseMessaging
    
    class AppDelegate: NSObject, UIApplicationDelegate {
     func application(
         _ application: UIApplication,
         didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
     ) -> Bool {        
         FirebaseApp.configure() 
         
         CustomerIO.initialize(siteId: "YOUR SITE ID", apiKey: "YOUR API KEY")
    
         // Set FCM messaging delegate
         Messaging.messaging().delegate = self
    
         // You should register for remote push when the app starts.
         // This asserts that the Customer.io SDK always has a valid device token.
         UIApplication.shared.registerForRemoteNotifications()
    
         return true
     }
    }
    
    extension AppDelegate: MessagingDelegate {
     func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
         MessagingPush.shared.messaging(messaging, didReceiveRegistrationToken: fcmToken)
     }
    }
    

  2. Setup push notification click handling. When a user taps a push notification, you’ll have the Customer.io SDK process the event so you can track push metrics tracking and handle deep links.

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

import CioTracking
import Foundation
import UIKit
   
class AppDelegate: NSObject, UIApplicationDelegate {
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
    ) -> Bool {        
        // Other code such as CustomerIO.initialize()
   
        UNUserNotificationCenter.current().delegate = self
      
        return true
    }
}

extension AppDelegate: UNUserNotificationCenterDelegate {
    // Function that gets called when push notification clicked 
    func userNotificationCenter(
        _ center: UNUserNotificationCenter,
        didReceive response: UNNotificationResponse,
        withCompletionHandler completionHandler: @escaping () -> Void
    ) {
        // Send Customer.io SDK click event to process. This enables features such as 
        // push metrics and deep links. 
        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])
    }
}
  1. Identify the person if you have not already. Even after you add a device token, you can’t use it until you associate it with a person.
    CustomerIO.shared.identify(identifier: "989388339", body: ["first_name": firstName]) 
    

When you identify a person, you should see their device token in your workspace. You can send a simple push notification to test your implementation.

Note that when you identify a different person or stop identifying a person, the SDK automatically removes the device token from any previously identified profile. This ensures that a device token is only registered to the currently identified profile in the SDK and prevents you from sending duplicate messages messaging the wrong person.

Set up rich push

This process prepares your app to receive push notifications with images and links.

  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
    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.         
            // The last parameter optionally allows you to configure the SDK. 
            CustomerIO.initialize(siteId: "YOUR SITE ID", apiKey: "YOUR API KEY", region: Region.US) { config in
                config.autoTrackPushEvents = true 
            }
    
            // For simple apps that only use Customer.io for sending rich push messages,
            // This 1 line of code is all that you need!
            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
    31
    32
    33
    
    // 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.         
         // The last parameter optionally allows you to configure the SDK. 
         CustomerIO.initialize(siteId: "YOUR SITE ID", apiKey: "YOUR API KEY", region: Region.US) { config in
             config.autoTrackPushEvents = true 
         }
    
         // 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
    32
    33
    34
    
    // 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.         
         // The last parameter optionally allows you to configure the SDK. 
         CustomerIO.initialize(siteId: "YOUR SITE ID", apiKey: "YOUR API KEY", region: Region.US) { config in
             config.autoTrackPushEvents = true 
         }
    
         // 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.

Deep links let you open a specific page in your app instead of opening the device’s web browser. Want to open a screen in your app or perform an action when a push notification or in-app button is clicked? Deep links work great for this!

  1. Setup deep linking in your app. There are two ways to do this; you can do both if you choose.

    • Universal Links: universal links let you open your mobile app instead of a web browser when someone interacts with a URL on your website. For example: https://your-social-media-app.com/profile?username=dana—notice how this URL is the same format as a webpage.
    • App scheme: app scheme deep links are quick and easy to setup. Example of an app scheme deep link: your-social-media-app://profile?username=dana. Notice how this URL is not a URL that could show a webpage if your mobile app is not installed.

    Universal Links provide a fallback for links if your audience doesn’t have your app installed, but they take longer to set up than App Scheme deep links. App Scheme links are easier to set up but won’t work if your audience doesn’t have your app installed.

To enable Universal Links in your iOS app, follow the instructions on the Apple documentation website. Be sure to complete all of the steps required including making modifications to your website to host a new file and making modifications to your mobile app’s code to handle the deep link.

 Install iOS SDK 2.0.6 or later

Earlier versions of the SDK had an issue causing both your app and browser to open when users tapped a universal link.

Depending on how you set up your mobile app (SwiftUI, UIKit, watchOS, etc), you may need to handle deep links in multiple functions in your code. One of the functions you are required to have in your app is:

class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
        guard let universalLinkUrl = userActivity.webpageURL else {
            return false 
        }

        // Parse `universalLinkUrl` object to perform the action you want in your app. 

        // return true from this function if your app handled the deep link. 
        // return false from this function if your app did not handle the deep link and you want sdk to open the URL in a browser.
    }
}

 Check universal links using your Notes app

Try creating a note with a universal link and tapping the link to double-check that the link opens in your app and not in a browser window. This is an easy way to make sure that you’ve set up universal links correctly.

  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

Capture push metrics

Customer.io supports device-side metrics that help you determine the efficacy of your push notifications: delivered when a push notification is received by the app and opened when a push notification is clicked.

If you already configured rich push notifications, the SDK will automatically track opened and delivered events for push notifications originating from Customer.io.

Otherwise, you can:

Capture push metrics with UserNotifications

If you’re using a version of iOS that supports UserNotifications, you can track metrics using our UNNotificationContent helper.

func userNotificationCenter(
    _ center: UNUserNotificationCenter,
    didReceive response: UNNotificationResponse,
    withCompletionHandler completionHandler: @escaping () -> Void
) {
    // This 1 line of code might be all that you need!
    MessagingPush.shared.userNotificationCenter(center, didReceive: response, withCompletionHandler: completionHandler)

    // If you use `UserNotifications` for more then Customer.io push notifications, you can check 
    // if the SDK handled the push for you or not. 
    let handled = MessagingPush.shared.userNotificationCenter(center, didReceive: response, withCompletionHandler: completionHandler)
    if !handled {
        // Notification was *not* displayed by Customer.io. Handle the notification yourself. 
    }
}

Extract delivery ID and token

If you’re not using a version of iOS that supports UserNotifications, you should send the push metric manually by extracting the CIO-Delivery-ID and CIO-Delivery-Token parameters directly to track push metrics.

guard let deliveryID: String = notificationContent.userInfo["CIO-Delivery-ID"] as? String, 
      let deviceToken: String = notificationContent.userInfo["CIO-Delivery-Token"] as? String else {
    // Not a push notification delivered by Customer.io
    return
}

MessagingPush.shared.trackMetric(deliveryID: deliveryID, event: .delivered, deviceToken: deviceToken)

Disable automatic push tracking

Automatic push metric recording is enabled by default when you install the SDK. You can disable this behavior in the SDK’s configuration.

CustomerIO.initialize(...) { config in 
  config.autoTrackPushEvents = false
}

Test your push implementation

After you set up push notifications, you should send some test messages. You can send messages through the Customer.io push composer. If your app is set up to use keys outside the standard ones that our SDK supports, you’ll want to send a custom payload.

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

The payloads below represent what your app can expect to receive from Customer.io. If you use a custom payload, you’ll need to use the format(s) below to make sure that the SDK receives your message properly.

When testing, 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.
    {
        "aps": {
            // basic iOS message and options go here
            "mutable-content": 1,
            "sound": "default",
            "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
        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
        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.
    {
      "message": {
        "apns": {
          "payload": {
            "aps": {
              // basic iOS message and options go here
              "mutable-content": 1,
              "sound": "default",
              "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
              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
              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.

Sound in push notifications

When you send a push notification to iOS devices that uses our SDK, you can opt to send the Default system sound or no sound at all. If your audience’s phone is set to vibrate, or they’ve disabled sound permissions for your app, the Default setting will cause the device to vibrate rather than playing a sound.

In most cases, you should use the Default sound setting to make sure your audience hears (or feels) your message. But, before you send sound, you should understand:

  1. Your app needs permission from your users to play sounds. This is done by your app, not our SDKs. Here’s an example from our iOS sample app showing how to request sound permissions.
  2. iOS users can go into System Settings and disable sound permissions for your app. Enabling the Default setting doesn’t guarantee that your audience hears a sound when your message is delivered!

 We don’t support custom sounds yet

If you want to send a custom sound, you’ll need to handle it on your own, outside the SDK and use a custom payload when you set up your push notifications.

Copied to clipboard!
  Contents
Current release
 2.8.2
Is this page helpful?