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 app!

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

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. 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.

After you set up rich push notifications you can enable deep links in rich push notifications. Deep links let you open a specific page in your app when your audience touches a push notification.

  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) { config in }
    
         // Must call this function in order for `UNUserNotificationCenterDelegate` functions
         // to be called.
         UNUserNotificationCenter.current().delegate = self
       
         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; 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. See the section below for help setting up universal links.
    • 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. see the section below for help setting up app scheme deep links.

    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, Scenes, 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 prefer the deep link to open the deep link 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.

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

Capture push metrics

Customer.io supports three device-side metrics that help you determine the efficacy of your push notifications: delivered, opened, and converted.

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. 
    }
}

 UserNotifications has some inconsistencies when tracking delivered events.

If delivered events are important to you, we recommend that you follow the setup instructions for rich push notifications, even if you do not plan on sending rich push notifications as rich push tracks delivered events more reliably.

Extract delivery ID and token

If you’re not using a version if 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,
            "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!
  Contents
Current release
 2.1.2
Is this page helpful?