Push notifications
UpdatedThere's a new version available!
These pages cover version 2 of our SDK, but a newer version is available. In general, we suggest that you update to the latest version to take advantage of new features and fixes.
- Are you new to our SDKs? Check out the latest docs.
- Otherwise, learn about updating to the latest version
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!
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!
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.
Use Swift Package Manager to install your push package. See our Getting Started page for installation instructions.
- APNs: install
MessagingPushAPN
- FCM: install
MessagingPushFCM
- APNs: install
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.
The SDK automatically handles push registration and push clicks for you. However, you’ll still need to identify users before you can send them push notifications.
Using version 2.10 or earlier?
As of version 2.11, we automatically register device tokens and handle push clicks. Follow our upgrade guide to remove unnecessary code and increase compatibility with 3rd party SDKs in your app.
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") // Initialize Customer.io push features after you initialize the SDK: MessagingPushAPN.initialize { config in // Automatically register push device tokens to the Customer.io SDK config.autoFetchDeviceToken = true // When your app is in the foreground and a push is delivered, show the push config.showPushAppInForeground = true } return true } }
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") // Initialize Customer.io push features after you initialize the SDK: MessagingPushFCM.initialize { config in // Automatically register push device tokens to the Customer.io SDK config.autoFetchDeviceToken = true // When your app is in the foreground and a push is delivered, show the push config.showPushAppInForeground = true } return true } }
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.
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() { } }
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
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!
Setup deep linking in your app. There are two ways to do this; you can do both if you want.
- 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.
Set up Universal Links
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.
If your links are opening Safari instead of your app, check this Apple document to troubleshoot.
Setup App Scheme Deep Links
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.
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:
- Record push metrics with
UserNotifications
. - Extract delivery ID and Delivery Token parameters directly.
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 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 withhttps://
and nothttp://
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 stringThe URL of an HTTPS image that you want to use for your message.
- link stringA deep link (to a page in your app), or a link to a web page.
-
-
- alertstringA simple alert message.
- badge integerThe number you want to display on your app’s icon. Set to 0 to remove the current badge, if any.
- category stringThe notification’s type. This string must correspond to the identifier of one of the
UNNotificationCategory
objects you register at launch time. - content-available integerThe background notification flag. Use
1
without analert
to perform a silent update.0
indicates a normal push notification. - interruption-level stringIndicates the importance and delivery timing of a notification.
Accepted values:
passive
,active
,time-sensitive
,critical
- mutable-content integerIf you use the Customer.io SDK, you must set this value to
1
to support images and “delivered” metrics from your push notifications. When 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 numberA number between 0 and 1. The highest score is considered the “most relevant” and is featured in the notification summary.
- soundstringThe 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 stringThe identifier of the window brought forward.
- thread-id stringAn 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 stringThe body of your push notification.
- image stringThe URL of an HTTPS image that you want to use for your message.
- link stringA deep link (to a page in your app), or a link to a web page.
- title stringThe title of your push notification.
-
-
- alertstringA simple alert message.
- badge integerThe number you want to display on your app’s icon. Set to 0 to remove the current badge, if any.
- category stringThe notification’s type. This string must correspond to the identifier of one of the
UNNotificationCategory
objects you register at launch time. - content-available integerThe background notification flag. Use
1
without analert
to perform a silent update.0
indicates a normal push notification. - interruption-level stringIndicates the importance and delivery timing of a notification.
Accepted values:
passive
,active
,time-sensitive
,critical
- mutable-content integerIf you use the Customer.io SDK, you must set this value to
1
to support images and “delivered” metrics from your push notifications. When 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 numberA number between 0 and 1. The highest score is considered the “most relevant” and is featured in the notification summary.
- soundstringThe 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 stringThe identifier of the window brought forward.
- thread-id stringAn identifier to group related notifications.
- Custom key-value pairs* any typeAdditional 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:
- 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.
- 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.