# React Native SDK Documentation
Generated on: 1/30/2026
This documentation bundle contains the complete React Native SDK documentation for use with LLMs and offline reference.
---
## Quick start guide
**Source:** /integrations/sdk/react-native/quick-start-guide
# Quick Start Guide
 Our MCP server can help you get started
Our MCP server includes SDK-installation tools that can help you get integrated quickly with Customer.io and troubleshoot any issues you might have. See [Set up an MCP server](/ai/mcp-server/) to get started.
## Setup process overview[](#setup-process-overview)
React Native lets you build native mobile apps with JavaScript. Our React Native SDK helps you integrate Customer.io to `identify` people, `track` their activity, and send both push notifications and in-app messages.
1. [Install the SDK.](#install)
2. [Identify and Track](#identify-and-track)
3. [Push Notifications](#push-notifications)
4. [In-App](#in-app)
## 1\. Install the SDK[](#install)
You need to add a new *React Native* connectionRepresents an integration between Customer.io and another service or app under *[Data & Integrations > Integrations](https://fly.customer.io/workspaces/last/integrations/all/overview)*. A connection in Customer.io provides you with API keys and settings for your integration. in Customer.io to get your CDP API key. See [Get your CDP API key](../getting-started/auth/#get-your-cdp-api-key) for details.
1. Make sure you [set up your React Native environment](https://reactnative.dev/docs/environment-setup) first. **You must use React Native 0.79 or later**.
2. Open your terminal and go to your project folder.
3. Install the `customerio-reactnative` package using NPM or Yarn:
```bash
npm install customerio-reactnative
# or
yarn add customerio-reactnative
```
4. Set up your project to support iOS and/or Android deployments:
iOS
#### iOS[](#iOS)
For iOS, install CocoaPods dependencies:
```bash
pod install --repo-update --project-directory=ios
```
Make sure your minimum iOS deployment target is set to `13.0` in both your `Podfile` and Xcode project settings.
Android
#### Android[](#Android)
1. For Android, include the Google Services plugin by adding the following to your project-level `android/build.gradle` file:
```groovy
buildscript {
repositories {
google() // Google's Maven repository
}
dependencies {
classpath 'com.google.gms:google-services:' // Google Services plugin
}
}
allprojects {
repositories {
google() // Google's Maven repository
}
}
```
2. Add the plugin to your app-level `android/app/build.gradle`:
```groovy
apply plugin: 'com.google.gms.google-services' // Google Services plugin
```
3. Download `google-services.json` from your Firebase project and place it in `android/app/google-services.json`.
5. Add your *CDP API key* and *site ID* to your configuration.
* **CDP API Key**: Youâll find this key in your [*React Native* connection](https://fly.customer.io/workspaces/last/pipelines/sources).
* **Site ID**: Youâll find this value in your workspace under **[Settings > Workspace Settings > API and webhook credentials](https://fly.customer.io/workspaces/last/settings/api_credentials)**.
6. Initialize the SDK in your app. Add the following code to your main component or App.js file:
```typescript
import React, { useEffect } from 'react';
import {
CioConfig,
CioRegion,
CioLogLevel,
CustomerIO,
PushClickBehaviorAndroid
} from 'customerio-reactnative';
useEffect(() => {
const initializeCustomerIO = async () => {
const config: CioConfig = {
cdpApiKey: 'YOUR_CDP_API_KEY', // Required
region: CioRegion.US, // Replace with CioRegion.EU if your Customer.ioaccount is in the EU
logLevel: CioLogLevel.Debug,
trackApplicationLifecycleEvents: true,
inApp: {
siteId: 'YOUR_SITE_ID', // Required for in-app messaging
}
};
await CustomerIO.initialize(config);
};
initializeCustomerIO();
}, []);
```
7. Run your application to ensure everything is set up correctly:
* **iOS**: `npx react-native run-ios`
* **Android**: `npx react-native run-android`
## 2\. Identify and Track[](#identify-and-track)
1. Identify a user in your app using the `CustomerIO.identify` method. You *must* identify a user before you can send push notifications and personalized in-app messages.
```typescript
import { CustomerIO } from "customerio-reactnative";
const identifyUserExample = async () => {
await CustomerIO.identify({
userId: '[email protected]',
traits: {
firstName: 'John',
lastName: 'Doe',
email: '[email protected]',
subscriptionStatus: 'active',
},
});
console.log('User identified successfully');
};
```
2. Track a custom event using the `CustomerIO.track` method. Events help you trigger personalized campaigns and track user activity.
```typescript
import { CustomerIO } from "customerio-reactnative";
const trackCustomEventExample = async () => {
await CustomerIO.track('purchased_item', {
product: 'Premium Subscription',
price: 99.99,
currency: 'USD'
});
console.log('Custom event tracked successfully');
};
```
3. Track screen views to trigger in-app messages associated with specific screens.
```typescript
import { CustomerIO } from "customerio-reactnative";
const trackScreenViewExample = async () => {
await CustomerIO.screen('ProductDetails');
console.log('Screen view tracked successfully');
};
```
## 3\. Push Notifications[](#push-notifications)
1. Set up your push notification credentials in Customer.io:
* **iOS**: [Upload your Apple Push Notification certificate](https://fly.customer.io/workspaces/last/settings/actions/push/ios) (.p8 file).
* **Android**: [Upload your Firebase Cloud Messaging server key](https://fly.customer.io/workspaces/last/settings/actions/push/android) (.json format).
2. Request push notification permissions from the user:
```typescript
import { CustomerIO, CioPushPermissionStatus } from "customerio-reactnative";
const requestPushPermissions = async () => {
const permissionStatus = await CustomerIO.pushMessaging.showPromptForPushNotifications({
ios: {
sound: true,
badge: true
}
});
switch (permissionStatus) {
case CioPushPermissionStatus.Granted:
console.log('Push notifications enabled');
break;
case CioPushPermissionStatus.Denied:
case CioPushPermissionStatus.NotDetermined:
console.log('Push notifications denied');
break;
}
};
```
3. For iOS: to ensure that metrics are tracked, configure Background Modes. In Xcode, enable âRemote notificationsâ under Capabilities > Background Modes.
4. For Android: add notification icon resources:
* Place a notification icon file named `ic_notification.png` in your drawable folders.
* Make sure your appâs `AndroidManifest.xml` has the proper FCM permissions.
## 4\. In-App[](#in-app)
1. To enable in-app messaging, all you need to do is add the site ID. Remember, youâll find your site ID under **[Data & Integrations > Integrations > Customer.io API: Track](https://fly.customer.io/workspaces/last/settings/api_credentials)** in the *Connections* tab.
2. Ensure that the SDK is initialized with the site ID in your app. You can call the `initialize` method from your components or services:
```typescript
import { CioConfig, CustomerIO } from "customerio-reactnative";
import { useEffect } from "react";
useEffect(() => {
const initializeCustomerIO = async () => {
const config: CioConfig = {
cdpApiKey: 'YOUR_CDP_API_KEY',
inApp: {
siteId: 'YOUR_SITE_ID',
}
};
await CustomerIO.initialize(config);
};
initializeCustomerIO();
}, []);
```
 Check out our code samples!
Youâll find a complete, working sample app in our [React Native SDKâs example directory](https://github.com/customerio/customerio-reactnative/tree/main/example). If you get stuck, you can refer to the sample app to see how everything fits together.
---
## Getting started > Auth
**Source:** /integrations/sdk/react-native/getting-started/auth
# Authentication
To use the SDK, youâll need two kinds of API keys: A *CDP API Key* to send data to Customer.io and a *Site ID*, telling the SDK which workspace your messages come from.
These keys come from different places in Customer.io!
1. **CDP API Key**: Youâll get this key when you [set up your mobile app as a data-in integration](#set-up-a-new-source) in Customer.io.
2. **Site ID**: This key tells the SDK which workspace your in-app messages come from. Youâll use it to support `inApp` messages. If youâre upgrading from a previous version of the Customer.io SDK, it also serves as the `migrationSiteId`.
## Get your CDP API Key[](#get-your-cdp-api-key)
Youâll use your write key to initialize the SDK and send data to Customer.io; youâll get this key from your React Native entry under **[Data & Integrations > Integrations](https://fly.customer.io/workspaces/last/integrations/all/overview)**. If you donât see your app on this page, youâll need to [add up a new integration](#set-up-a-new-source).
1. Go to *Data & Integrations* > Integrations\*.
2. Go to the **Connections** tab and find your React Native connection. If you donât have a React Native connection, youâll need to [set one up](#set-up-a-new-source).
[](#0dfe697d9a6725af8493a5709d6adc95-lightbox)
3. Go to **Settings** and find your **API Key**. Copy this key into the `CioConfig.CdpApiKey` config option.
[](#53593c4c304208d983b57468465e6b6a-lightbox)
```javascript
import {
CioRegion, CustomerIO, CioConfig
} from 'customerio-reactnative';
const App = () => {
useEffect(() => {
const config: CioConfig = {
cdpApiKey: 'CDP API Key', // Mandatory
migrationSiteId: 'siteId', // Required if migrating from an earlier version
region: CioRegion.US, // Replace with CioRegion.EU if your Customer.io account is in the EU.
inApp: {
siteId: 'site_id',
}
};
CustomerIO.initialize(config)
}, [])
}
```
## Add a new integration[](#set-up-a-new-source)
If you donât already have a write key, youâll need to set up a new connectionRepresents an integration between Customer.io and another service or app under *[Data & Integrations > Integrations](https://fly.customer.io/workspaces/last/integrations/all/overview)*. A connection in Customer.io provides you with API keys and settings for your integration.. The connection represents your app and the stream of data that youâll send to Customer.io.
1. Go to [*Data & Integrations* > *Integrations*](https://fly.customer.io/workspaces/last/journeys/integrations/all/overview) and click **Add Integration**.
2. Select **React Native**.
3. Enter a *Name* for your integration, like âMy React Native Appâ.
4. Weâll present you with a code sample containing a `cdpApiKey` that youâll use to initialize the SDK. Copy this key and keep it handy.
5. Click **Complete Setup** to finish setting up your integration.
[](#40e573b250197f1c640f816429d6bf9b-lightbox)
Remember, you can also [connect your React Native app to services outside of Customer.io](/integrations/data-out/add-destination/)âlike your analytics provider, data warehouse, or CRM.
## Get your Site ID[](#get-your-site-id)
Youâll use your site ID with the `inApp` option to support in-app messaging.
And if youâre upgrading from a previous version of the SDK, youâll also use your site ID as your `migrationSiteId`. This key is used to send remaining tasks to Customer.io when your audience updates your app.
1. Go to Settings > **Workspace Settings** in the upper-right corner of the Customer.io app and go to [**API and Webhook Credentials**](https://fly.customer.io/workspaces/last/settings/api_credentials).
2. Copy the **Site ID** for the set of credentials that you want to send your in-app messages from. If you donât have a set of credentials, click **Create Tracking API Key**.
[](#64d2b27827ffddb00dc77b851a7a6854-lightbox)
3. Youâll use this key to initialize the `inApp` package.
```javascript
import {
CioLogLevel, CioRegion, CustomerIO, CioConfig
} from 'customerio-reactnative';
const App = () => {
useEffect(() => {
const config: CioConfig = {
cdpApiKey: 'CDP API Key', // Mandatory
migrationSiteId: 'siteId', // Required if migrating from an earlier version
region: CioRegion.US, // Replace with CioRegion.EU if your Customer.io account is in the EU.
logLevel: CioLogLevel.Debug,
trackApplicationLifecycleEvents: true,
inApp: {
siteId: 'site_id',
},
push: {
android: {
pushClickBehavior: PushClickBehaviorAndroid.ActivityPreventRestart
}
}
};
CustomerIO.initialize(config)
}, [])
}
```
## Securing your credentials[](#securing-your-credentials)
To simplify things, code samples in our documentation sometimes show API keys directly in your code. But you donât have to hard-code your keys in your app. You can use environment variables, management tools that handle secrets, or other methods to keep your keys secure if youâre concerned about security.
To be clear, the keys that youâll use to initialize the SDK donât provide read access to data in Customer.io; they only write data to Customer.io. A bad actor who found your credentials canât use your keys to read data from our servers.
---
## Getting started > How it works
**Source:** /integrations/sdk/react-native/getting-started/how-it-works
# How it works
Before you can take advantage of our SDK, you need to install the module(s) you want to use, initialize the SDK, and understand the order of operations.
Our SDKs provide a ready-made integration to identify people who use mobile devices and send them notifications. Before you start using the SDK, you should understand a bit about how the SDK works with Customer.io.
Before a person logs into your app, any activity they perform is associated with an anonymous person in Customer.io. In this state, you can track their activity, but you canât send them messages through Customer.io.
When someone logs into your app, youâll send an `identify` call to Customer.io. This makes the person eligible to receive messages and reconciles their anonymous activity to their identified profile in Customer.io.
You send messages to a person through the Customer.io campaign builder, broadcasts, etc. These messages are not stored on the device side. If you want to send an event-triggered campaign to a mobile device, the mobile device user must be identified and have a connection such that it can send an event back to Customer.io and receive a message payload.
## Your app is a data source and Customer.io is a destination[](#your-app-is-a-data-source-and-customerio-is-a-destination)
Our SDK is a data inAn integration that feeds data *into* Customer.io. integration. It routes data from your app to both Customer.io *and* any other outbound services where you might use your mobile data. This makes it easy to use your app as a part of your larger data stack without using extra packages or code.
When you set up your app, youâll integrate our SDK. But youâll also determine where you want to route your data toâyour Customer.io workspace *and* destinations outside of Customer.io.
[](#0dfe697d9a6725af8493a5709d6adc95-lightbox)
## Minimum requirements[](#minimum-requirements)
To support the Customer.io SDK, you must:
* Use React Native versions 0.79 and later. Current versions of XCode donât support earlier versions of React Native.
* Set iOS 13 or later as your minimum deployment target in XCode
* Have an Android device or emulator with Google Play Services enabled and a minimum OS version between Android 5.0 (API level 21) and Android 13.0 (API level 33).
* Have an iOS 13+ device to test your implementation. You cannot test push notifications in a simulator.
* Add React Navigation to your app to support deep links and screen tracking.
## The Processing Queue[](#the-processing-queue)
The SDK automatically adds all calls to a queue system, and waits to perform these calls until certain criteria is met. This queue makes things easier, both for you and your users: it handles errors and retries for you (even when users lose connectivity), and it can save usersâ battery life by batching requests.
The queue holds requests until any one of the following criteria is met:
* There are 20 or more tasks in the queue.
* 30 seconds have passed since the SDK performed its last task.
* The app is closed and re-opened.
For example, when you identify a new person in your app using the SDK, you wonât see the created/updated person immediately. Youâll have to wait for the SDK to meet any of the criteria above before the SDK sends a request to the Customer.io API. Then, if the request is successful, youâll see your created/updated person in your workspace.
---
## Getting started > Packages options
**Source:** /integrations/sdk/react-native/getting-started/packages-options
# Packages and Configuration Options
## SDK packages[](#sdk-packages)
The SDK consists of a few packages. You *must* use the `CioConfig` and `CustomerIO` packages to configure and initialize the SDK.
Package Product
Required?
Description
CustomerIO
â
The main SDK package. Used to initialize the SDK and call the SDKâs methods.
CioConfig
â
Configure the SDK including in-app messaging support.
CioRegion
Used inside the `CioConfig.region` option to declare your regionâEU or US.
CioLogLevel
Used inside the `CioConfig.logLevel` option to set the level of logs you can view from the SDK.
## Configuration options[](#configuration-options)
You can determine global behaviors for the SDK in using `CioConfig` package. You must provide configuration options before you initialize the SDK; you cannot declare configuration changes after you initialize the SDK.
Import `CioConfig` and then set configuration options to configure things like your logging level and whether or not you want to automatically track device attributes, etc. Note that the `logLevel` option requires the `CioLogLevel` package and the `region` option requires the `CioRegion` package.
```javascript
import {
CioLogLevel, CioRegion, CustomerIO, CioConfig
} from 'customerio-reactnative';
const App = () => {
useEffect(() => {
const config: CioConfig = {
cdpApiKey: 'CDP API Key', // Mandatory
migrationSiteId: 'siteId', // Required if migrating from an earlier version
region: CioRegion.US, // Replace with CioRegion.EU if your Customer.io account is in the EU.
logLevel: CioLogLevel.Debug,
trackApplicationLifecycleEvents: true,
inApp: {
siteId: 'site_id',
},
push: {
android: {
pushClickBehavior: PushClickBehaviorAndroid.ActivityPreventRestart
}
}
};
CustomerIO.initialize(config)
}, [])
}
```
Option
Type
Default
Description
`cdpApiKey`
string
**Required**: the key you'll use to initialize the SDK and send data to Customer.io
`region`
`CioRegion.EU` or `CioRegion.US`
`CioRegion.US`
****Requires the CioRegion package.** You must set the region your account is in the EU (`CioRegion.EU`).**
`apiHost`
string
The domain youâll proxy requests through. Youâll only need to set this (and `cdnHost`) if youâre [proxying requests](#proxying-requests).
`autoTrackDeviceAttributes`
boolean
`true`
Automatically gathers information about devices, like operating system, device locale, model, app version, etc
`cdnHost`
string
The domain youâll fetch configuration settings from. Youâll only need to set this (and `apiHost`) if youâre [proxying requests](#proxying-requests).
`logLevel`
string
`error`
**Requires the `CioLogLevel` package**. Sets the level of logs you can view from the SDK. Set to `debug` or `info` to see more logging output.
`migrationSiteId`
string
**Required if you're updating from 3.x**: the credential for previous versions of the SDK. This key lets the SDK send remaining tasks to Customer.io when your audience updates your app.
`screenViewUse`
`All` or `InApp`
`All`
`ScreenView.All` (Default): Screen events are sent to Customer.io. You can use these events to build segments, trigger campaigns, and target in-app messages.
`ScreenView.InApp`: Screen view events not sent to Customer.io. Youâll *only* use them to target in-app messages based on page rules.
`trackApplicationLifecycleEvents`
boolean
`true`
Set to `false` if you don't want the app to send lifecycle events
`inApp`
object
**Required for in-app support**. This object takes a `siteId` property, determining the workspace your in-app messages come from.
`push`
object
Takes a single option called `PushClickBehaviorAndroid`. This object and option controls how your app behaves when your Android audience taps push notifications.
## Proxying requests[](#proxying-requests)
By default, requests go through our domain at `cdp.customer.io`. You can proxy requests through your own domain to provide a better privacy and security story, especially when submitting your app to app stores.
To proxy requests, youâll need to set the `apiHost` and `cdnHost` properties in your `SDKConfigBuilder`. While these are separate settings, you should set them to the same URL.
While you need to initialize the SDK with a `cdpApiKey`, you can set this to any value you want. You only need to pass your actual key when you send requests from your server backend to Customer.io. If you want to secure requests to your proxy server, you can set the `cdpApiKey` to a value representing basic authentication credentials that you handle on your own. See [proxying requests](/integrations/data-in/proxying-requests) for more information.
```javascript
import {
CustomerIO,
CioConfig,
CioRegion,
} from 'customerio-reactnative';
const config: CioConfig = {
cdpApiKey: 'your-cdp-api-key',
region: CioRegion.US,
inApp: {
siteId: 'your-site-id',
},
// Proxy requests through your own domain
apiHost: 'proxy.example.com',
cdnHost: 'proxy.example.com',
} as CioConfig;
CustomerIO.initialize(config);
```
---
## Getting started > Troubleshooting
**Source:** /integrations/sdk/react-native/getting-started/troubleshooting
# Troubleshooting
If youâre having trouble with the SDK, here are some basic steps to troubleshoot your problems, and solutions to some known issues.
## Basic troubleshooting steps[](#basic-troubleshooting-steps)
1. **Make sure your app meets our [prerequisites](/integrations/sdk/react-native/getting-started/#prerequisites)**: Attempting to use our SDK in an environment that doesnât match our supported versions may result in build errors.
1. **Update to the latest version**: When troubleshooting problems with our SDKs, we generally recommend that you try updating to the latest version. That helps us weed out issues that might have been seen in previous versions of the SDK.
2. **Try running our MCP server**: Our MCP server includes an `integration` tool that can provide immediate help with your implementation, including problems with push and in-app notifications. See [Use our MCP server to troubleshoot your implementation](#troubleshoot-with-mcp) below.
3. **Enable `debug` logging**: Reproducing your issue with `loglevel` set to `debug` can help you (or us) pinpoint problems.
 Donât use debug mode in your production app
Debug mode is great for helping you find problems as you integrate with Customer.io, but we strongly recommend that you set `loglevel` to `error` in your publicly available, production app.
4. **Try our test image**: Using [an image](#image-display-issues) that we know works in push and in-app notifications can help you narrow down problems relating to images in your messages.
### If you need to contact support[](#if-you-need-to-contact-support)
Weâre here to help! If you contact us for help with an SDK-related issue, weâll generally ask for the following information. Having it ready for us can help us solve your problem faster.
1. **Share information about your device and environment**: Let us know where you had an issueâthe SDK and version of the SDK that youâre using, the specific device, operating system, message, use case, and so on. The more information you share with us, the easier it is for us to weed out externalities and find a solution.
2. **Provide comprehensive debug logs**: When sharing logs with our support team, please ensure your logs include:
* **SDK initialization**: Show that the SDK was initialized with your site ID and API key
* **Profile identification**: Show that a profile was identified in your app
* **Issue reproduction**: Capture the exact issue youâre experiencing
* **Unfiltered logs**: Provide complete, unfiltered logsâdonât remove or filter out any log entries
* **Debug level enabled**: Make sure `loglevel` is set to `debug` when capturing logs for support
3. **For push notification issues**:
* **Use live push examples**: If your issue relates to push notifications, provide logs from a **live push notification** sent through a campaign or API call, not a test send. Live pushes show the actual payload that was delivered to the profile.
* **Test in different app states**: Test and document the issue in various app states:
* **Foreground**: App is open and active
* **Background**: App is running but not in focus
* **Killed/Terminated**: App is completely closed
* **Include the push payload**: Share the complete push notification payload that you sent.
4. **[Grant access to your workspace](/journeys/privacy/#support-team-access)**: It may help us to see exactly what triggers a campaign, what data is associated with devices youâre troubleshooting, etc. You can grant access for a limited time, and revoke access at any time.
### Troubleshooting issues with our MCP server[](#troubleshoot-with-mcp)
Our [MCP server](/ai/mcp-server/) includes an `integration` tool that can help troubleshoot your implementation, including problems with push and in-app notifications. It has a deep understanding of our SDKs and provides an immediate way to get support with your implementationâwithout necessarily needing to capture debug logs, etc.
You can ask the MCP server basic questions like, âMy push notifications arenât working. Can you help me troubleshoot the problem?â
Or you can ask more specific questions like, âDeep links in push notifications donât work for customers in my Android app.â Or âIâm not receiving metrics for push notifications for iOS users.â
The tool will return detailed steps to help you find and troubleshoot problems.
### Capture logs[](#capture-logs)
Logs help us pinpoint the problem and find a solution.
1. **Enable debug logging in your app**.
 You should not use debug mode in your production app. Remember to disable debug logging before you release your app to the App Store.
```javascript
import { CustomerIO, CioConfig, CioLogLevel } from 'customerio-reactnative';
const config: CioConfig = {
CioApiKey: 'Your CDP API Key',
logLevel: CioLogLevel.Debug,
}
CustomerIO.initialize(config) ;
```
2. Build and run your app on a physical device or emulator.
3. In the console, run:
```fallback
react-native log-ios
react-native log-android
```
4. Export your log to a text file and send it to our Support team at [[email protected]](mailto:[email protected]). In your message, describe your problem and provide relevant information about:
* The version of the SDK youâre using.
* The type of problem youâve encountered.
* An existing GitHub issue URL or existing support email so we know what these log files are in reference to.
## NaN, infinite, or imaginary number values[](#nan-infinite-or-imaginary-number-values)
Customer.io doesnât handle invalid JSON values in your payloads, like `NaN`, infinite, or imaginary number values. If you send these values in `identify`, `track`, `screen`, or similar calls, weâll drop them and record errors.
While we drop invalid values, we donât drop the entire payload. The operation itself will still succeed. For example, if you send an identify call with two attributes, one of which is a `NaN` value, weâll drop the `NaN` value, but the identify call succeeds with the other attribute.
## Push notification issues[](#push-notification-issues)
### Problems with rich push notifications (images, delivered metrics, etc)[](#problems-with-rich-push-notifications-images-delivered-metrics-etc)
If you have trouble with rich push features, like images not showing up in your push notifications, delivery metrics not being reported when a push notification is visible on the device, and so on, itâs possible that you either need to re-create your NSE target to support rich notifications your you may not have embeded the `NotificationServiceExtension` (NSE) at all.
1. Remove your current NSE extension.
1. In XCode, select your project.
2. Go to the *Signing & Capabilities* tab.
3. Click the NotificationServiceExtension target; it has a bell icon next to it.
4. Click the minus sign to remove the target
5. Confirm the **Delete** operation.
[](#c66794617622894d2a61b5a026240a3b-lightbox)
2. Remove existing NSE files.
1. Right click the `NotificationServiceExtension` folder in your project and select **Delete**.
2. Confirm **Move to Trash**.
[](#6a5a6ee211296a0c49beb046b941bfd8-lightbox)
3. Recreate the notification service extension, following instructions for your framework. When You create your target NSE file, make sure you select your appâs name from the *Embed in Application* dropdown.
[](#97f7eea0f5f268a29a24b1bdea3c767c-lightbox)
4. Then add the required files:
* [React Native](/integrations/sdk/react-native/push-notifications/push/#integrate-push-capabilities-in-your-app)
* [Flutter](/integrations/sdk/flutter/push-notifications/push-setup/#swift-push)
* Expo (does this automatically)
* [iOS](/integrations/sdk/ios/push/#rich-push)
5. After all files are added, go to the NSE target and, under the **General** tab, check **Deployment Target** and set it to a value that is identical to your host appâs iOS version.
[](#7e7c735e2a16c72a6db71ef6cf9fc50b-lightbox)
When you create a new target, by default, XCode sets the highest version of deployment target version available. While testing if your deviceâs iOS version is lower than this deployment target, then the NSE wonât be connected to the main target and you wonât receive rich push notifications.
Then you can build and run your app to test if you can receive a rich push notification.
### Why arenât devices added to people in Production builds?[](#why-arent-devices-added-to-people-in-production-builds)
If you see devices register successfully on your Staging builds, but not in Production or TestFlight builds, there might be an issue with your project setup. Check that the Push capability is enabled for both Release and Debug modes in your project. You might also need to enable the *Background Modes (Remote Notifications)* capability, depending on your project setup and messaging needs.
### Image display issues[](#image-display-issues)
If youâre having trouble, try using our test image in a message! If it works, then thereâs likely a problem with your original image.
[](#880a298d31bff7acc0ac56edbb6a6dc6-lightbox)
Android and iOS devices support different image sizes and formats. In general, you should stick to the smallest size (under 1 MBâthe limit for Android devices) and common formats (PNG, JPEG).
iOS
Android
In-App (all platforms)
**Format**
JPEG, PNG, BMP, GIF
JPEG, PNG, BMP
JPEG, PNG, GIF
**Maximum size**
10 MB\*
1 MB
**Maximum resolution**
2048 x 1024 px
1038 x 1038 px
\*For linked media only. If you host images in our Asset Library, youâre limited to 3MB per image.
### Try updating iOS package dependencies[](#try-updating-ios-package-dependencies)
This SDK uses our iOS push package. In some cases, we may make fixes in our iOS packages that fix downstream issues in, or expose new features to this SDK. You can update the version in your podfile and then run the following command to get the latest iOS packages.
Our instructions above list out the full version of the iOS push package. If you want to automatically increment packages, you can remove the patch and minor build numbers (the second and third parts of the version number), and `pod update` will automatically fetch the latest package versions. However, please understand that fetching the latest versions can cause build issues if the latest iOS package doesnât agree with code in your app!
```shell
pod update --repo-update --project-directory=ios
```
### Why didnât everybody in my segment get a push notification?[](#why-didnt-everybody-in-my-segment-get-a-push-notification)
If your [segmentA group of people who match a series of conditions. People enter and exit the segment automatically when they match or stop matching conditions.](/journeys/data-driven-segments/) doesnât specify people who have an existing device, itâs likely that people entered your segment without using your app. If you send a push notification to such a segment, the âSentâ count will probably show fewer sends than there were people in your segment.
### Why are messages sent but not delivered or opened?[](#why-are-messages-sent-but-not-delivered-or-opened)
The *sent* status means that we sent a message to your delivery providerâAPNS or FCM. Itâll be marked *delivered* or *opened* when the delivery provider forwards the message to the device and the SDK reports the metric back to Customer.io. If a person turned their device off or put it in airplane mode, they wonât receive your push notification until theyâre back on a network.
 Make sure youâve configured your app to track metrics
If your app isnât set up to capture push metrics, your app will *never* report `delivered` or `opened` metrics!
### Why donât my messages play sounds?[](#why-dont-my-messages-play-sounds)
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](https://github.com/customerio/customerio-ios/blob/main/Apps/APN-UIKit/APN%20UIKit/Util/NotificationUtil.swift#L12-L13) 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.
### FCM `SENDER_ID_MISMATCH` error[](#fcm-sender_id_mismatch-error)
This error occurs when the FCM Sender ID in your app does not match the Sender ID in your Firebase project. To resolve this issue, youâll need to ensure that the Sender ID in your app matches the Sender ID in your Firebase project.
1. Check that you uploaded the correct JSON certificate to Customer.io. If your JSON certificate represents the wrong Firebase project, you may see this error.
2. Verify that the Sender ID in your app matches the Sender ID in your Firebase project.
3. If you imported devices (device tokens) from a previous project, make sure that you imported tokens from the correct Firebase project. If the tokens represent a different app than the one you send push notifications to, youâll see this error.
### Error: Push notifications not working[](#error-push-notifications-not-working)
If push notifications donât work, make sure that youâve initialized the Customer.io SDK in your `AppDelegate.swift` file. You must initialize the SDK in the `application(_:didFinishLaunchingWithOptions:)` method.
```swift
@main
class AppDelegateWithCioIntegration: CioAppDelegateWrapper {}
class AppDelegate: NSObject, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
...
// Initialize the Customer.io SDK for push notifications
MessagingPushAPN.initialize(withConfig: MessagingPushConfigBuilder().build())
return true
}
}
```
### Deep linking to iOS when your app is killed[](#troubleshoot-ios-deep-links)
Thereâs a known issue preventing deep links from working when your app is closed on iOS devices. When the app is in a closed state, the native click event fires before the appâs lifecycle begins. We recommend a workaround:
1. Update `didFinishLaunchingWithOptions` in your `AppDelegate.swift` file with the code below. We extract the deep link from the push notification payload and add it to the launch options, ensuring that your React Native app receives the link when it starts.
```swift
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let delegate = ReactNativeDelegate()
let factory = RCTReactNativeFactory(delegate: delegate)
...
if var launchOptions = launchOptions,
let remotePush = launchOptions[UIApplication.LaunchOptionsKey.remoteNotification] as? [String: [String: [String: String]]],
let link = remotePush["CIO"]?["push"]?["link"], let url = URL(string:link),
launchOptions[UIApplication.LaunchOptionsKey.url] == nil
{
launchOptions[UIApplication.LaunchOptionsKey.url] = url
}
let appName = Bundle.main.displayName
factory.startReactNative(
withModuleName: appName,
in: window,
initialProperties: ["appName": appName],
launchOptions: launchOptions
)
...
}
```
### Compiler error: âXâ is unavailable in application extensions for iOS[](#compiler-error-x-is-unavailable-in-application-extensions-for-ios)
This error occasionally occurs when users add a notification extension to handle rich push messages. If you see this error, try the following steps:
1. Add this code to the end of your `Podfile`:
```ruby
post_install do |installer|
installer.pods_project.targets.each do |target|
if target.name.start_with?('CustomerIO')
puts "Modifying target #{target.name} with workaround"
target.build_configurations.each do |config|
puts "Setting build config settings for #{target.name}"
config.build_settings['APPLICATION_EXTENSION_API_ONLY'] ||= 'NO'
end
end
end
end
```
2. In the root directory of your app, run `pod install --project-directory=ios`. This command will apply the above workaround to your project.
3. Try to compile your app again.
*If you still see the error message*, itâs likely that the error you see is related to a different SDK that you use in your app and not the Customer.io SDK. We suggest that you contact the developers of the SDK that you see in the error message for help.
*If you donât see an error message*, [send our technical support team a message](mailto:[email protected]) with:
* The error message that you see when compiling your app.
* The contents of your `ios/Podfile` and `ios/Podfile.lock` files.
* The version of the React Native SDK that you are using.
### Deep links on iOS only open in a browser[](#deep-links-on-ios-only-open-in-a-browser)
It sounds like you want to use [universal links](/universal-links/#deep-links-vs-universal-links)âlinks that go to your app if a person has your app installed and to your website if they donât. Universal links are a bit different than your average deep link and require a little bit of additional setup.
## In-App message issues[](#in-app-message-issues)
### My in-app messages are sent but not delivered[](#my-in-app-messages-are-sent-but-not-delivered)
People wonât get your message until they open your app. If you use page rules, they wonât see your message until they visit the right screen(s), so delivery times for in-app messages can vary significantly from other types of messages.
---
## In app messages > In app actions
**Source:** /integrations/sdk/react-native/in-app-messages/in-app-actions
# In-app event listeners
## How it works[](#how-it-works)
In-app messages often have a call to action. Most basic actions are handled automatically by the SDK. For example, if you set a call-to-action button to open a web page, the SDK will open the web page when the user taps the button.
But you can also set up custom actions that require your app to handle the response. If you set up custom actions, youâll need to handle the action yourself and dismiss the resulting message when youâre done with it.
### Handle responses to messages (event listeners)[](#event-listeners)
You can set up event listeners to handle your audienceâs response to your messages. For example, you might run different code in your app when your audience taps a button in your message or when they dismiss the message without tapping a button.
You can listen for four different events:
* `messageShown`: a message is âsentâ and appears to a user
* `messageDismissed`: the user closes the message (by tapping an element that uses the `close` action)
* `errorWithMessage`: the message itself produces an errorâthis probably prevents the message from appearing to the user
* `messageActionTaken`: the user performs an action in the message.
After you initialize the SDK, you can register an event listener to subscribe to in-app events. In the code below, `event` is an instance of `InAppMessageEvent` containing details about the in-app message, e.g. `messageId`, `deliveryId`.
```javascript
import { CustomerIO, InAppMessageEventType } from "customerio-reactnative";
CustomerIO.inAppMessaging.registerEventsListener((event) => {
switch (event.eventType) {
case InAppMessageEventType.messageShown:
// handle message shown
break;
case InAppMessageEventType.messageDismissed:
// handle message dismissed
break;
case InAppMessageEventType.errorWithMessage:
// handle message error
break;
case InAppMessageEventType.messageActionTaken:
// event.actionValue => The type of action that triggered the event.
// event.actionName => The name of the action specified when building the in-app message.
// handle message action
break;
}
});
```
## Handling custom actions[](#handling-custom-actions)
When you set up an in-app message, you can determine the âactionâ to take when someone taps a button, taps your message, etc. In most cases, youâll want to deep link to a screen, etc. But, in some cases, you might want to execute some custom action or codeâlike requesting that a user opts into push notifications or enables a particular setting.
In these cases, youâll want to use the `messageActionTaken` event listener and listen for custom action names or values to execute code. While youâll have to write custom code to handle custom actions, the SDK helps you [listen for in-app message events](#event-listeners) including your custom action, so you know when to execute your custom code.
1. When you [add an action to an in-app message](/journeys/in-app-messages/) in Customer.io, select *Custom Action* and set your Actionâs *Name* and value. The *Name* corresponds to the `actionName`, and the value represents the `actionValue` in your event listener.
[](#1005ee0b179f3999e81398a57e369557-lightbox)
2. [Register an event listener](#event-listener) for `MessageActionTaken`, and listen for the `actionName` or `actionValue` you set up in the previous step.
 Use names and values exactly as entered
We donât modify your actionâs name or value, so youâll need to match the case of names or values exactly as entered in your *Custom Action*.
3. When someone receives a message and invokes the action (tapping a button, tapping a message, etc), your app will perform the custom action.
## Dismiss in-app message[](#dismiss-in-app-message)
You can dismiss the currently display in-app message with the following method. This can be particularly useful to dismiss in-app messages when your audience clicks or taps custom actions.
```javascript
CustomerIO.inAppMessaging.dismissMessage();
```
## Deep links[](#deep-links)
You can open deep links when a user clicks actions inside in-app messages. Setting up deep links for in-app messages is the same as [setting up deep links for push notifications](/integrations/sdk/react-native/push-notifications/deep-links/).
---
## In app messages > Inline in app
**Source:** /integrations/sdk/react-native/in-app-messages/inline-in-app
# Inline in-app messages
Inline in-app messages help you send dynamic content into your app. The messages can look and feel like a part of your app, but provide fresh and timely content without requiring app updates.
## How it works[](#how-it-works)
An inline message targets a specific view in your app. Basically, youâll create an empty placeholder view in your appâs UI, and weâll fill it with the content of your message.
This makes it easy to show dynamic content in your app without development effort. You donât need to force an update every time you want to talk to your audience. *And*, unlike push notifications, banners, toasts, and so on, in-line messages can look like natural parts of your app.
## 1\. Add View to your app UI to support inline messages[](#1-add-view-to-your-app-ui-to-support-inline-messages)
Youâll need to include a UI element in your app UI to render inline messages. The view will automatically adjust its height when messages are loaded or interacted with.
 Weâve set up examples in [our sample apps](https://github.com/customerio/customerio-reactnative/tree/main/example) that might help if you want to see a real-world implementation of this feature.
Add the `InlineInAppMessageView` component to your React Native app:
```jsx
import { InlineInAppMessageView } from 'customerio-reactnative';
function MyComponent() {
return (
{
console.log('Action clicked:', { message, actionValue, actionName });
}}
/>
);
}
```
### View layout[](#view-layout)
The `InlineInAppMessageView` automatically adjusts its height at runtime when messages load or users interact with them. You should avoid setting a fixed height on this component as it might interfere with message rendering.
Youâre responsible for setting layout styles to position your view correctly (width, margins, padding, and so on). The component will handle its own height dynamically.
## 2\. Build and send your message[](#2-build-and-send-your-message)
When you add an in-app message to a broadcast or campaign in Customer.io:
1. Set the **Display** to **Inline** and set the **Element ID** to the ID you set in your app. If the editor says that the inline display feature is *Web/iOS only*, donât worry about that. Weâre working on updating this UI.
2. (Optional) If you send multiple messages to the same Element ID, youâll also want to set the **Priority**. This determines which message weâll show to your audience first, if there are multiple messages in the queue.
Then craft and send your message!
[](#74810b23329cd479a11edfdda9f818e0-lightbox)
## Handling custom actions[](#handling-custom-actions)
When you set up an in-app message, you can determine the âactionâ to take when someone taps a button, taps your message, etc. In most cases, youâll want to deep link to a screen, etc. But, in some cases, you might want to execute some custom action or codeâlike requesting that a user opts into push notifications or enables a particular setting.
While youâll have to write custom code to handle custom actions, the SDK helps you listen for in-app message events including your custom action, so you know when to execute your custom code.
Follow the steps below to implement custom actions for inline messages:
### 1\. Compose an in-app message with a custom action[](#1-compose-an-in-app-message-with-a-custom-action)
When you [add an action to an in-app message](/journeys/in-app-messages/) in Customer.io, select *Custom Action* and set your Actionâs *Name* and value. The *Name* corresponds to the `actionName`, and the value represents the `actionValue` in your event listener.
[](#1005ee0b179f3999e81398a57e369557-lightbox)
### 2\. Listen for events[](#2-listen-for-events)
There are two ways to listen for these click events in inline in-app messages.
1. Register a callback with your inline view:
```jsx
import { InlineInAppMessageView } from 'customerio-reactnative';
function MyComponent() {
const handleActionClick = (message, actionValue, actionName) => {
// Perform some logic when people tap an action button.
// Example code handling button tap:
switch (actionValue) { // use actionValue or actionName, depending on how you composed the in-app message.
case "enable-auto-renew":
// Perform the action to enable auto-renew
enableAutoRenew(actionName);
break;
// You can add more cases here for other actions
default:
// Handle unknown actions or do nothing
console.log("Unknown action:", actionValue);
}
};
return (
);
}
```
2. Register a global SDK event listener.
When you register an [event listener](../in-app-event-listeners/#event-listeners) with the SDK, weâll call the `messageActionTaken` event listener. We call this event listener for both modal and inline in-app message types, so you can reuse logic for inline and non-inline messages if you want.
## Handle responses to messages (event listeners)[](#event-listeners)
Like [modal in-app messages](../in-app-event-listeners/#event-listeners), you can set up event listeners to handle your audienceâs response to your messages.
For inline messages, you can listen for three different events:
* `messageShown`: a message is âsentâ and appears to a user.
* `errorWithMessage`: the message itself produces an errorâthis probably prevents the message from appearing to the user.
* `messageActionTaken`: the user performs an action in the message. As [shown above](#2-listen-for-events), this is only called if the View instance doesnât have an `onActionClick` callback set.
Unlike modal in-app messages, youâll notice that thereâs no `messageDismissed` event. This is because inline messages donât really have a concept of dismissal like modal messages do. Theyâre meant to be a part of your app!
---
## In app messages > Set up in app
**Source:** /integrations/sdk/react-native/in-app-messages/set-up-in-app
# Set up in-app messages
## How it works[](#how-it-works)
An in-app message is a message that people see within the app. People wonât see your in-app messages until they open your app. If you set an *expiry* period for your message, and that time elapses before someone opens your app, they wonât see your message.
You can also set *page rules* to display your in-app messages when people visit specific pages in your app. However, to take advantage of page rules, you need to use [screen tracking](/integrations/sdk/react-native/tracking/screen-events) features. Screen tracking tells us the names of your pages and which page a person is on, so we can display in-app messages on the correct pages in your app.
## Set up in-app messaging[](#set-up-in-app-messaging)
In-app messages are disabled by default. Just set the `inApp.siteId` option in your `CioConfig`, and your app will be able to receive in-app messages.
1. Go to and select **Workspace Settings** in the upper-right corner of the Customer.io app and go to [**API and Webhook Credentials**](https://fly.customer.io/workspaces/last/settings/api_credentials).
2. Copy the **Site ID** for the set of credentials that you want to send your in-app messages from. If you donât have a set of credentials, click **Create Tracking API Key** to generate them.
[](#64d2b27827ffddb00dc77b851a7a6854-lightbox)
```javascript
const config: CioConfig = {
cdpApiKey: 'cdp_api_key',
region: CioRegion.US,
inApp: {
siteId: 'site_id',
}
};
```
## Page rules[](#page-rules)
You can set page rules when you create an in-app message. A page rule determines the page that your audience must visit in your app to see your message. However, before you can take advantage of page rules, you need to:
1. Track screens in your app. See the [Track Events](/integrations/sdk/react-native/tracking/track-events/#manual-screenview) page for help sending `screen` events.
2. Provide page names to whomever sets up in-app messages in fly.customer.io. If we donât recognize the page that you set for a page rule, your audience will never see your message.
[](#a55af0f9917c15a7b484c9df200f448d-lightbox)
Keep in mind: page rules are case sensitive. Make sure your page rules match the casing of the `title` in your `screen` events.
[](#ba51bbdc9b4c25b5402f99a8a9d30245-lightbox)
## Anonymous messages[](#anonymous-messages)
As of version 4.11, you can send [anonymous in-app messages](/journeys/anonymous-in-app/). These are messages that are sent *only* to people you havenât identified yet.
You *can* use lead forms in anonymous messages to capture leads and potentially identify people when they submit your form. For example, you could use a lead form and offer a coupon or newsletter to people who provide their email addresses. See [Lead forms](/journeys/messages-and-webhooks/in-app/lead-form/) for more information.
---
## Push notifications > Deep links
**Source:** /integrations/sdk/react-native/push-notifications/deep-links
# Deep Links
## How it works[](#how-it-works)
Deep links are the links that directs users to a specific location within a mobile app. When you set up your notification, you can set a âdeep link.â When your audience taps the notification, the SDK will route users to the right place.
Deep links help make your message meaningful, with a call to action that makes it easier, and more likely, for your audience to follow. For example, if you send a push notification about a sale, you can send a deep link that takes your audience directly to the sale page in your app.
However, to make deep links work, youâll have to handle them in your app. Weâve provided instructions below to handle deep links in both Android and iOS versions of your app.
## Android: set up deep links[](#deep-links-android)
1. Deep links provide a way to link to a screen in your app. Youâll set up deep links by adding [intent filters](https://developer.android.com/training/app-links/deep-linking) to the `AndroidManifest.xml` file.
```xml
```
2. Now youâre ready to handle deep links. In your `App.js` file or anywhere you handle navigation, youâll add code that looks like this.
```javascript
import { NavigationContainer } from '@react-navigation/native';
const config = {
screens: {
Home: {
path: 'home/:id?',
parse: {
id: (id: String) => `${id}`,
},
},
}
};
const linking = {
prefixes: ['amiapp://'],
config
};
return (
...
)
```
After you set up intent filters, you can test your implementation with the Rich Push editor or the payloads included for [Testing push notifications](https://customer.io/sdk/react-native/push/#rich-push-payloads).
### Push Click Behavior[](#push-click-behavior)
The `push.android.pushClickBehavior` config option controls how your app behaves when your audience taps push notifications on Android devices. The SDK automatically tracks `Opened` metrics for all options.
```javascript
const config: CioConfig = {
cdpApiKey: 'cdp_api_key',
region: CioRegion.US,
inApp: {
siteId: 'site_id',
},
push: {
android: {
pushClickBehavior: PushClickBehaviorAndroid.ActivityPreventRestart
}
}
};
CustomerIO.initialize(config)
```
The available options are:
* `ActivityPreventRestart` **(Default)**: If your app is already in the foreground, the SDK will not re-create your app when your audience clicks a push notification. Instead, the SDK will reuse the existing activity. If your app *is not* in the foreground, weâll launch a new instance of your deep-linked activity. We recommend that you use this setting if your app has screens that your audience shouldnât navigate away fromâlike a shopping cart screen.
* `ActivityNoFlags`: If your app is in the foreground, the SDK will re-create your app when your audience clicks a notification. The activity is added on top of the appâs existing navigation stack, so if your audience tries to go back, they will go back to where they previously were.
* `ResetTaskStack`: No matter what state your app is in (foreground, background, killed), the SDK will re-create your app when your audience clicks a push notification. Whether your app is in the foreground or background, the state of your app will be killed so your audience cannot go back to the previous screen if they press the back button.
## iOS: Set up deep links[](#deep-links-ios)
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.
### Setup App Scheme deep links[](#app-scheme-deep-links)
After you [set up push notifications](/integrations/sdk/react-native/push/#register-push) you can enable deep links in rich push notifications. There are a number of ways to enable deep links. Our example below uses `@react-navigation` with a `config` and `prefix` to automatically set paths. The paths are the values youâd use in your push payload to send a link.
However, before you can do this, you need to set up your app link scheme for iOS. [Learn more about URL schemes for iOS apps](https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app).
 Thereâs an issue deep linking into iOS when the app is closed
In iOS, deep link click events wonât fire when your app is closed. See our [troubleshooting section](/integrations/sdk/react-native/troubleshooting/#troubleshoot-ios-deep-links) for a workaround to this issue.
1. Open your project in Xcode and select your root project in the Project Navigator.
2. Go to the **Info** tab.
3. Scroll down to the options in the *Info* tab and expand **URL Types**.
4. Click to add a new, untitled schema.
5. Under **Identifier** and **URL Schemes**, add the name of your schema.
[](#c619327cb81e2efb9935b0551472c376-lightbox)
6. Open your `AppDelegate.swift` file and add the code below. Note that the Customer.io SDK automatically forwards deep links from Customer.io push notifications to the `application(:open:options:)` method. For push notifications from other providers, you still need to handle deep links manually in the `userNotificationCenter(didReceive:withCompletionHandler:)` method.
```swift
import UIKit
import CioMessagingPushAPN
import React
@main
class AppDelegateWithCioIntegration: CioAppDelegateWrapper {}
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
// Handle deep links
return RCTLinkingManager.application(app, open: url, options: options)
}
}
```
7. Now youâre ready to handle deep links. In your `App.js` file or anywhere you handle navigation, youâll add code that looks like this.
```javascript
import { NavigationContainer } from '@react-navigation/native';
const config = {
screens: {
Home: {
path: 'home/:id?',
parse: {
id: (id: String) => `${id}`,
},
},
}
};
const linking = {
prefixes: ['amiapp://'],
config
};
return (
...
)
```
### Set up Universal Links[](#universal-links-deep-links)
Follow [React Nativeâs documentation](https://reactnative.dev/docs/linking) to implement Universal Links in your app.
---
## Push notifications > Multiple push providers
**Source:** /integrations/sdk/react-native/push-notifications/multiple-push-providers
# Handling Multiple Push Providers
## How to handle multiple push providers[](#how-to-handle-multiple-push-providers)
If Customer.io is the only SDK that you use in your app to display push notifications, then you donât need to do anything special to display push notifications.
But, if you use another module in your app that can display push notifications like `expo-notifications`, `react-native-push-notification`, or `rnfirebase`, these modules can take over push handling by default and prevent your app from receiving push notifications from Customer.io.
You can solve this problem using one (and **only one**) of the methods below, but we typically recommend [the first option](#cio-process-notification-payloads), because it doesnât require you to write native code! Please note that the following methods will always return `true` for iOS.
## Option 1 (Recommended): Set Customer.io SDK to handle push clicks[](#cio-process-notification-payloads)
You can pass the payloads of other message services to Customer.io whenever a device receives a notification, and our SDK can process it for you. The SDK exposes the `onMessageReceived` method for this that takes two arguments:
* a `message.data` object containing the incoming notification payload
* a `handleNotificationTrigger` boolean indicating whether or not to trigger a notification.
* `true` (default) means that the Customer.io SDK will generate the notification and track associated metrics.
* `false` means that the SDK will process the notification to track metrics but *will not* generate a notification on the device.
Youâll use the `onMessageReceived` like this:
```javascript
CustomerIO.pushMessaging.onMessageReceived(message).then(handled => {
// If true, the push was a Customer.io notification and handled by our SDK
// Otherwise, `handled` is false
});
```
You can pass values in `onMessageReceived` by listening to notification events exposed by other SDKs. Make sure that you add listeners in the right places to process notifications that your app receives when itâs in the foreground and add background listeners that might be required by other SDK to process notifications that your app receives when itâs in background/killed state.
If you *always* send rich push messages (with `image` and/or `link`), adding event listeners is enough. But if you send custom push payloads using the `notification` object or send simple push messages (with just a `body` and `title`), you may get duplicate notifications when your app is backgrounded because Firebase itself displays notifications sent using the `notification` object.
To avoid this, You can pass `false` in `handleNotificationTrigger` to track metrics for simple and custom payload push notifications. To simplify this behavior, the SDK also exposes an `onBackgroundMessageReceived` method that automatically suppresses pushes with the notification object when your app is in background.
If you use `rnfirebase`, you can setup listeners like this:
Foreground Listener
#### Foreground Listener[](#Foreground Listener)
To listen to messages in the foreground, set `onMessage` listener where appropriate:
```javascript
useEffect(() => {
const unsubscribe = messaging().onMessage(async remoteMessage => {
CustomerIO.pushMessaging.onMessageReceived(remoteMessage).then(handled => {
// If true, the push was a Customer.io notification and handled by our SDK
// Otherwise, `handled` is false
});
});
return unsubscribe;
}, []);
```
Background Listener
#### Background Listener[](#Background Listener)
To listen to messages when app is in background/killed state, set `setBackgroundMessageHandler` in your `index.js` file
```javascript
messaging().setBackgroundMessageHandler(async remoteMessage => {
CustomerIO.pushMessaging.onBackgroundMessageReceived(remoteMessage).then(handled => {
// If true, the push was a Customer.io notification and handled by our SDK
// Otherwise, `handled` is false
});
});
```
## Option 2: Register Customer.io Messaging Service[](#register-messaging-service)
You can register Customer.ioâs messaging service in your `Manifest` file so that we handle all notifications for your app. You can do this by adding the following code under the `` tag in the `AndroidManifest.xml` file in your appâs `android` folder.
```xml
```
 The Customer.io SDK will handle all your push notifications
The code above hands all push notifications responsibility to our SDK, meaning:
* Your app will receive all simple and rich push notifications from Customer.io.
* When your app is in the background, it can receive push notifications with a `notification` payload from other services.
* Your app **cannot receive** `data`\-only push notifications from another service.
## Manually track push metrics[](#manual-push-metrics)
If you need to manually track push metrics when you use multiple push providers (like when you display notifications yourself or use another library), you can parse a push notification payload and send `opened` or `delivered` events to the SDK in relevant callbacks:
```javascript
CustomerIO.trackMetric({
deliveryID: deliveryID,
deviceToken: deviceToken,
event: MetricEvent.Opened,
});
```
The `trackMetric` method requires the following parameters:
* `deliveryID`: The delivery ID extracted from the push notification payload
* `deviceToken`: The device token extracted from the push notification payload
* `event`: The metric event type, either `MetricEvent.Opened` or `MetricEvent.Delivered`
---
## Push notifications > Push
**Source:** /integrations/sdk/react-native/push-notifications/push
# Set up push notifications
Our React Native SDK supports push notifications over APN or FCMâincluding rich push messages with links and images. Use this page to add support for your push provider and set your app up to receive push notifications.
## How it works[](#how-it-works)
Under the hood, our React Native SDK takes advantage of our native Android and iOS SDKs. This helps us keep the React Native SDK up to date. But, for now, it also means youâll need to add a *bit* of code to support your iOS users. For Android, youâre ready to go if you followed our [getting started instructions](/integrations/sdk/react-native/getting-started/#install).
Before a device can receive a push notification, you must:
1. (iOS) [Add push notification capabilities in XCode](#add-push-capabilities-in-xcode).
2. (iOS) [Integrate push notifications](#register-push): code samples on this page help you do that.
3. [Identify a person](/integrations/sdk/react-native/identify/). This associates a token with the person; you canât send push notifications to a device until you identify the recipient.
4. [Request, or check for, push notification permissions](#prompt-users-for-to-opt-into-push-notifications). If your appâs user doesnât grant permission, notifications will not appear in the system tray.
While push providers support a number of features in their payloads, **our React Native package 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.
 Did you already set up your push providers?
To send, test, and receive push notifications, youâll need to set up your push notification service(s) in Customer.io. If you havenât already, set up [Apple Push Notification Service (APNs)](/journeys/push-getting-started/#for-ios) and/or [Firebase Cloud Messaging (FCM)](/push-getting-started/#for-android).
## Set up push on Android[](#register-push-android)
If you followed our [Getting Started instructions](/integrations/sdk/react-native/quick-start-guide/#install), youâre already set up to send standard push notifications to Android devices.
## Set up push on iOS[](#register-push)
Youâll need to add some additional code to support push notifications for iOS. Youâll need to add push capabilities in XCode and integrate push capabilities in your app.
### Add push capabilities in Xcode[](#add-push-capabilities-in-xcode)
Before you can work with push notifications, you need to add Push Notification capabilities to your project in XCode.
1. In your React Native project, go to the `ios` subfolder and open `.xcworkspace`.
2. Select your project, and then under *Targets*, select your main app.
3. Click the **Signing & Capabilities** tab
4. Click **Capability**.
5. Add **Push Notifications** to your app. When youâre done, youâll see **Push Notifications** added to your appâs capabilities, but there are still a few more steps to finish setting things up.
[](#b837646bba75943a4f08d0fee059210c-lightbox)
6. Go to **File** > **New** > **Target**.
[](#64d64173bde7b46bad5fc1f14cc8f36a-lightbox)
7. Select **Notification Service Extension** and click **Next**.
[](#6413f7694da0358105aca5a02cf835dc-lightbox)
8. Enter a product name, like *NotificationServiceExtension* (which we use in our examples on this page) and **set the *Language* to *Swift* or *Objective C* based on the language you use for native iOS files.** Click **Finish**.
[](#97f7eea0f5f268a29a24b1bdea3c767c-lightbox)
9. When presented with the dialog below, click **Cancel**. This helps Xcode continue debugging your app and not just the extension you just added.
[](#7a87192ad7f0dc9047625d6dfc407e77-lightbox)
Now you have another target in your project navigator named `NotificationServiceExtension`. Weâll configure this extension when we [Integrate Push Notifications](#integrate-push-capabilities-in-your-app) in the following section.
### Integrate push capabilities in your app[](#integrate-push-capabilities-in-your-app)
Pick your push provider (*APN* or *FCM*) and the language your native files are written in to get started (*Objective C* or *Swift*).
#### APN/Objective-C[](#APN/Objective-C)
1. Open your `ios/Podfile` and add the Customer.io push dependency, highlighted here, to both your main target and `NotificationServiceExtension` target.
```ruby
target 'SampleApp' do # Look for the main app target.
# Make all file modifications after this line:
config = use_native_modules!
# Add the following line to add the Customer.io native dependency:
pod 'customerio-reactnative/apn', :path => '../node_modules/customerio-reactnative'
end
# Next, copy and paste the code below to the bottom of your Podfile:
target 'NotificationServiceExtension' do
# Notice the '-richpush' in the line below. This line of code is different from what you added for your main target.
pod 'customerio-reactnative-richpush/apn', :path => '../node_modules/customerio-reactnative'
end
```
2. Open your terminal, go to your project path and install the pods.
```bash
pod install --project-directory=ios
```
3. Open `ios/.xcworkspace` in Xcode, and add a new Swift file to your project.
Copy the code here into your file. Weâre calling our file `MyAppPushNotificationsHandler.swift` and the associated class `MyAppPushNotificationsHandler`, but you might want to rename things to fit your app.
```swift
import Foundation
import CioMessagingPushAPN
@objc
public class MyAppPushNotificationsHandler : NSObject {
public override init() {}
@objc(setupCustomerIOClickHandling)
public func setupCustomerIOClickHandling() {
// This line of code is required in order for the Customer.io SDK to handle push notification click events.
// Initialize MessagingPushAPN module to automatically handle push notifications that originate from Customer.io
MessagingPushAPN.initialize(withConfig: MessagingPushConfigBuilder().build())
}
@objc(application:deviceToken:)
public func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
// Register device to receive push notifications with device token
MessagingPush.shared.application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
}
@objc(application:error:)
public func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
MessagingPush.shared.application(application, didFailToRegisterForRemoteNotificationsWithError: error)
}
}
```
4. Open your `ios/AppDelegate.mm` file and import your header file. The name of the header file will depend on your appâs main target name i.e. `YourMainTargetName-Swift.h` and is auto-created by Xcode.
If youâre not a native iOS developer, the `.h` and `.mm` files represent interface and implementation respectively. Itâs a convention of XCode to keep these files separate.
```obj-c
#import "SampleApp-Swift.h"
```
5. Inside AppDelegateâs `@implementation`, create an object of `MyAppPushNotificationsHandler` (remember to substitute the name of your handler).
```obj-c
@implementation AppDelegate
MyAppPushNotificationsHandler* pnHandlerObj = [[MyAppPushNotificationsHandler alloc] init];
```
6. Update `AppDelegate.mm` to register a device to the current app user and handle push notifications.
```obj-c
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
RCTAppSetupPrepareApp(application, true);
NSMutableDictionary *modifiedLaunchOptions = [NSMutableDictionary dictionaryWithDictionary:launchOptions];
if (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) {
NSDictionary *pushContent = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
if (pushContent[@"CIO"] && pushContent[@"CIO"][@"push"] && pushContent[@"CIO"][@"push"][@"link"]) {
NSString *initialURL = pushContent[@"CIO"][@"push"][@"link"];
if (!launchOptions[UIApplicationLaunchOptionsURLKey]) {
modifiedLaunchOptions[UIApplicationLaunchOptionsURLKey] = [NSURL URLWithString:initialURL];
}
}
}
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:modifiedLaunchOptions];
#if RCT_NEW_ARCH_ENABLED
_contextContainer = std::make_shared<:react::contextcontainer const>();
_reactNativeConfig = std::make_shared<:react::emptyreactnativeconfig const>();
_contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
_bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer];
bridge.surfacePresenter = _bridgeAdapter.surfacePresenter;
#endif
NSDictionary *initProps = [self prepareInitialProps];
UIView *rootView = RCTAppSetupDefaultRootView(bridge, @"SampleApp", initProps, true);
[application registerForRemoteNotifications];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
[pnHandlerObj setupCustomerIOClickHandling];
[RNNotifications startMonitorNotifications];
return YES;
}
...
// Required to register device token.
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
// Register device to receive push notifications with device token
[pnHandlerObj application:application deviceToken:deviceToken];
}
// Required for the registration error event.
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
[pnHandlerObj application:application error:error];
}
```
7. In XCode, select your `NotificationServiceExtension`. Go to **File** > **New** > **File** > **Swift File** and click **Next**. Enter a file name, like `NotificationServicePushHandler`, and click **Create**. This adds a new swift file in your extension target.
Copy the code on the right and paste it into this new file (which weâve called `NotificationServicePushHandler.swift`) fileâreplacing everything in the file and update `Env.cdpApiKey` with your CDP API key.
```swift
import CioMessagingPushAPN
import Foundation
import UserNotifications
@objc
public class NotificationServicePushHandler: NSObject {
public override init() {}
@objc(didReceive:withContentHandler:)
public func didReceive(
_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
) {
MessagingPushAPN.initializeForExtension(
withConfig: MessagingPushConfigBuilder(cdpApiKey: Env.cdpApiKey)
// Optional: specify region where your Customer.io account is located (.US or .EU). Default: US
//.region(.US)
.build()
)
MessagingPush.shared.didReceive(request, withContentHandler: contentHandler)
}
@objc(serviceExtensionTimeWillExpire)
public func serviceExtensionTimeWillExpire() {
MessagingPush.shared.serviceExtensionTimeWillExpire()
}
}
```
8. Open your `NotificationService.m` file and copy the highlighted lines (beginning on line 2) into your file. The name of the header file on line 2 will depend on your extensionâs name i.e. `YourNotificationServiceExtensionName-Swift.h` and is automatically created by Xcode.
After this, you can run your app on a physical device and send yourself a push notification with images and deep links to test your implementation. Youâll have to use a physical device because simulators canât receive push notifications.
```swift
#import "NotificationService.h"
#import "NotificationServiceExtension-Swift.h"
@interface NotificationService ()
@end
@implementation NotificationService
// Create object of class NotificationServicePushHandler
NotificationServicePushHandler* nsHandlerObj = nil;
// Initialize the object
+ (void)initialize{
nsHandlerObj = [[NotificationServicePushHandler alloc] init];
}
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
[nsHandlerObj didReceive:request withContentHandler:contentHandler];
}
- (void)serviceExtensionTimeWillExpire {
// Called just before the extension will be terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
[nsHandlerObj serviceExtensionTimeWillExpire];
}
@end
```
#### APN/Swift[](#APN/Swift)
1. Open your `ios/Podfile` and add the Customer.io push dependency, highlighted here, to both your main target and `NotificationServiceExtension` target.
```ruby
target 'SampleApp' do # Look for the main app target.
# Make all file modifications after this line:
config = use_native_modules!
# Add the following line to add the Customer.io native dependency:
pod 'customerio-reactnative/apn', :path => '../node_modules/customerio-reactnative'
end
# Next, copy and paste the code below to the bottom of your Podfile:
target 'NotificationServiceExtension' do
# Notice the '-richpush' in the line below. This line of code is different from what you added for your main target.
pod 'customerio-reactnative-richpush/apn', :path => '../node_modules/customerio-reactnative'
end
```
2. Open your terminal, go to your project path and install the pods.
```bash
pod install --project-directory=ios
```
3. In your iOS subfolder, update your `AppDelegate.swift` file to use the Customer.io wrapper class that handles push notifications automatically. This approach replaces the need to manually implement push notification delegate methods.
```swift
import UIKit
import CioMessagingPushAPN
import UserNotifications
@main
class AppDelegateWithCioIntegration: CioAppDelegateWrapper {}
class AppDelegate: NSObject, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
...
// Optional: add if you need UNUserNotificationCenterDelegate methods
UNUserNotificationCenter.current().delegate = self
// Initialize the Customer.io SDK for push notifications
MessagingPushAPN.initialize(withConfig: MessagingPushConfigBuilder().build())
return true
}
}
extension AppDelegate: UNUserNotificationCenterDelegate {
// Optional: add this method if you want more control over notifications when your app is in the foreground
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.banner, .list, .badge, .sound])
}
}
```
4. Add a notification service extension to call the appropriate Customer.io functions. This lets your app display rich push notifications, including images, etc.
See [Deep Links](/integrations/sdk/react-native/push-notifications/deep-links/) if you want to support deep links from push notifications.
```swift
import CioMessagingPushAPN
import Foundation
import UserNotifications
class NotificationService: UNNotificationServiceExtension {
override func didReceive(
_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
) {
MessagingPushAPN.initializeForExtension(
withConfig: MessagingPushConfigBuilder(cdpApiKey: Env.cdpApiKey)
// Optional: specify region where your Customer.io account is located (.US or .EU). Default: US
//.region(.US)
.build()
)
MessagingPush.shared.didReceive(request, withContentHandler: contentHandler)
}
override func serviceExtensionTimeWillExpire() {
// Called just before the extension is terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
MessagingPush.shared.serviceExtensionTimeWillExpire()
}
}
```
#### FCM/Objective-C[](#FCM/Objective-C)
1. Open your `ios/Podfile` and add the Customer.io push dependency, highlighted here, to both your main target and `NotificationServiceExtension` target.
```ruby
# Note: You may need to add this line, as required by FCM, to the top of your Podfile if you encounter errors during 'pod install'
use_frameworks! :linkage => :static
target 'YourApp' do # Look for the main app target.
# Make all file modifications after this line:
config = use_native_modules!
# Add the following line to add the Customer.io native dependency:
pod 'customerio-reactnative/fcm', :path => '../node_modules/customerio-reactnative'
end
# Next, copy and paste the code below to the bottom of your Podfile:
target 'NotificationServiceExtension' do
# Notice the '-richpush' in the line below. This line of code is different from what you added for your main target.
pod 'customerio-reactnative-richpush/fcm', :path => '../node_modules/customerio-reactnative'
end
```
2. Open your terminal, go to your project path and install the pods.
```bash
pod install --project-directory=ios
```
3. Open `ios/.xcworkspace` in Xcode, and add a new Swift file to your project.
Copy the code here into your file. Weâre calling our file `MyAppPushNotificationsHandler.swift` and the associated class `MyAppPushNotificationsHandler`, but you might want to rename things to fit your app.
```swift
import CioMessagingPushFCM
import CioFirebaseWrapper
import FirebaseMessaging
import Foundation
@objc
public class MyAppPushNotificationsHandler: NSObject {
public override init() {}
@objc(setupCustomerIOClickHandling)
public func setupCustomerIOClickHandling() {
// Initialize MessagingPushFCM module to automatically handle your appâs push notifications that originate from Customer.io
MessagingPushFCM.initialize(withConfig: MessagingPushConfigBuilder().build())
}
@objc(didReceiveRegistrationToken:fcmToken:)
public func didReceiveRegistrationToken(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
// Register device on receiving a device token (FCM)
MessagingPush.shared.messaging(messaging, didReceiveRegistrationToken: fcmToken)
}
}
```
4. Open `AppDelegate.h` and add the `FIRMessagingDelegate` import statement.
If youâre not a native Objective-C developer, the `.h` and `.mm` files represent interface and implementation respectively. Itâs a convention of XCode to keep these files separate.
```obj-c
#import
#import
#import
@interface AppDelegate : RCTAppDelegate
@end
```
5. Open your `ios/AppDelegate.mm` file and import your header file, as weâve shown on line 2 and also import `FirebaseCore` as weâve shown on line 5. The name of the header file will depend on your appâs main target name i.e. `YourMainTargetName-Swift.h` and is auto-created by Xcode.
```obj-c
#import "AppDelegate.h"
#import
#import
#import
#import
```
6. In your `AppDelegate.mm` file, create an object of your push notification handler. We called ours `MyAppPushNotificationsHandler`.
```obj-c
@implementation AppDelegate
// Create Object of class MyAppPushNotificationsHandler
MyAppPushNotificationsHandler *pnHandlerObj = [[MyAppPushNotificationsHandler alloc] init];
```
7. Update `AppDelegate.mm` to configure Firebase and handle tokens. Weâve highlighted the code here to show what you need to add.
```obj-c
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.moduleName = @"FCMSampleApp";
// You can add your custom initial props in the dictionary below.
// They will be passed down to the ViewController used by React Native.
self.initialProps = @{};
// Configure Firebase
[FIRApp configure];
// Set FCM messaging delegate
[FIRMessaging messaging].delegate = self;
// Use modifiedLaunchOptions for passing link to React Native bridge to sends users to the specified screen
NSMutableDictionary *modifiedLaunchOptions = [NSMutableDictionary dictionaryWithDictionary:launchOptions];
if (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) {
NSDictionary *pushContent = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
if (pushContent[@"CIO"] && pushContent[@"CIO"][@"push"] && pushContent[@"CIO"][@"push"][@"link"]) {
NSString *initialURL = pushContent[@"CIO"][@"push"][@"link"];
if (!launchOptions[UIApplicationLaunchOptionsURLKey]) {
modifiedLaunchOptions[UIApplicationLaunchOptionsURLKey] = [NSURL URLWithString:initialURL];
}
}
}
[pnHandlerObj setupCustomerIOClickHandling];
return [super application:application didFinishLaunchingWithOptions:modifiedLaunchOptions];
}
...
@end
```
8. In XCode, select your `NotificationServiceExtension`. Go to **File** > **New** > **File** > **Swift File** and click **Next**. Enter a file name, like `NotificationServicePushHandler`, and click **Create**. This adds a new swift file in your extension target.
Copy this code into the new file and replace `Env.cdpApiKey` with your CDP API key.
```swift
import CioMessagingPushFCM
import CioFirebaseWrapper
import Foundation
import UserNotifications
@objc
public class MyAppNotificationServicePushHandler: NSObject {
public override init() {}
@objc(didReceive:withContentHandler:)
public func didReceive(
_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
) {
MessagingPushFCM.initializeForExtension(
withConfig: MessagingPushConfigBuilder(cdpApiKey: Env.cdpApiKey)
// Optional: specify region where your Customer.io account is located (.US or .EU). Default: US
//.region(.US)
.build()
)
MessagingPush.shared.didReceive(request, withContentHandler: contentHandler)
}
@objc(serviceExtensionTimeWillExpire)
public func serviceExtensionTimeWillExpire() {
MessagingPush.shared.serviceExtensionTimeWillExpire()
}
}
```
9. In your `NotificationService.m` file, import the auto-generated header fileâe.g. `NotificationServiceExtension-Swift.h`. Youâll also need to create an object class of `MyAppNotificationServicePushHandler` and call the functions in the code sample here.
Now you can run your app on a physical device and send yourself a push notification with images and deep links to test your implementation. Youâll have to use a physical device because simulators canât receive push notifications.
```obj-c
#import
#import "NotificationService.h"
@interface NotificationService ()
@end
@implementation NotificationService
// Create object of class MyAppNotificationServicePushHandler
MyAppNotificationServicePushHandler* nsHandlerObj = nil;
// Initialize the object
+ (void)initialize {
nsHandlerObj = [[MyAppNotificationServicePushHandler alloc] init];
}
- (void)didReceiveNotificationRequest:(UNNotificationRequest*)request withContentHandler:(void (^)(UNNotificationContent* _Nonnull))contentHandler {
[nsHandlerObj didReceive:request withContentHandler:contentHandler];
}
- (void)serviceExtensionTimeWillExpire {
[nsHandlerObj serviceExtensionTimeWillExpire];
}
@end
```
#### FCM/Swift[](#FCM/Swift)
1. Open your `ios/Podfile` and add the Customer.io push dependency, highlighted here, to both your main target and `NotificationServiceExtension` target.
```ruby
# Note: You may need to add this line, as required by FCM, to the top of your Podfile if you encounter errors during 'pod install'
use_frameworks! :linkage => :static
target 'YourApp' do # Look for the main app target.
# Make all file modifications after this line:
config = use_native_modules!
# Add the following line to add the Customer.io native dependency:
pod 'customerio-reactnative/fcm', :path => '../node_modules/customerio-reactnative'
end
# Next, copy and paste the code below to the bottom of your Podfile:
target 'NotificationServiceExtension' do
# Notice the '-richpush' in the line below. This line of code is different from what you added for your main target.
pod 'customerio-reactnative-richpush/fcm', :path => '../node_modules/customerio-reactnative'
end
```
2. Open your terminal, go to your project path and install the pods.
When complete, you should see `Pod installation complete!`
```bash
pod install --project-directory=ios
```
3. Update your `AppDelegate.swift` file to handle push notifications. You can copy the code sample on the right, but you may need to update imports and other things to fit your app.
```swift
import UIKit
import CioMessagingPushFCM
import CioFirebaseWrapper
import UserNotifications
import Firebase
import FirebaseMessaging
@main
class AppDelegateWithCioIntegration: CioAppDelegateWrapper {}
class AppDelegate: NSObject, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
...
// Configure Firebase
FirebaseApp.configure()
// Optional: Set FCM messaging delegate if you need it. The Customer.io SDK will automatically read FCM tokens
Messaging.messaging().delegate = self
// Initialize MessagingPushFCM module to automatically handle your app's push notifications that originate from Customer.io
MessagingPushFCM.initialize(withConfig: MessagingPushConfigBuilder().build())
return true
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
// This is required for FCM. The Customer.io SDK does not make changes to other SDKs
Messaging.messaging().apnsToken = deviceToken
}
}
extension AppDelegate: UNUserNotificationCenterDelegate {
// Optional: add this method if you want fine-grained control over presenting Notifications in foreground
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.banner, .list, .badge, .sound])
}
}
extension AppDelegate: MessagingDelegate {
// Optional: add this method if you need access to `fcmToken` - Customer.io SDK will read this automatically
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
}
}
```
4. Add a notification service extension to call the appropriate Customer.io functions. This lets your app display rich push notifications, including images, etc.
See [Deep Links](/integrations/sdk/react-native/push-notifications/deep-links/) if you want to support deep links from push notifications.
```swift
import CioMessagingPushFCM
import Foundation
import UserNotifications
class NotificationService: UNNotificationServiceExtension {
override func didReceive(
_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
) {
MessagingPushFCM.initializeForExtension(
withConfig: MessagingPushConfigBuilder(cdpApiKey: Env.cdpApiKey)
// Optional: specify region where your Customer.io account is located (.US or .EU). Default: US
//.region(.US)
.build()
)
MessagingPush.shared.didReceive(request, withContentHandler: contentHandler)
}
override func serviceExtensionTimeWillExpire() {
// Called just before the extension is terminated by the system.
// Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
MessagingPush.shared.serviceExtensionTimeWillExpire()
}
}
```
### Sound in push notifications (iOS Only)[](#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](https://github.com/customerio/customerio-ios/blob/main/Apps/APN-UIKit/APN%20UIKit/Util/NotificationUtil.swift#L12-L13) 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.
## Push icon (Android)[](#push-icon-android)
Youâll set the icon that appears on normal push notifications as a part of your app manifestâ`android/app/src/main/AndroidManifest.xml`. If your icon appears in the wrong size, or if you want to change the standard icon that appears with your push notifications, youâll need to update your appâs manifest.
```xml
```
## Prompt users to opt-into push notifications[](#prompt-users-to-opt-into-push-notifications)
Your audience has to opt into push notifications. To display the native iOS and Android push notification permission prompt, youâll use the `CustomerIO.showPromptForPushNotifications` method.
You can configure push notifications to request authorization for sounds and badges as well (only on iOS). If a user opts into push notifications, the `CustomerIO.showPromptForPushNotifications` method will return `Granted`, otherwise it returns `Denied` as a `string`. If the user has not yet been asked to opt into notifications, the method will return `NotDetermined` (only for iOS).
```javascript
var options = {"ios" : {"sound" : true, "badge" : true}}
CustomerIO.showPromptForPushNotifications(options).then(status => {
switch(status) {
case "Granted":
// Push permission is granted, your app can now receive push notifications
break;
case "Denied":
// App is not authorized to receive push notifications
// You might need to explain users why your app needs permission to receive push notifications
break;
case "NotDetermined":
// Push permission status is not determined (Only for iOS)
break;
}
}).catch(error => {
// Failed to show push permission prompt
console.log(error)
})
```
### Get a userâs permission status[](#get-a-users-permission-status)
To get a userâs current permission status, call the `CustomerIO.getPushPermissionStatus()` method. This returns a promise with the current status as a string.
```javascript
CustomerIO.getPushPermissionStatus().then(status => {
console.log("Push permission status is - " + status)
})
```
### Optional: Remove `POST_NOTIFICATIONS` permission from Android apps[](#optional-remove-post_notifications-permission-from-android-apps)
By default, the SDK includes the `POST_NOTIFICATIONS` permission which is [required by Android 13 to show notifications on Android device](https://developer.android.com/develop/ui/views/notifications/notification-permission). However, if you do not want to include the permission because donât use notifications, or for any other reason, you can remove the permission by adding the following line to your `android/app/src/main/AndroidManifest.xml` file:
```xml
```
## Fetch the current device token[](#fetch-the-current-device-token)
You can fetch the currently stored device token using the `CustomerIO.pushMessaging.getRegisteredDeviceToken()` method. This method returns an APN/FCM token in a promise as a string.
```javascript
let token = await CustomerIO.pushMessaging.getRegisteredDeviceToken()
if (token) {
// Use the token as required in your app for example save in a state
setDeviceToken(token);
}
```
## Test your implementation[](#rich-push-payloads)
After you set up rich push, you should test your implementation. Below, we show the payload structure we use for iOS and Android. In general, you can use our regular rich push editor; itâs set up to send messages using the JSON structure we outline below.
If you want to fashion your own payload, you can use our [custom payload](/journeys/push-custom-payloads/#getting-started-with-custom-payloads).
[](#4e089ac68a22d5b994db09266a531737-lightbox)
iOS APNs payload
#### iOS APNs payload[](#iOS APNs payload)
```json
{
"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
}
}
}
```
* CIO object
Contains options supported by the Customer.io SDK.
* push object
Required Describes push notification options supported by the CIO SDK.
* 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.
* aps object
A push payload intended for an iOS device.
* alert
string
A simple alert message.
* body string
The body of your push notification.
* launch-image string
The name of the launch image file you want to display. When a user launches your app, theyâll see this image or storyboard file rather than your appâs normal launch image.
* loc-args array of \[ strings \]
An array of replacement value strings for variables in your message text. Each %@ character in the loc-key is replaced by a value from this array, in the order they appear in the message body.
* loc-key string
The key for a localized message string in your appâs Localizable.strings file.
* subtitle string
Additional information that explains the purpose of the notification.
* subtitle-loc-args array of \[ strings \]
An array of replacement value strings for variables in your subtitle string. Each %@ character in the subtitle-loc-key is replaced by a value from this array, in the order they appear in the subtitle string.
* subtitle-loc-key string
The key for a localized subtitle string in your appâs Localizable.strings file.
* title string
The title of your push notification.
* title-loc-args array of \[ strings \]
An array of replacement value strings for variables in your title string. Each %@ character in the title-loc-key is replaced by a value from this array, in the order they appear in the title string.
* title-loc-key string
The key for a localized title string in your appâs Localizable.strings files.
* 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
If 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 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.
* critical integer
1 indicates critical. 0 is not critical.
* name 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.
* volume number
The volume for a critical alert between 0 and 1, where 0 is silent and 1 is full volume.
* target-content-id string
The identifier of the window brought forward.
* thread-id string
An identifier to group related notifications.
iOS FCM payload
#### iOS FCM payload[](#iOS FCM payload)
```json
{
"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
}
}
}
}
```
* message object
Required The base object for all FCM payloads.
* apns object
Required Defines a payload for iOS devices sent through Firebase Cloud Messaging (FCM).
* headers object
Headers defined by [Appleâs payload reference](https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/sending_notification_requests_to_apns) that you want to pass through FCM.
* payload object
Required Contains a push payload.
* CIO object
Contains properties interpreted by the Customer.io iOS SDK.
* push object
Required A push payload for the iOS SDK.
* 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.
* aps object
A push payload intended for an iOS device.
* alert
string
A simple alert message.
* body string
The body of your push notification.
* launch-image string
The name of the launch image file you want to display. When a user launches your app, theyâll see this image or storyboard file rather than your appâs normal launch image.
* loc-args array of \[ strings \]
An array of replacement value strings for variables in your message text. Each %@ character in the loc-key is replaced by a value from this array, in the order they appear in the message body.
* loc-key string
The key for a localized message string in your appâs Localizable.strings file.
* subtitle string
Additional information that explains the purpose of the notification.
* subtitle-loc-args array of \[ strings \]
An array of replacement value strings for variables in your subtitle string. Each %@ character in the subtitle-loc-key is replaced by a value from this array, in the order they appear in the subtitle string.
* subtitle-loc-key string
The key for a localized subtitle string in your appâs Localizable.strings file.
* title string
The title of your push notification.
* title-loc-args array of \[ strings \]
An array of replacement value strings for variables in your title string. Each %@ character in the title-loc-key is replaced by a value from this array, in the order they appear in the title string.
* title-loc-key string
The key for a localized title string in your appâs Localizable.strings files.
* 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
If 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 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.
* critical integer
1 indicates critical. 0 is not critical.
* name 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.
* volume number
The volume for a critical alert between 0 and 1, where 0 is silent and 1 is full volume.
* 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.
Android payload
#### Android payload[](#Android payload)
```json
{
"message": {
"data": {
"title": "string", //(optional) The title of the notification.
"body": "string", //The message you want to send.
"image": "string", //https URL to an image you want to include in the notification
"link": "string" //Deep link in the format remote-habits://deep?message=hello&message2=world
}
}
}
```
* message
Required The parent object for all push payloads.
* android object
Contains properties that are **not** interpreted by the SDK but are defined by FCM. You need to write your own code to handle these Android push features.
* notification object
Properties supported specifically by Android on FCM.
* body\_loc\_arg string
Variable string values used in place of the format specifiers in `body_loc_key` to localize the body text to the userâs current localization. See Formatting and Styling for more information.
* body\_loc\_key string
The key to the body string in the appâs string resources that you want to use to localize the body text to the userâs current localization. See [String Resources](https://developer.android.com/guide/topics/resources/string-resource/) for more information.
* click\_action string
The action that occurs when a user taps on the notification. Launches an activity with a matching intent filter when a person taps the notification.
* color string
The notificationâs icon color in `#rrggbb` format.
* icon string
Sets the notification icon to `myicon` for drawable resource `myicon`. If you donât send this key, FCM displays the launcher icon from your app manifest.
* sound string
The sound that plays when the device receives the notification. Supports `"default"` or the filename of a sound resource bundled in your app. Sound files must reside in `/res/raw/`.
* tag string
Identifier to replace existing notifications in the notification drawer. If empty, each request creates a new notification.
If you specify a tag, and a notification with the same tag is already being shown, the new notification replaces the existing one in the notification drawer.
* title\_loc\_arg string
Variable string values used in place of the format specifiers in `title_loc_key` to localize the title text to the userâs current localization. See Formatting and Styling for more information.
* title\_loc\_key string
The key to the title string in the appâs string resources that you want to use to localize the title text to the userâs current localization. See [String Resources](https://developer.android.com/guide/topics/resources/string-resource/) for more information.
* data object
Required Contains all properties interpreted by the SDK.
* 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.
* android object
Contains properties that are **not** interpreted by the SDK but are defined by FCM. You need to write your own code to handle these Android push features.
* notification object
Properties supported specifically by Android on FCM.
* body\_loc\_arg string
Variable string values used in place of the format specifiers in `body_loc_key` to localize the body text to the userâs current localization. See Formatting and Styling for more information.
* body\_loc\_key string
The key to the body string in the appâs string resources that you want to use to localize the body text to the userâs current localization. See [String Resources](https://developer.android.com/guide/topics/resources/string-resource/) for more information.
* click\_action string
The action that occurs when a user taps on the notification. Launches an activity with a matching intent filter when a person taps the notification.
* color string
The notificationâs icon color in `#rrggbb` format.
* icon string
Sets the notification icon to `myicon` for drawable resource `myicon`. If you donât send this key, FCM displays the launcher icon from your app manifest.
* sound string
The sound that plays when the device receives the notification. Supports `"default"` or the filename of a sound resource bundled in your app. Sound files must reside in `/res/raw/`.
* tag string
Identifier to replace existing notifications in the notification drawer. If empty, each request creates a new notification.
If you specify a tag, and a notification with the same tag is already being shown, the new notification replaces the existing one in the notification drawer.
* title\_loc\_arg string
Variable string values used in place of the format specifiers in `title_loc_key` to localize the title text to the userâs current localization. See Formatting and Styling for more information.
* title\_loc\_key string
The key to the title string in the appâs string resources that you want to use to localize the title text to the userâs current localization. See [String Resources](https://developer.android.com/guide/topics/resources/string-resource/) for more information.
* data object
Contains the `link` property (interpreted by the SDK) and additional properties that you want to pass to your app.
* link string
A deep link (to a page in your app), or a link to a web page.
* notification object
Required Contains properties interpreted by the SDK except for the `link`.
* body string
The body of your push notification.
* image string
The URL of an HTTPS image that you want to use for your message.
* title string
The title of your push notification.
---
## Push notifications > Push metrics
**Source:** /integrations/sdk/react-native/push-notifications/push-metrics
# Capture Push Metrics
## Automatic push handling[](#automatic-push-handling)
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.
The SDK automatically tracks `opened` and `delivered` events for push notifications originating from Customer.io after you configure your app to receive [push notifications](/integrations/sdk/react-native/push-notifications/push/). You donât have to add any code to track `opened` push metrics or launch deep links.
 Do you use multiple push services in your app?
The Customer.io SDK only handles push notifications that originate from Customer.io. Push notifications that were sent from other push services or displayed locally on device are not handled by the Customer.io SDK. You must add custom handling logic to your app to handle those push events.
### Choose whether to show push while your app is in the foreground[](#show-push-app-foreground)
If your app is in the foreground and the device receives a Customer.io push notification, your app gets to choose whether or not to display the push.
You can configure this behavior by adding the following configuration to the class that you created as a part of our [push notification setup instructions](/integrations/sdk/react-native/push-notifications/push/) in your `AppDelegate.swift` file.
```swift
// In your AppDelegate.swift
@main
class AppDelegateWithCioIntegration: CioAppDelegateWrapper {}
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Initialize with foreground push display option
MessagingPushAPN.initialize(
withConfig: MessagingPushConfigBuilder().showPushAppInForeground(true).build()
)
return true
}
}
```
If the push did not come from Customer.io, youâll need to [perform custom handling](#handle-push-received-foreground) to determine whether to display the push or not.
### Custom handling when users click a push[](#handle-push-click)
You might need to perform custom handling when a user clicks a push notificationâlike you want to process custom fields in your push notification payload.
For now, the React Native SDK does not provide callbacks when your audience clicks a push notification. But you can use one of the many popular React Native push notification SDKs to receive a callback.
For example, the code below receives callbacks when users click a push using `react-native-push-notification`. Be sure to follow the documentation for the push notification SDK you choose to use to receive callbacks with.
```js
import { Notifications } from 'react-native-notifications';
Notifications.events().registerNotificationOpened((notification: Notification, completion) => {
// Process custom data attached to payload, if you need:
let pushPayload = notification.payload;
// Important: When you're done processing the push notification, you're required to call completion().
// Even if you do not process a push, this is still a requirement.
completion();
});
```
 Do you use deep links?
If youâre performing custom push click handling on push notifications originating from Customer.io, we recommend that you donât launch a deep link URL yourself. Instead, let our SDK launch deep links to avoid unexpected behaviors.
### Custom handling when getting a push while the app is foregrounded[](#handle-push-received-foreground)
If your app is in the foreground and you get a push notification, your app gets to choose whether or not to display the push. For push notifications originating from Customer.io, [your SDK configuration determines if you show the notification](#show-push-app-foreground). But you can add custom logic to your app when this kind of thing happens.
For now, the React Native SDK does not provide callbacks when a push notification is received and your app is in the foreground. But you can use one of the many popular React Native push notification SDKs to receive a callback.
For example, the code below receives a callback using `react-native-push-notification`. Be sure to follow the documentation for the push notification SDK you choose to use to receive callbacks with.
```js
import { Notifications } from 'react-native-notifications';
Notifications.events().registerNotificationReceivedForeground(
(notification: Notification, completion) => {
// Important: When you're done processing the push notification, you must call completion().
// Even if you do not process a push, you must still call completion().
completion({ alert: true, sound: true, badge: true });
// If the push notification originated from Customer.io, the value returned in the `completion` is ignored by the SDK.
// Use the SDK's push configuration options instead.
});
```
## Manually record push metrics using Javascript methods[](#metrics-javascript)
 Avoid duplicate push metrics
If you manually track your own metrics, you should [disable automatic push tracking](#disabling-automatic-push-tracking) to avoid duplicate push metrics.
 Known issue tracking `opened` push metrics in app `killed` state
When manually tracking push metrics using Javascript methods, `opened` push metrics are *not tracked* when the app is in `killed` or `closed` state. This is a known behavior and itâs recommended to instead use the automatic push tracking feature.
To monitor the `delivered` push metrics of a received push notification, use the `CustomerIO.pushMessaging.trackNotificationReceived()` method.
```javascript
CustomerIO.pushMessaging.trackNotificationReceived()
```
To track `opened` push metrics, use the `CustomerIO.pushMessaging.trackNotificationResponseReceived()` method.
```javascript
CustomerIO.pushMessaging.trackNotificationResponseReceived()
```
The method that you use to retrieve the `` value depends on API of the SDK that you are using to receive push notifications from. Here is a code snippet as an example from `expo-notifications`:
```javascript
// Listener called when a push notification is received
Notifications.addNotificationReceivedListener(notification => {
...
// Fetch Customer.io payload from the push notification
const payload = notification.request.trigger.payload
CustomerIO.pushMessaging.trackNotificationReceived(payload)
...
});
// Receives response when user interacts with the push notification
Notifications.addNotificationResponseReceivedListener(response => {
...
// Fetch Customer.io payload from the push notification response
const payload = response.notification.request.trigger.payload
CustomerIO.pushMessaging.trackNotificationResponseReceived(payload)
...
});
```
## Disabling automatic push tracking[](#disabling-automatic-push-tracking)
After you set up [push notifications](/integrations/sdk/react-native/push-notifications/push/), update your `AppDelegate.swift` file to disable automatic push notification tracking:
```swift
// In your AppDelegate.swift
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Initialize with auto-tracking disabled
MessagingPushAPN.initialize(
withConfig: MessagingPushConfigBuilder().autoTrackPushEvents(false).build()
)
return true
}
```
---
## Push notifications > Push notification channel
**Source:** /integrations/sdk/react-native/push-notifications/push-notification-channel
# Android push notification channels
ðNew in v4.5.0
Starting in Android 8.0, you can set up ânotification channels,â which categorize notifications for your Android app. Every notification now belongs to a channel and the channel determines the behavior of notificationsâwhether they play sounds, appear as heads-up notifications, and so on. Channels also give users control over which channels they want to see notifications from. For example, if you had a news app, you might have different channels for sports, entertainment, and breaking news, giving users the ability to pick the channels they care about.
Today, Customer.io supports **a single channel per app**, and it has three settings, listed in the table below. You can customize your channel when you first set up the Customer.io SDK, but you cannot change the channel ID or importance level after youâve created a channel. You can only change the channel name. [Learn more from the official Android developer docs](https://developer.android.com/develop/ui/views/notifications/channels).
Channels are created on the audienceâs side when they receive their first push from Customer.io. Users can see your channel in their device settings.
Channel setting
Default
Description
Channel ID
`[your package name]`
The ID of the channel.
Channel name
`[your app name] Notifications`
The name of the channel.
Importance
`3`
The importance of the channel. Acceptable values are `0` (min), `1` (low), `2` (medium), `3` (default/high), and `4` (urgent). See [the Android developer documentation](https://developer.android.com/develop/ui/views/notifications/channels#importance) for more about the behavior of each importance level.
## Channel configuration[](#channel-configuration)
When you first integrate with the Customer.io SDK, you can set up your Android channel. Remember, after youâve released a version of your app with channel settings, you can only change the channel name. Changes to other settings have no effect.
Youâll customize your channel in your appâs manifest.
```xml
```
### What channel settings can I change?[](#what-channel-settings-can-i-change)
When you first set up the Customer.io React-Native SDK, you can customize your channel. But after you release a version of your app with the Customer.io SDK, you cannot change the channel ID or importance level. After that, you can only change the channel name. (This is a limitation imposed by Android, not Customer.io.)
If you released your app with a version of the Customer.io React-Native SDK prior to 4.5.0, you can delete your old channel and create a new one with completely new settings per [Androidâs developer documentation](https://developer.android.com/develop/ui/views/notifications/channels#DeleteChannel).
The chart below shows what channel settings you can or canât change:
## Delete a channel[](#delete-a-channel)
If youâve released a version of your app with the Customer.io SDK earlier than v4.5.0, you can delete your old channel and create a new one with completely new settings per [Androidâs developer documentation](https://developer.android.com/develop/ui/views/notifications/channels#DeleteChannel).
```kotlin
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val id: String = context.packageName
notificationManager.deleteNotificationChannel(id)
```
---
## Tracking > Anonymous activity
**Source:** /integrations/sdk/react-native/tracking/anonymous-activity
# Anonymous activity
Before you identify a person, calls you make to the SDK are associated with an `anonymousId`. When you identify that person, we reconcile their anonymous activity with the identified person.
In Customer.io, youâll see anonymous activity in the *Activity Log*, but we donât surface anonymous [profilesAn instance of a person. Generally, a person is synonymous with their profile; there should be a one-to-one relationship between a real person and their profile in Customer.io. You reference a personâs profile attributes in liquid using `customer`âe.g. `{{customer.email}}`.](/merge-people) in Customer.io. You wonât be able to find an âanonymous personâ in your workspace, and an anonymous person canât trigger campaigns or get messages (including push notifications) from Customer.io.
When you identify a person, we merge anonymous activity with the identified person. And then the identified personâs previously-anonymous activity *can* trigger campaigns and cause your audience to receive messages.
For example, imagine that you have an ecommerce app, and you want to message people who view a specific product. An anonymous user looks at the product in question, goes to a different page, and then logs into your app. When they log in, we merge their anonymous activity including their `screen` view. This triggers the campaign you set up for people who visited the product page.
---
## Tracking > Identify
**Source:** /integrations/sdk/react-native/tracking/identify
# Identify people
Use `CustomerIO.identify()` to identify a person. You need to identify a mobile user before you can send them messages or track events for things they do in your app.
## Identify a person[](#identify)
Identifying a person:
1. Adds or updates the person in your workspace. This is basically the same as an [`identify` call to our server-side API](/api/#operation/identify).
2. Saves the personâs information on the device. Future calls to the SDK reference the identified person. For example, after you identify a person, any events that you track are automatically associated with that person.
3. Associates the current device token with the the person.
You can only identify one customer at a time. The SDK âremembersâ the most recently-identified customer. If you identify person A, and then call the identify function for person B, the SDK âforgetsâ person A and assumes that person B is the current app user. You can also [stop identifying a person](#clearIdentify), which you might do when someone logs off or stops using your app for a significant period of time.
An identify request takes two parameters:
* **userId** (Required): The unique value representing a personâan ID, email address, or the [cio\_idAn identifier for a person that is automatically generated by Customer.io and cannot be changed. This identifier provides a complete, unbroken record of a person across changes to their other identifiers (id, email, etc).](/identifying-people/#cio_id)
* **traits** (Optional): An object containing [attributesA key-value pair that you associate with a person or an objectâlike a personâs name, the date they were created in your workspace, or a companyâs billing date etc. Use attributes to target people and personalize messages.](/journeys/attributes/) that you want to add to, or update on, a person
```javascript
import { CustomerIO } from "customerio-reactnative";
// Call this method whenever you are ready to identify a user
CustomerIO.identify({
userId: "user_id",
traits: {
first_name: "user_name",
email: "email_identifier",
},
});
```
### Update a personâs attributes[](#update-person)
You store information about a person in Customer.io as [attributesA key-value pair that you associate with a person or an objectâlike a personâs name, the date they were created in your workspace, or a companyâs billing date etc. Use attributes to target people and personalize messages.](/journeys/attributes/). When you call the `CustomerIO.identify()` function, you can update a personâs attributes on the server-side.
If a person is already identified, and then updates their preferences, provides additional information about themselves, or performs other attribute-changing actions, you can update their attributes with `setProfileAttributes`.
You only need to pass the attributes that you want to create or modify to `setProfileAttributes`. For example, if you identify a new person with the attribute `["first_name": "Dana"]`, and then you call `CustomerIO.setProfileAttributes = ["favorite_food": "pizza"]` after that, the personâs `first_name` attribute will still be `Dana`.
```javascript
const profileAttributes = {
favouriteFood: "Pizza",
favouriteDrink: "Mango Shake"
};
CustomerIO.setProfileAttributes(profileAttributes)
```
### Device attributes[](#device-attributes)
By default (if you donât set `.autoTrackDeviceAttributes(false)` in your config), the SDK automatically collects a series of [attributesA key-value pair that you associate with a person or an objectâlike a personâs name, the date they were created in your workspace, or a companyâs billing date etc. Use attributes to target people and personalize messages.](/journeys/attributes/) for each device. You can use these attributes in [segmentsA group of people who match a series of conditions. People enter and exit the segment automatically when they match or stop matching conditions.](/journeys/data-driven-segments/) and other campaign workflow conditions to target the device owner, just like you would use a personâs other attributes. You cannot, however, use device attributes to personalize messages with [liquidA syntax that supports variables, letting you personalize messages for your audience. For example, if you want to reference a personâs first name, you might use the variable `{{customer.first_name}}`.](/using-liquid) yet.
Along with these attributes, we automatically set a `last_used` timestamp for each device indicating when the device owner was last identified, and the `last_status` of a push notification you sent to the device. You can also set your own custom device attributes. Youâll see a personâs devices and each deviceâs attributes when you go to **Journeys > People > Select a person**, and click **Devices**.
[](#db82cfb11dbec1c42a9f938183dcedbd-lightbox)
 Your integration shows device attributes in the `context` object
When you inspect calls from the SDK (in your integrationâs data inAn integration that feeds data *into* Customer.io. tab), youâll see device information in the `context` object. We flatten the device attributes that you send into your workspace, so that theyâre easier to use in [segmentsA segment is a group of people in your workspace. Use segments to trigger campaigns, track membership over time, or fine-tune your audience. There are two types of segments: data-driven and manual. Data-driven segments automatically update when people start or stop matching criteria. Manual segments are static.](/journeys/segments/). For example, `context.network.cellular` becomes `network_cellular`.
* id string
Required The device token.
* attributes object
Attributes that you can reference to segment your audienceâlike a personâs attributes, but specific to a device. These can be either the attributes defined below or custom key-value attributes.
* \_last\_status string
The delivery status of the last message sent to the deviceâsent, bounced, or suppressed. An empty string indicates that that the device hasnât received a push yet.
Accepted values:,`bounced`,`sent`,`suppressed`
* app\_version string
The version of your app that a customer uses. You might target app versions to let people know when they need to update, or expose them to new features when they do.
* cio\_sdk\_version string
The version of the Customer.io SDK in the app.
* device\_locale string
The four-letter [IETF language code](/localization/#supported-languages) for the device. For example, `en-MX` (indicating an app in Spanish formatted for a user in Mexico) or `es-ES` (indicating an app in Spanish formatted for a user in Spain).
* device\_model string
The model of the device a person uses.
* device\_os string
The operating system, including the version, on the device.
* network\_bluetooth boolean
If `true`, the deviceâs bluetooth connection is on.
* network\_cellular boolean
If `true`, the deviceâs cellular connection is on.
* network\_wifi boolean
If `true`, the deviceâs WiFi connection is on.
* push\_enabled string
If `"true"`, the device is opted-in and can receive push notifications.
Accepted values:`true`,`false`
* screen\_height integer
The height of the deviceâs screen in pixels.
* screen\_width integer
The width of the deviceâs screen in pixels.
* timezone string
The time zone of the device.
* *Custom Device Attributes\** string
Custom properties that you want to associate with the device.
* last\_used integer  (unix timestamp)
The `timestamp` when you last identified this device. If you donât pass a timestamp when you add or update a device, we use the time of the request itself. Our SDKs identify a device when a person launches their app.
* platform string
Required The device/messaging platform.
Accepted values:`ios`,`android`
#### Set custom device attributes[](#update-device)
You can also set custom device attributes with the `setDeviceAttributes` method. You might do this to save app preferences, time zone, or other custom values specific to the device. Like profile attributes, you can pass nested JSON to device attributes.
However, before you set custom device attributes, consider whether the attribute is specific to the `device` or if it applies to the person broadly. Device tokens are ephemeralâthey can change based on user behavior, like when a person uninstalls and reinstalls your app. If you want an attribute to persist beyond the life of the device, you should [apply it to the person](#update-person) rather than the device.
```javascript
const setDeviceAttributes = () => {
const deviceAttributes = {
type : "primary_device",
parentObject : {
childProperty : "someValue",
},
};
CustomerIO.setDeviceAttributes(deviceAttributes)
}
```
#### Manually add device to profile[](#add-device)
In the standard flow, identifying a person automatically associates the token with the identified person in your workspace. If you need to manually add or update the device elsewhere in your code, call the method `CustomerIO.registerDeviceToken(token)`.
```javascript
const registerDevice = () => {
// Customer.io expects a valid token to send push notifications
// to the user.
const token = 'token'
CustomerIO.registerDeviceToken(token)
}
```
## Stop identifying a person[](#clearIdentify)
When a person logs out, or does something else to tell you that they no longer want to be tracked, you should stop identifying them.
Use `clearIdentify()` to stop identifying the previously identified person (if there was one).
```javascript
CustomerIO.clearIdentify()
```
### Identify a different person[](#identify-a-different-person)
If you want to identify a new personâlike when someone switches profiles on a streaming app, etcâyou can simply call `identify()` for the new person. The new person then becomes the currently-identified person, with whom all new informationâmessages, events, etcâis associated.
```javascript
CustomerIO.identify( userId: "[email protected]", traits: {
first_name: "New",
last_name: "Person"
})Â
```
---
## Tracking > Lifecycle events
**Source:** /integrations/sdk/react-native/tracking/lifecycle-events
# Mobile Lifecycle events
By default, our Android SDK automatically tracks events that represent the lifecycle of your app and your users experiences with it.
By default, we track the following lifecycle events:
* **Application Installed**: A user installed your app.
* **Application Updated**: A user updated your app.
* **Application Opened**: A user opened your app.
* **Application Foregrounded**: A user switched back to your app.
* **Application Backgrounded**: A user backgrounded your app or switched to another app.
You might also want to send your own lifecycle events, like `Application Crashed` or `Application Updated`. You can do this using the `track` method. Youâll find a list of properties for these eventsâboth the ones we track automatically and other events you might send yourselfâin our [Mobile App Lifecycle Event specification](/integrations/api/cdp/#section/Semantic-events-for-data-out-integrations/mobile-application-lifecycle-event-schemas).
## Lifecycle event examples[](#lifecycle-event-examples)
A lifecycle event is basically a `track` call that the SDK makes automatically for you.
When you look at your data in Customer.io, youâll see lifecycle events as `track` calls, where the event `properties` are specific to the name of the event. For example, the `Application Installed` event includes the app `version` and `build` properties.
```json
{
"userId": "[email protected]",
"type": "track",
"event": "Application Installed",
"properties": {
"version": "3.2.1", "build": "247"
}
}
```
## Sending custom lifecycle events[](#sending-custom-lifecycle-events)
You can send your own lifecycle events using the `track` call. However, whenever you send lifecycle events, you should use the *Application EventName* convention that we use for our default lifecycle events.
These [semantic event](/integrations/data-in/semantic-events/) names and properties represent [a standard](/integrations/data-in/semantic-events/mobile-app/) that we use across Customer.io and our downstream destinations. Adhering to this standard ensures that your events automatically map to the correct event types in Customer.io and any other services you send your data to.
If you opt out of automatic lifecycle events, you can send your own `track` calls for these events. Or, for events we canât track automatically, you might be able to use a webhook or a callback to collect crash events. For example, you might want to send a `track` call for `Application Crashed` when your app crashes or `Application Updated` when people update your app.
```javascript
CustomerIO.track("Application Crashed", {
url: "/page/in/app"
});
```
## Disable lifecycle events[](#disable-lifecycle-events)
We track lifecycle events by default. You can disable this behavior by passing the `trackApplicationLifecycleEvents` option in the `CioConfig` object when you initialize the SDK.
```javascript
import {
CioLogLevel, CioRegion, CustomerIO, CioConfig
} from 'customerio-reactnative';
const config: CioConfig = {
cdpApiKey: 'cdp_api_key', // Mandatory
migrationSiteId: 'site_id', // For migration
region: CioRegion.US,
trackApplicationLifecycleEvents: false,
inApp: {
siteId: 'site_id', // this removes the use of enableInApp and simplifies in-app configuration
}
};
CustomerIO.initialize(config)
```
---
## Tracking > Screen events
**Source:** /integrations/sdk/react-native/tracking/screen-events
# Screen tracking
Screen views are events that record the pages that your audience visits in your app. They have a `type` property set to `screen`, and a `name` representing the title of the screen or page that a person visited in your app.
Screen view events let you trigger [campaignsCampaigns are automated workflows you set up to send people messages and perform other actions when they meet your criteria.](/journeys/campaigns-in-customerio/) or add people to [segmentsA group of people who match a series of conditions. People enter and exit the segment automatically when they match or stop matching conditions.](/journeys/data-driven-segments/) based on the parts of your app your audience uses. Screen view events also update your audienceâs âLast Visitedâ attribute, which can help you track how recently people used your app.
## Enable automatic screen tracking[](#auto-screenview)
Weâve provided some example code below using [React Navigation](https://reactnavigation.org/docs/screen-tracking/) for automatic screen tracking. This example requires `@react-navigation/native` and `@react-navigation/native-stack` to create a navigation container in `App.js`
If you want to send more data with screen events, or you donât want to send events for every individual screen that people view in your app, you [send screen events manually](#manual-screenview).
```javascript
import { NavigationContainer, useNavigationContainerRef } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { useRef } from 'react';
const Stack = createNativeStackNavigator();
export default function App() {
const navigationRef = useNavigationContainerRef();
const routeNameRef = useRef();
return (
{
routeNameRef.current = navigationRef.getCurrentRoute().name;
}}
onStateChange={async () => {
const previousRouteName = routeNameRef.current;
const currentRouteName = navigationRef.getCurrentRoute().name;
if (previousRouteName !== currentRouteName) {
CustomerIO.screen(currentRouteName)
}
routeNameRef.current = currentRouteName;
}}
>
);
};
```
### Screenview settings for in-app messages[](#screenview-settings-for-in-app-messages)
Customer.io uses `screen` events to determine where users are in your app so you can target them with in-app messages on specific screens. By default, the SDK sends `screen` events to Customer.ioâs backend servers. But, if you donât use `screen` events to track user activity, segment your audience, or to trigger campaigns, these events might constitute unnecessary traffic and event history.
If you donât use `screen` events for anything other than in-app notifications, you can set the `ScreenViewUse` parameter to `ScreenView.InApp`. This setting stops the SDK from sending `screen` events back to Customer.io but still allows the SDK to use `screen` events for in-app messages, so you can target in-app messages to the right screen(s) without sending event traffic into Customer.io!
```javascript
import {
CioLogLevel, CioRegion, CustomerIO, CioConfig
} from 'customerio-reactnative';
const App = () => {
useEffect(() => {
const config: CioConfig = {
cdpApiKey: 'CDP API Key', // Mandatory
region: CioRegion.US,
screenViewUse: ScreenView.All
trackApplicationLifecycleEvents: true,
inApp: {
siteId: 'site_id',
}
};
CustomerIO.initialize(config)
}, [])
}
```
## Send your own screen events[](#manual-screenview)
Screen events use the `.screen` method. Like other event types, you can add a `data` object containing additional information about the event or the currently-identified person.
```javascript
CustomerIO.screen("screen-name", {"property": "value"})
```
---
## Tracking > Track events
**Source:** /integrations/sdk/react-native/tracking/track-events
# Track events
Events represent things people do in your app so that you can track your audienceâs activity and metrics. Use events to segment your audience, trigger campaigns, and capture usage metrics in your app.
## Track an event[](#track-an-event)
The `track` method helps you send events representing your audienceâs activities to Customer.io. When you send events, you can include event `properties`âinformation about the person or the event that they performed.
In Customer.io, you can use events to trigger campaigns and broadcasts. Those campaigns might send someone a push notification or manipulate information associated with the person in your workspace.
Events include the following:
* `name`: the name of the event. Most event-based searches in Customer.io hinge on the name, so make sure that you provide an event name that will make sense to other members of your team.
* `properties` (Optional): Additional information that you might want to reference in a message. You can reference data attributes in messages and other campaign actionsA block in a campaign workflowâlike a message, delay, or attribute change. using [liquidA syntax that supports variables, letting you personalize messages for your audience. For example, if you want to reference a personâs first name, you might use the variable `{{customer.first_name}}`.](/using-liquid) in the format `{{event.}}`.
```javascript
import { CustomerIO } from "customerio-reactnative";
CustomerIO.track("event_name", {
propertyName: propertyValue
});
```
 Perform downstream actions with semantic events
Some downstream actions donât neatly map to our simple `identify`, `track`, and other calls. For these, we use âsemantic events,â events that have a special meaning in Customer.io and your destinations. See [Semantic Events](#semantic-events) for more information.
### Anonymous activity[](#anonymous-activity)
If you send a `track` call before you `identify` a person, weâll attribute the event to an `anonymousId`. When you identify the person, weâll reconcile their anonymous activity with the identified person.
When we apply anonymous events to an identified person, the previously anonymous activity becomes eligible to trigger campaigns in Customer.io.
## Semantic Events[](#semantic-events)
Some actions donât map cleanly to our simple `identify`, `track`, and other calls. For these, we use âsemantic events,â events that have a special meaning in Customer.io and your destinations.
These are especially important in Customer.io for destructive operations like deleting a person. When you send an event with a semantic `event` name, weâll perform the appropriate action.
For example, if a person decides to leave your service, you might delete them from your workspace. In Customer.io, youâll do that with a `Delete Person` event.
```javascript
CustomerIO.track("User Deleted)
```
---
## Whats new > 4.3 upgrade
**Source:** /integrations/sdk/react-native/whats-new/4.3-upgrade
# 4.x -> 4.3
Version 4.3 of the Customer.io React Native SDK introduces a new `CioAppDelegateWrapper` pattern for iOS that simplifies push notification setup and eliminates the need for method swizzling.
## Key Changes[](#key-changes)
The primary change in version 4.3 is the introduction of the wrapper pattern for handling push notifications on iOS. This change:
* **Eliminates method swizzling**: No more automatic method replacement
* **Simplifies setup**: Less boilerplate code required
* **Improves reliability**: More predictable behavior
See the instructions below to update your app depending on whether you send push notifications with APN or FCM and whether you use UIKit or SwiftUI.
## Update with APN (Apple Push Notification service)[](#update-appdelegate-apn)
### UIKit[](#uikit-apn)
Update your `AppDelegate.swift` file to use the new `CioAppDelegateWrapper` pattern. See the *Before* sample to see what needs to change and the *After* sample to see the new pattern.
Before (4.x)
#### Before (4.x)[](#Before \(4.x\))
```swift
import UIKit
import CioMessagingPushAPN
import UserNotifications
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Initialize push
MessagingPushAPN.initialize(withConfig: MessagingPushConfigBuilder().build())
// Register for push notifications
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, _ in
if granted {
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
}
return true
}
// Manual push handling methods
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)
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
MessagingPush.shared.application(application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: completionHandler)
}
}
```
After (4.3)
#### After (4.3)[](#After \(4.3\))
```swift
import UIKit
import CioMessagingPushAPN
import UserNotifications
@main
class AppDelegateWithCioIntegration: CioAppDelegateWrapper {}
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Initialize push with wrapper - handles all push methods automatically
MessagingPushAPN.initialize(withConfig: MessagingPushConfigBuilder().build())
// Register for push notifications
// You can move this line to any part of your app. It's not critical to call it in this method.
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, _ in
if granted {
// Remove this, as Customer.io SDK handles this automatically
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
}
return true
}
// No manual push methods needed - CioAppDelegateWrapper handles everything
}
```
### SwiftUI[](#swiftui-apn)
If youâre using SwiftUI, youâll need to use the `@UIApplicationDelegateAdaptor` instead of the `@main` attribute. See the *Before* sample to see what needs to change and the *After* sample to see the new pattern.
Before (4.x)
#### Before (4.x)[](#Before \(4.x\))
```swift
import SwiftUI
import CioMessagingPushAPN
import UserNotifications
@main
struct MyApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class AppDelegate: NSObject, UIApplicationDelegate {
// Similar manual push handling as UIKit example above
}
```
After (4.3)
#### After (4.3)[](#After \(4.3\))
```swift
import SwiftUI
import CioMessagingPushAPN
import UserNotifications
@main
struct MyApp: App {
@UIApplicationDelegateAdaptor(CioAppDelegateWrapper.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Initialize push with wrapper
MessagingPushAPN.initialize(withConfig: MessagingPushConfigBuilder().build())
return true
}
// No manual push methods needed
}
```
## Update with FCM (Firebase Cloud Messaging)[](#update-appdelegate-fcm)
### UIKit[](#uikit-fcm)
Update your `AppDelegate.swift` file to use the new `CioAppDelegateWrapper` pattern. See the *Before* sample to see what needs to change and the *After* sample to see the new pattern.
Before (4.x)
#### Before (4.x)[](#Before \(4.x\))
```swift
import UIKit
import CioMessagingPushFCM
import UserNotifications
import Firebase
import FirebaseMessaging
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Configure Firebase
FirebaseApp.configure()
// Set FCM messaging delegate
Messaging.messaging().delegate = self
// Register for push notifications
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, _ in
if granted {
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
}
return true
}
// Manual push handling methods
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().apnsToken = deviceToken
MessagingPush.shared.application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
MessagingPush.shared.application(application, didFailToRegisterForRemoteNotificationsWithError: error)
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
MessagingPush.shared.application(application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: completionHandler)
}
}
extension AppDelegate: MessagingDelegate {
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
// Handle FCM token
}
}
```
After (4.3)
#### After (4.3)[](#After \(4.3\))
```swift
import UIKit
import CioMessagingPushFCM
import UserNotifications
import Firebase
import FirebaseMessaging
@main
class AppDelegateWithCioIntegration: CioAppDelegateWrapper {}
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Configure Firebase
FirebaseApp.configure()
// Set FCM messaging delegate
Messaging.messaging().delegate = self
// Initialize push FCM with wrapper - handles all push methods automatically
MessagingPushFCM.initialize(withConfig: MessagingPushConfigBuilder().build())
// Register for push notifications
// You can move this line to any part of your app. It's not critical to call it in this method.
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, _ in
if granted {
// Remove this, as Customer.io SDK handles this automatically
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
}
return true
}
// No manual push methods needed - wrapper handles everything
}
extension AppDelegate: MessagingDelegate {
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
// Handle FCM token - Customer.io SDK will also receive this automatically
}
}
```
### SwiftUI[](#swiftui-fcm)
If youâre using SwiftUI, youâll need to use the `@UIApplicationDelegateAdaptor` instead of the `@main` attribute. See the *Before* sample to see what needs to change and the *After* sample to see the new pattern.
Before (4.x)
#### Before (4.x)[](#Before \(4.x\))
```swift
import SwiftUI
import CioMessagingPushFCM
import UserNotifications
@main
struct MyApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class AppDelegate: NSObject, UIApplicationDelegate {
// Similar manual push handling as UIKit example above
}
```
After (4.3)
#### After (4.3)[](#After \(4.3\))
```swift
import SwiftUI
import CioMessagingPushFCM
import UserNotifications
@main
struct MyApp: App {
@UIApplicationDelegateAdaptor(CioAppDelegateWrapper.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Initialize push with wrapper
MessagingPushAPN.initialize(withConfig: MessagingPushConfigBuilder().build())
return true
}
// No manual push methods needed
}
```
## Important Notes[](#important-notes)
1. **Manual push handling methods are not required**: the `CioAppDelegateWrapper` automatically records information from following methods. But you can still use these methods if you want to add custom push handling:
* `didRegisterForRemoteNotificationsWithDeviceToken`
* `didFailToRegisterForRemoteNotificationsWithError`
* `didReceiveRemoteNotification`
* All other push-related delegate methods
2. **The `@main` attribute** - Must be on the wrapper class, not your AppDelegate.
## Troubleshooting[](#troubleshooting)
If push notifications stop working after you update your implementation:
1. Make sure that youâve added the `@main` attribute to the wrapper class
2. Verify that youâve removed `@main` from your original AppDelegate
3. Check that youâre calling `MessagingPushAPN.initialize()` or `MessagingPushFCM.initialize()`
4. If you encounter some unexpected behavior and want to test is it related to new Push Notification tracking system, just comment the following line and compare `class AppDelegateWithCioIntegration: CioAppDelegateWrapper {}` with the original AppDelegate.
---
## Whats new > 4.x upgrade
**Source:** /integrations/sdk/react-native/whats-new/4.x-upgrade
# Upgrade from 3.4x to 4x
This page provides steps to help you upgrade from react native 3.4 or later so you understand the development effort required to update your app and take advantage of the latest features.
## What changed?[](#what-changed)
This update provides native support for our new integrations framework. While this represents a significant change âunder the hood,â weâve tried to make it as seamless as possible for you; much of your implementation remains the same.
This move also adds two additional features:
* **Support for anonymous tracking**: you can send events and other activity for anonymous users, and weâll reconcile that activity with a person when you identify them.
* **Built-in lifecycle events**: the SDK now automatically captures events like âApplication Installedâ and âApplication Updatedâ for you.
* **New device-level data**: the SDK captures the device `name` and other device-level context for you.
## Upgrade process[](#upgrade-process)
Youâll update initialization calls for the SDK itself and the push and/or in-app messaging modules.
**As a part of this process, your credentials change**. Youâll need to set up a new data inAn integration that feeds data *into* Customer.io. integration in Customer.io and get a new *CDP API Key*. But youâll *also* need to keep your previous `siteId` as a `migrationSiteId` when you initialize the SDK. The `migrationSiteId` is a key helps the SDK send remaining traffic when people update your app.
When youâre done, youâll also need to change a few base properties to fit the new APIs. In general, `identifier` becomes `userId`, `body` becomes `traits`, and `data` becomes `properties`.
### 1\. Get your new *CDP API Key*[](#1-get-your-new-cdp-api-key)
The new version of the SDK requires you to set up a new data inAn integration that feeds data *into* Customer.io. integration in Customer.io. As a part of this process, youâll get your *CDP API Key*.
1. Go to [*Data & Integrations* > *Integrations*](https://fly.customer.io/workspaces/last/journeys/integrations/all/overview) and click **Add Integration**.
2. Select **React Native**.
3. Enter a *Name* for your integration, like âMy React Native Appâ.
4. Weâll present you with a code sample containing a `cdpApiKey` that youâll use to initialize the SDK. Copy this key and keep it handy.
5. Click **Complete Setup** to finish setting up your integration.
[](#40e573b250197f1c640f816429d6bf9b-lightbox)
Remember, you can also [connect your React Native app to services outside of Customer.io](/integrations/data-out/add-destination/)âlike your analytics provider, data warehouse, or CRM.
### 2\. Update your initialization[](#2-update-your-initialization)
Youâll initialize the new version of the SDK and its packages with `CioConfig` objects instead of `CustomerioConfig`. While weâve listed all the new configuration options, youâll want to pay close attention to the following changes:
* `CustomerIOEnv` is no longer necessary.
* `Region` becomes `CioRegion`.
* `siteId` becomes `migrationSiteId`.
* Youâll initialize the SDK with `initialize(config)` instead of `initialize(env, config)`.
If you previously used the `backgroundQueueMinNumberOfTasks` or `backgroundQueueSecondsDelay` options, you should remove them from your configuration as well. These options are no longer supported, and may cause build errors if you use strict type checking.
```javascript
import {
CioLogLevel, CioRegion, CustomerIO, CioConfig
} from 'customerio-reactnative';
const config: CioConfig = {
cdpApiKey: 'cdp_api_key', // Mandatory
migrationSiteId: 'site_id', // For migration
region: CioRegion.US,
logLevel: CioLogLevel.Debug,
trackApplicationLifecycleEvents: true,
inApp: {
siteId: 'site_id', // this removes the use of enableInApp and simplifies in-app configuration
},
push: {
android: {
pushClickBehavior: PushClickBehaviorAndroid.ActivityPreventRestart
}
}
};
CustomerIO.initialize(config)
```
### 3\. Update your AppDelegate push notification handler[](#3-update-your-appdelegate-push-notification-handler)
In your `MyAppPushNotificationsHandler.swift` (or the associated file where you add a push notification handler in your main target), you can remove the `CioTracking` module and the `initialize` method.
**If you write native code in Objective-C**, youâll also need to update your `MessagingPushAPN` or `MessagingPushFCM` initialization. Weâve highlighted the lines youâll need to remove or modify in the code sample below.
APN
#### APN[](#APN)
```swift
import Foundation
import CioMessagingPushAPN
// remove this line
import CioTracking
@objc
public class MyAppPushNotificationsHandler : NSObject {
public override init() {}
@objc(setupCustomerIOClickHandling)
public func setupCustomerIOClickHandling() {
// remove this line
CustomerIO.initialize(siteId: "siteId", apiKey: "apiKey", region: .US) { config in }
// update this line to
MessagingPushAPN.initialize(withConfig: MessagingPushConfigBuilder().build())
}
@objc(application:deviceToken:)
public func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
MessagingPush.shared.application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
}
@objc(application:error:)
public func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
MessagingPush.shared.application(application, didFailToRegisterForRemoteNotificationsWithError: error)
}
}
```
FCM
#### FCM[](#FCM)
```swift
import Foundation
import CioMessagingPushFCM
import FirebaseMessaging
// remove this line
import CioTracking
@objc
public class MyAppPushNotificationsHandler : NSObject {
public override init() {}
@objc(setupCustomerIOClickHandling)
public func setupCustomerIOClickHandling() {
// remove this line
CustomerIO.initialize(siteId: Env.siteId, apiKey: Env.apiKey, region: Region.US) { config in }
// update this line to
MessagingPushFCM.initialize(withConfig: MessagingPushConfigBuilder().build())
}
// Register device on receiving a device token (FCM)
@objc(didReceiveRegistrationToken:fcmToken:)
public func didReceiveRegistrationToken(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
MessagingPush.shared.messaging(messaging, didReceiveRegistrationToken: fcmToken)
}
}
```
### 4\. Update your NotificationService push notification handler[](#4-update-your-notificationservice-push-notification-handler)
In your `NotificationServicePushHandler.swift` (or the associated file where you add a push notification handler in NotificationServiceExtension), you can remove the `CioTracking` module and the `initialize` method.
**If you write native code in Objective-C**, youâll also need to update your `MessagingPushAPN` or `MessagingPushFCM` initialization. Weâve highlighted the lines youâll need to remove or modify in the code sample below.
APN
#### APN[](#APN)
```swift
import Foundation
import UserNotifications
import CioMessagingPushAPN
// remove this line
import CioTracking
@objc
public class NotificationServicePushHandler: NSObject {
public override init() {}
@objc(didReceive:withContentHandler:)
public func didReceive(
_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
) {
// remove this line
CustomerIO.initialize(siteId: "siteId", apiKey: "apiKey", region: .US) { config in }
// update this line to
MessagingPushAPN.initializeForExtension(
withConfig: MessagingPushConfigBuilder(cdpApiKey: "cdpApiKey")
// Optional: specify region where your Customer.io account is located (.US or .EU). Default: US
// .region(.US)
.build()
)
MessagingPush.shared.didReceive(request, withContentHandler: contentHandler)
}
@objc(serviceExtensionTimeWillExpire)
public func serviceExtensionTimeWillExpire() {
MessagingPush.shared.serviceExtensionTimeWillExpire()
}
}
```
FCM
#### FCM[](#FCM)
```swift
import Foundation
import UserNotifications
import CioMessagingPushFCM
// remove this line
import CioTracking
@objc
public class NotificationServicePushHandler: NSObject {
public override init() {}
@objc(didReceive:withContentHandler:)
public func didReceive(
_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
) {
// remove this line
CustomerIO.initialize(siteId: "siteId", apiKey: "apiKey", region: .US) { config in }
// update this line to
MessagingPushFCM.initializeForExtension(
withConfig: MessagingPushConfigBuilder(cdpApiKey: "cdpApiKey")
// Optional: specify region where your Customer.io account is located (.US or .EU). Default: US
// .region(.US)
.build()
)
MessagingPush.shared.didReceive(request, withContentHandler: contentHandler)
}
@objc(serviceExtensionTimeWillExpire)
public func serviceExtensionTimeWillExpire() {
MessagingPush.shared.serviceExtensionTimeWillExpire()
}
}
```
### 5\. Update your `identify` call[](#5-update-your-identify-call)
Our APIs changed slightly in this release. Weâve done our best to make the new APIs as similar as possible to the old ones. The names of a few properties that youâll pass in your calls have changed, but their functionality has not.
* `identify`: `identifier` becomes `userId` and `body` becomes `traits`
* `track` and `screen` calls are structured the same as previous versions, but the `data` object is now called `properties`.
Weâve highlighted changes in the sample below.
```javascript
//identify: identifier becomes userId, body becomes traits
CustomerIO.identify({
userId: "user_id",
traits: {
first_name: "user_name",
email: "email_identifier",
},
});
//track: no significant change to method
//in Customer.io data object renamed properties
CustomerIO.track("track_event_name", {
propertyName: propertyValue
});
//screen: no significant change to method.
//name becomes title, data object renamed properties
CustomerIO.screen("screen_event_name", {
propertyName: propertyValue
});
```
## Configuration Changes[](#configuration-changes)
As a part of this release, weâve changed a few configuration options when you initialize the SDK. Youâll use `CioConfig` to set your configuration options. The following table shows the changes to the configuration options.
Field
Type
Default
Description
`cdpApiKey`
string
Replaces `apiKey`; required to initialize the SDK and send data into Customer.io.
`migrationSiteId`
string
Replaces `siteId`; required if youâre updating from 2.x. This is the key representing your previous version of the SDK.
`trackApplicationLifeCycleEvents`
boolean
`true`
When true, the SDK automatically tracks application lifecycle events (like *Application Installed*).
`inApp`
object
Replaces the former `enableInApp` option, providing a place to set in-app configuration options. For now, it takes a single property called `siteId`.
`push`
object
Replaces the former `enablePush` option, providing a place to set push configuration options. For now, it only takes the `android.pushClickBehavior` setting.
`backgroundQueueMinNumberOfTasks`
removed
This option is no longer available.
`backgroundQueueSecondsDelay`
removed
This option is no longer available.
---
## Whats new > 5.x upgrade
**Source:** /integrations/sdk/react-native/whats-new/5.x-upgrade
# 4.x -> 5.0.0
Version 5.x of the Customer.io React Native SDK introduces Firebase wrapper support for FCM users that improves Firebase compatibility and simplifies push notification setup.
## What changed?[](#what-changed)
Version 5.x introduces a Firebase wrapper that improves compatibility with Firebase Cloud Messaging (FCM) and other Firebase services in your app.
## Do you need to update to this version?[](#do-you-need-to-update-to-this-version)
**You need to update to this version if** you use **FCM (Firebase Cloud Messaging)** for push notifications
## Update process[](#update-process)
Add the `CioFirebaseWrapper` import to your Swift files that use `CioMessagingPushFCM`.
Add the Firebase wrapper import to your `AppDelegate.swift` file:
```swift
import UIKit
import CioMessagingPushFCM
import CioFirebaseWrapper // Add this import for FCM users
import UserNotifications
import Firebase
import FirebaseMessaging
@main
class AppDelegateWithCioIntegration: CioAppDelegateWrapper {}
```
## Troubleshooting[](#troubleshooting)
If you see build errors related to Firebase after upgrading:
1. **Clean your build**: Run `cd ios && pod install && cd ..` then rebuild
2. **Check imports**: Ensure youâve added `import CioFirebaseWrapper` to all files that import `CioMessagingPushFCM`
---
## Whats new > 6.x upgrade
**Source:** /integrations/sdk/react-native/whats-new/6.x-upgrade
# 5.x -> 6.0.0
Version 6.0.0 of the Customer.io React Native SDK requires the React Native new architecture.
## What changed?[](#what-changed)
Version 6.0.0 removes support for React Nativeâs legacy architecture. This aligns with [React Nativeâs move to exclusively support their new architecture](https://reactnative.dev/blog/2025/12/10/react-native-0.83). You must migrate your app to use React Nativeâs new architecture to use this and future versions of the SDK.
## Do you need to update to this version?[](#do-you-need-to-update-to-this-version)
We recommend updating to the latest SDK version. However, if your app uses the old React Native architecture and youâre not ready to migrate, you can continue using version 5.x until youâre ready to adopt the new architecture.
### Prerequisites[](#prerequisites)
Before updating to SDK version 6.0.0, your app must use React Nativeâs new architecture. See [React Nativeâs documentation](https://reactnative.dev/) for more information.
## Update process[](#update-process)
If your app already uses React Nativeâs new architecture, updating to Customer.io SDK version 6.0.0 is straightforward. There are no public API changes.
### 1\. Migrate to the new React Native architecture[](#1-migrate-to-the-new-react-native-architecture)
If you havenât migrated to React Nativeâs new architecture yet, see the [React Native documentation](https://reactnative.dev/blog/2024/10/23/the-new-architecture-is-here) for instructions. When your app is successfully running on the new architecture, you can update to Customer.io SDK version 6.0.0.
### 2\. Update the SDK version[](#2-update-the-sdk-version)
Update your `package.json` to use version 6.0.0:
```bash
npm install [email protected]
# or
yarn add [email protected]
```
### 3\. Install dependencies[](#3-install-dependencies)
For a clean installation to ensure codegen works properly:
```bash
# Clean install
rm -rf node_modules
npm install
# iOS
cd ios && rm -rf Pods Podfile.lock && pod install && cd ..
# Rebuild your app
npm run ios
# or
npm run android
```
### 4\. Test your integration[](#4-test-your-integration)
Since there are no public API changes, your existing Customer.io SDK calls will continue to work. However, you should test your app after updating to ensure everything works as expected.
## Troubleshooting[](#troubleshooting)
If you encounter build errors after updating, perform a clean build:
```bash
# Clean install
rm -rf node_modules
npm install
# iOS
cd ios && rm -rf Pods Podfile.lock && pod install && cd ..
# Android
cd android && ./gradlew clean && cd ..
# Rebuild
npm run ios
npm run android
```
If issues persist, ensure your app is properly configured for the React Native new architecture and that all dependencies support it.
 Try our MCP server!
Our MCP server includes an `integration` tool that can help you install and troubleshoot issues with our SDK, including problems with push and in-app notifications. See our [MCP server documentation](/ai/mcp-server/) for more information.
---
## Whats new > Changelog
**Source:** /integrations/sdk/react-native/whats-new/changelog
# Changelog
Check out release history our React Native SDK. Stable releases have been tested thoroughly and are ready for use in your production apps.
#### Need to upgrade?
Select your current version to see all the features and fixes from your version to the latest release.
6.1.06.0.05.3.05.2.05.1.15.1.05.0.15.0.04.11.04.10.04.9.04.8.34.8.24.8.14.8.04.7.04.6.04.5.24.5.14.5.04.4.34.4.24.4.14.4.04.3.14.3.04.2.74.2.64.2.54.2.44.2.34.2.24.2.14.2.04.1.14.1.04.0.24.0.14.0.03.9.13.9.03.8.03.7.23.7.13.7.03.6.03.5.43.5.33.5.23.5.13.5.03.4.03.3.23.3.13.3.03.2.13.2.03.1.133.1.123.1.113.1.103.1.93.1.83.1.73.1.63.1.53.1.43.1.33.1.23.1.13.1.03.0.02.5.12.5.02.4.22.4.12.4.02.3.32.3.22.3.12.3.02.2.12.2.02.1.02.0.12.0.01.0.0
### Breaking Changes
### Features
### Bug Fixes
# 6.x Releases[](#6x-releases)
* * *
* ### 6.1.0[](#610)
January 14, 2026[code changes](https://github.com/customerio/customerio-reactnative/compare/6.0.0...6.1.0)
### Features
* In-app messages now support SSE (Server-Sent Events) as an alternative to polling, reducing latency and improving message delivery efficiency ([#555](https://github.com/customerio/customerio-reactnative/issues/555)) ([a777c69](https://github.com/customerio/customerio-reactnative/commit/a777c6958cc8b48116687a0a521a44fbee4582bd))
* ### 6.0.0[](#600)
December 24, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/5.3.0...6.0.0)
### â BREAKING CHANGES
* Apps must now use React Native new architecture to use this and future versions of the SDK. No public API changes were made, so existing apps already using the new architecture should not be affected. (#553)
### Features
* Support for old React Native architecture has been removed to align with official React Native recommendations ([#553](https://github.com/customerio/customerio-reactnative/issues/553)) ([4956640](https://github.com/customerio/customerio-reactnative/commit/495664072c744d4a2005bce243b2a6b5564d2c0f))
* Added support for lead capture with anonymous messages
# 5.x Releases[](#5x-releases)
* * *
* ### 5.3.0[](#530)
December 4, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/5.2.0...5.3.0)
### Features
* Update example app icons ([#549](https://github.com/customerio/customerio-reactnative/issues/549)) ([5e6f255](https://github.com/customerio/customerio-reactnative/commit/5e6f2552f96c7a385952482354bcbb7921426858))
* ### 5.2.0[](#520)
November 20, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/5.1.1...5.2.0)
### Features
* Exposed `trackMetric` method for manually tracking push notification metrics. This is useful when using multiple push providers or displaying notifications without relying on Customer.io SDK. ([#539](https://github.com/customerio/customerio-reactnative/issues/539)) ([43deefe](https://github.com/customerio/customerio-reactnative/commit/43deefef5afe66161ed954948c5f0388ba79be37))
* ### 5.1.1[](#511)
November 14, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/5.1.0...5.1.1)
### Bug Fixes
* expose in-app listener for expo ([#538](https://github.com/customerio/customerio-reactnative/issues/538)) ([48687f0](https://github.com/customerio/customerio-reactnative/commit/48687f07556645f58c569fd11bea7720494da829))
* ### 5.1.0[](#510)
October 30, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/5.0.1...5.1.0)
### Features
* Updated the SDK and dependencies for Android 16 compatibility, including minor updates to better support newer OS restrictions and behavior changes. ([#533](https://github.com/customerio/customerio-reactnative/issues/533)) ([fd567f3](https://github.com/customerio/customerio-reactnative/commit/fd567f3db1841e0f59a19de00e4449b2b74b9a8d))
### â ï¸ Notes
* Apps now may need to update their `compileSdk` version to `36` and Gradle version to at least `8.9.3` to ensure compatibility with updated dependencies and to successfully build against Android 16.
* ### 5.0.1[](#501)
October 24, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/5.0.0...5.0.1)
### Bug Fixes
* prevent message type crash in release builds ([#531](https://github.com/customerio/customerio-reactnative/issues/531)) ([587012b](https://github.com/customerio/customerio-reactnative/commit/587012b2d2a7035eb035852ba5a2fd4a033f1306))
* ### 5.0.0[](#500)
October 17, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/4.11.0...5.0.0)
### â BREAKING CHANGES
* Add CioFirebaseWrapper to pull in Firebase specific services (#528)
### Features
* Add CioFirebaseWrapper to pull in Firebase specific services ([#528](https://github.com/customerio/customerio-reactnative/issues/528)) ([3c11e2e](https://github.com/customerio/customerio-reactnative/commit/3c11e2e3d800adfc1bf1a1a6d7a2700975370ea4))
# 4.x Releases[](#4x-releases)
* * *
* ### 4.11.0[](#4110)
October 8, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/4.10.0...4.11.0)
### Features
* You can now send banners, modals, pop-ups, and surveys to anonymous visitors âno ID or email required. ([#526](https://github.com/customerio/customerio-reactnative/issues/526)) ([f114d29](https://github.com/customerio/customerio-reactnative/commit/f114d290c6a4c2539b622a483a9c0bc440650a89))
* ### 4.10.0[](#4100)
October 7, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/4.9.0...4.10.0)
### Features
* Improve push notificaiton delivery receipts delay ([#524](https://github.com/customerio/customerio-reactnative/issues/524)) ([0c04cd2](https://github.com/customerio/customerio-reactnative/commit/0c04cd28abbb185de832a78ea92e79bee94efb00))
* ### 4.9.0[](#490)
October 4, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/4.8.3...4.9.0)
### Breaking Features
* Support for Kotlin 2+ is added (#591) (b5f94ff)
but this also means apps needs to have the following minimum requirement
* Gradle: 8.0 or later
* Android Gradle Plugin (AGP): 8.0 or later (8.2+ recommended)
* Kotlin: 1.9.20 or later (2.0+ required if using Kotlin Multiplatform or K2-specific features)
### Features
* Upgrade Kotlin and AGP versions ([#284](https://github.com/customerio/customerio-flutter/issues/284)) ([ed9da81](https://github.com/customerio/customerio-flutter/commit/ed9da81ad05500e07224391e696f725cc75d4b76))
* Added support for queue sticky sessions from latest Android native SDK (customerio/customerio-android#598)
* Align public API with other CIO SDK platforms from latest Android native SDK (customerio/customerio-android#600)
### Bug Fixes
* Resolved a crash when dismissing in app messages using back press during initial loading phase. Users can now safely navigate away from messages without encountering unexpected app crashes. from latest Android native SDK (customerio/customerio-android#608)
* Fix in-app inline tabbed bug (#521)
* ### 4.8.3[](#483)
September 19, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/4.8.2...4.8.3)
### Bug Fixes
* Disabled native logging on `armeabi` devices using new architecture to prevent rare crashes from low level C++ code, no functional impact to end users. ([#516](https://github.com/customerio/customerio-reactnative/issues/516)) ([c406795](https://github.com/customerio/customerio-reactnative/commit/c40679583d85abd6ca0046726f1257fd4dc6d52a))
* ### 4.8.2[](#482)
September 9, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/4.8.1...4.8.2)
### Improvements
* Ensures in-app message event listener is set during module initialization to better support Expo auto initialization and avoid direct React imports that could lead to runtime issues. Behavior remains unchanged for manually initialized setups. ([#513](https://github.com/customerio/customerio-reactnative/issues/513)) ([ad4b169](https://github.com/customerio/customerio-reactnative/commit/ad4b169058699f1143adce029e8647301693502b))
* ### 4.8.1[](#481)
September 5, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/4.8.0...4.8.1)
### Bug Fixes
* Add missing InApp dismissMessage() for legacy RN architecture ([#511](https://github.com/customerio/customerio-reactnative/issues/511)) ([6ab19f5](https://github.com/customerio/customerio-reactnative/commit/6ab19f55048b6ff69fdaaa3f643120f312d09308))
* ### 4.8.0[](#480)
August 27, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/4.7.0...4.8.0)
### Improvements
* Updated Native iOS SDK from `3.11.0` to `3.13.1` which includes following updates: ([#507](https://github.com/customerio/customerio-reactnative/issues/507)) ([e5821d3](https://github.com/customerio/customerio-reactnative/commit/e5821d33abc21a0289f3b3795e1f271c6e51cb62))
* Added support for queue sticky sessions
* Align public API with other CIO SDK platforms
* Fixed build issues on Xcode 26 beta that only affected apps using CocoaPods
* Fixed an issue where custom scheme URLs were not opening when using FCM with `CioAppDelegateWrapper`
* ### 4.7.0[](#470)
August 22, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/4.6.0...4.7.0)
### Features
* Improved logger listener setup to improve performance and reliability on low-end devices ([#493](https://github.com/customerio/customerio-reactnative/pull/493)) ([e6d37e4](https://github.com/customerio/customerio-reactnative/commit/e6d37e4988e7b604d15cbae064a53aceeb2bbb38))
* Added support for optional automatic SDK initialization with Expo plugin, reducing setup complexity ([#504](https://github.com/customerio/customerio-reactnative/issues/504)) ([d3ed7f9](https://github.com/customerio/customerio-reactnative/commit/d3ed7f92f002113919d669a66f4fd4fedc11a56f))
* ### 4.6.0[](#460)
July 29, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/4.5.2...4.6.0)
### âï¸ Utilizing React Native New Architecture
* This release fully adopts React Nativeâs new architecture using Fabric and TurboModules while maintaining compatibility with old architecture. No changes to public API, apps will automatically use appropriate setup based on their configuration. ([#484](https://github.com/customerio/customerio-reactnative/issues/484)) ([2476ee9](https://github.com/customerio/customerio-reactnative/commit/2476ee940d32e6b200c4cbea943413debbe51e75))
* ### 4.5.2[](#452)
July 24, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/4.5.1...4.5.2)
### Fixes and Tooling update
* Support for FCM 12.x is added which requires a minimum iOS deployment target of 15. If youâre using FCM module, ensure your deployment target and tooling are up to date. Or lock Firebase to 11.x to avoid compatibility issues
* Fixes a crash when build attributes from device are nullable (iOS 3.11.0, Android 4.7.1) ([#471](https://github.com/customerio/customerio-reactnative/issues/471)) ([f8b78eb](https://github.com/customerio/customerio-reactnative/commit/f8b78eb63a73d0e7849be1d8322dfd6b8b6f8d1b))
* ### 4.5.1[](#451)
July 21, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/4.5.0...4.5.1)
### Bug Fixes
* Fixed build issue on iOS by adding a default implementation for an internal SPI method in `DeepLinkUtil`, preventing conformance errors with `BUILD_LIBRARY_FOR_DISTRIBUTION = YES` ([#466](https://github.com/customerio/customerio-reactnative/issues/466)) ([ccc149c](https://github.com/customerio/customerio-reactnative/commit/ccc149c17dd6e57881d2d3a864f63cb69958d91a))
* ### 4.5.0[](#450)
July 17, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/4.4.3...4.5.0)
### Features
* Add ability to configure messaging channels for local notifications ([#462](https://github.com/customerio/customerio-reactnative/issues/462)) ([2ddf8f5](https://github.com/customerio/customerio-reactnative/commit/2ddf8f500515e86baf7cb16b9ce56633a47b7c6b))
* ### 4.4.3[](#443)
July 9, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/4.4.2...4.4.3)
### Bug Fixes
* Preserve numeric types when doing sanitization for JSON ([#460](https://github.com/customerio/customerio-reactnative/issues/460)) ([b1e5080](https://github.com/customerio/customerio-reactnative/commit/b1e5080dd1f225d37bafd597b0bab3185d14ead8))
* ### 4.4.2[](#442)
July 4, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/4.4.1...4.4.2)
### Bug Fixes
* Update RN to v0.80 ([#447](https://github.com/customerio/customerio-reactnative/issues/447)) ([bf8ddf3](https://github.com/customerio/customerio-reactnative/commit/bf8ddf311ca3675f7298bdcfb76d9fe38558a3d5))
* ### 4.4.1[](#441)
June 30, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/4.4.0...4.4.1)
### Bug Fixes
* Fixed a bug where apps using older versions of React Native with new architecture could fail to locate native inline wrapper component, causing integration issues on iOS ([#454](https://github.com/customerio/customerio-reactnative/issues/454)) ([557aa0e](https://github.com/customerio/customerio-reactnative/commit/557aa0e9238174e07e497a1a7ec7b77d2a03e9de))
* ### 4.4.0[](#440)
June 26, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/4.3.1...4.4.0)
### Features
* Added support for inline in-app messages. Inline in-app messages act like a part of the content on your page. They let you dynamically populate parts of your app and talk to your customers without interrupting their experience. ([#453](https://github.com/customerio/customerio-reactnative/issues/453)) ([d11041b](https://github.com/customerio/customerio-reactnative/commit/d11041b08f173ec00a718d5f3892ff1000bebb30))
* ### 4.3.1[](#431)
June 2, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/4.3.0...4.3.1)
### Bug Fixes
* Fixed an issue where the SDK enforced a strict version of firebase messaging, preventing integration with newer versions.
* Fixes the bug where multi screen in-app messages might dismiss earlier than intended ([#437](https://github.com/customerio/customerio-reactnative/issues/437)) ([e513bfb](https://github.com/customerio/customerio-reactnative/commit/e513bfb62696bff879c1773f14e73e552f0d0161))
* ### 4.3.0[](#430)
May 30, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/4.2.7...4.3.0)
### Features
* Includes new system for accessing device-token and tracking notifications events. This replaces old Swizzling-based system, in iOS SDK. The change increases stability and improves compatibility with other SDKs (like Firebase) ([#434](https://github.com/customerio/customerio-reactnative/issues/434)) ([f864e89](https://github.com/customerio/customerio-reactnative/commit/f864e892d2d150161567cb5e62c6b912618dc680))
* ### 4.2.7[](#427)
May 9, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/4.2.6...4.2.7)
### Bug Fixes
* Fixes the issues where wrong source of commonjs was picked([#426](https://github.com/customerio/customerio-reactnative/issues/426)) ([0468ddd](https://github.com/customerio/customerio-reactnative/commit/0468ddd96d7d376dc8980c8f1172635f8759bded))
* Fixes the issue where pod install command would fail because they pods different linkage requirement. ([#423](https://github.com/customerio/customerio-reactnative/issues/423)) ([bbd434c](https://github.com/customerio/customerio-reactnative/commit/bbd434c610f0eea17455d9cc806a4faf77c8951d))
* ### 4.2.6[](#426)
April 29, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/4.2.5...4.2.6)
### Bug Fixes
* Fixes the compatibility issues with React Native v0.79 ([7c74c4d](https://github.com/customerio/customerio-reactnative/commit/7c74c4d76c63e671eccc1bc5e24df89243a974f9))
* ### 4.2.5[](#425)
April 28, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/4.2.4...4.2.5)
### Bug Fixes
* Updated author for package published on NPM ([88e4da6](https://github.com/customerio/customerio-reactnative/commit/88e4da65ed117dee221c28779ba02b6958083526))
* ### 4.2.4[](#424)
April 14, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/4.2.3...4.2.4)
### Bug Fixes
* Added support for null handling in nested structures in attributes/traits ([#402](https://github.com/customerio/customerio-reactnative/issues/402)) ([c9d1baf](https://github.com/customerio/customerio-reactnative/commit/c9d1bafd33edca87d70f6384aeeb92fef9752022))
* ### 4.2.3[](#423)
April 3, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/4.2.2...4.2.3)
Updates the iOS native dependency from `3.7.2` to `3.8.1` and Android native dependency from `4.5.3` to `4.5.5` which includes the following improvements.
### Bug Fixes
* \[iOS\] Fixed an issue where the âApplication Installedâ event was incorrectly triggered on every app launch instead of only after the initial installation.
* \[iOS\] Incorrectly scrolling content for in-app modal messages positioned top/bottom. [https://github.com/customerio/customerio-ios/pull/858](https://github.com/customerio/customerio-ios/pull/858)
* \[Android\] Resolved syncing issues for events stored while in battery saver (offline) mode ([https://github.com/customerio/customerio-android/issues/498](https://github.com/customerio/customerio-android/issues/498)) ([6f3d16f](https://github.com/customerio/customerio-android/commit/6f3d16fe01a675cfa522099230baf03650cf9c42))
* \[Android\] Fixed the sequencing of screen tracking events for in-app messaging current screen state ([https://github.com/customerio/customerio-android/issues/500](https://github.com/customerio/customerio-android/issues/500)) ([6877daf](https://github.com/customerio/customerio-android/commit/6877daf98235ce9c96a2ce4932f188efb2c33a71))
* ### 4.2.2[](#422)
February 19, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/4.2.1...4.2.2)
### Bug Fixes
* Android 14 introduced strict rules for when apps are in the killed state, impacting push delivery tracking. This release fixes that.([#386](https://github.com/customerio/customerio-reactnative/issues/386)) ([e8a08a5](https://github.com/customerio/customerio-reactnative/commit/e8a08a511063bc236a7f411de0cabffeb1158907))
* ### 4.2.1[](#421)
January 9, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/4.2.0...4.2.1)
Updates the iOS native dependency from `3.7.1` to `3.7.2` and Android native dependency from `4.5.0` to `4.5.2` which includes the following improvements.
### Bug Fixes
* \[Android\] Fixes the bug where device update/delete events would not migrate automatically when migrating from v3 to v4 ([https://github.com/customerio/customerio-android/issues/481](https://github.com/customerio/customerio-android/issues/481))
* \[Android & iOS\] Fixes in-app messages overlay background color being ignored from in-app message payload ([https://github.com/customerio/customerio-android/issues/485](https://github.com/customerio/customerio-android/issues/485)) ([https://github.com/customerio/customerio-ios/issues/843](https://github.com/customerio/customerio-ios/issues/843))
* ### 4.2.0[](#420)
January 6, 2025[code changes](https://github.com/customerio/customerio-reactnative/compare/4.1.1...4.2.0)
### Features
* Added ability to disable forwarding screen events to destinations/servers. Apps can still send screen events for local processing and use them for page rules in in-app messages by updating SDK configuration during initialization. ([#369](https://github.com/customerio/customerio-reactnative/issues/369)) ([055835e](https://github.com/customerio/customerio-reactnative/commit/055835ebdbb4147152e50d5ed6f1ae5ac18e6fdc))
* ### 4.1.1[](#411)
November 20, 2024[code changes](https://github.com/customerio/customerio-reactnative/compare/4.1.0...4.1.1)
### Bug Fixes
* Resolved compatibility issues with React Native 0.76 on iOS apps ([#359](https://github.com/customerio/customerio-reactnative/issues/359)) ([6fcdd91](https://github.com/customerio/customerio-reactnative/commit/6fcdd91403fea14344d2d293c9184447f516aadf))
* ### 4.1.0[](#410)
November 14, 2024[code changes](https://github.com/customerio/customerio-reactnative/compare/4.0.2...4.1.0)
Updates the iOS Native dependency from 3.5.1 to 3.6.0 which includes the following improvements.
### Features
* This release introduces support for displaying larger in-app messages. ([#356](https://github.com/customerio/customerio-reactnative/issues/356)) ([72bda17](https://github.com/customerio/customerio-reactnative/commit/72bda17c434a41e72a4fa8a53d43fb2ff5dd0256))
### Fixes
* Fixes the push metric for the EU region by adding region support in the MessagingPush config in the notification extension ([https://github.com/customerio/customerio-ios/pull/836](https://github.com/customerio/customerio-ios/pull/836))
### Improvement
* Updated our SDK to use the v2 version of our in-app messages API. This will provide a more reliable experience for in-app messages. ([#834](https://github.com/customerio/customerio-ios/pull/834)) ([#461](https://github.com/customerio/customerio-android/issues/461))
* ### 4.0.2[](#402)
October 25, 2024[code changes](https://github.com/customerio/customerio-reactnative/compare/4.0.1...4.0.2)
Updates the iOS Native dependency from 3.5.0 to 3.5.1 which includes the following improvements.
### Bug Fixes
* Dismisses the keyboard when an in-app message appears on the screen, ensuring uninterrupted user interaction ([#350](https://github.com/customerio/customerio-reactnative/issues/350)) ([74b2379](https://github.com/customerio/customerio-reactnative/commit/74b2379fb435e450b05321bf80ee18bff1d4a1ab))
* ### 4.0.1[](#401)
October 16, 2024[code changes](https://github.com/customerio/customerio-reactnative/compare/4.0.0...4.0.1)
### Improvement
* Updated the workflow to automatically update the Android native SDK version in the package, ensuring greater consistency and reducing manual intervention during updates. ([b477fbc](https://github.com/customerio/customerio-reactnative/commit/b477fbc8e4f742a521289e63d4aafe297a88cde0))
* ### 4.0.0[](#400)
October 16, 2024[code changes](https://github.com/customerio/customerio-reactnative/compare/3.9.1...4.0.0)
### â BREAKING CHANGES
* Data Pipelines Support: Sending your mobile data into our customer data platform (CDP) helps you support Journeys and the rest of your martech stackâanalytics, data warehouses, CRMs, and more. (#349)
> ***NOTE:*** Please follow the [migration guide](https://docs.customer.io/sdk/react-native/whats-new/4.x-upgrade/) for a seamless upgrade to this version.
### Features
* Anonymous tracking: You can send anonymous events, and weâll reconcile anonymous activity with your users when you identify them. ([#349](https://github.com/customerio/customerio-reactnative/issues/349)) ([6665c9f](https://github.com/customerio/customerio-reactnative/commit/6665c9f9c4a8d4f175bae86f0e0294e0dc3b8bd1))
* Built-in lifecycle events: the SDK now automatically captures events like âApplication Installedâ and âApplication Updatedâ for you, so you better understand your usersâ behaviors in your app.
* New device data: The SDK captures complete device-level context, such as your audienceâs screen dimensions, device names, and more.
# 3.x Releases[](#3x-releases)
* * *
* ### 3.9.1[](#391)
October 10, 2024[code changes](https://github.com/customerio/customerio-reactnative/compare/3.9.0...3.9.1)
### Features
* Adds support for the latest versions of the Firebase dependency (>11) ([#344](https://github.com/customerio/customerio-reactnative/issues/344)) ([11d17e1](https://github.com/customerio/customerio-reactnative/commit/11d17e119f003593bc9a2dde7644bb2b3314bd45))
* ### 3.9.0[](#390)
August 28, 2024[code changes](https://github.com/customerio/customerio-reactnative/compare/3.8.0...3.9.0)
Updates the iOS Native dependency from 2.13.2 to 2.14.1 which includes the following improvements.
### Features
* Reduced the time for first in-app message to be shown for newly identified profiles. For new profiles, in-app messages are now fetched as soon as the profile is identified.([#307](https://github.com/customerio/customerio-reactnative/issues/307)) ([cb272c2](https://github.com/customerio/customerio-reactnative/commit/cb272c2ce0b406a58c6ddad951682cc21071fbd5))
### Bug Fixes
* Explicitly switched threads to avoid forcing identify calls to be made on main thread. The SDK now automatically switches to appropriate thread, regardless of the thread used to make identify calls.
* ### 3.8.0[](#380)
July 2, 2024[code changes](https://github.com/customerio/customerio-reactnative/compare/3.7.2...3.8.0)
### Features
* When using page rules and when an in-app messages need a second to load, the in-app message might appear after a user navigates to another page. We made changes to ensure the page-rules are checked after the message is loaded and immediately before itâs displayed in order to resolve this issue. ([#285](https://github.com/customerio/customerio-reactnative/issues/285)) ([478f644](https://github.com/customerio/customerio-reactnative/commit/478f6445fbc5dd2a92ae7ab32955c7fa5f92e812))
* ### 3.7.2[](#372)
June 26, 2024[code changes](https://github.com/customerio/customerio-reactnative/compare/3.7.1...3.7.2)
### Bug Fixes
* This release addresses a compatibility issue for apps that have installed two or more third-party SDKs (besides Customer.io SDK) that handle push notifications. While this issue was primarily reported by our Flutter customers, it could also affect native iOS and React Native applications. ([#283](https://github.com/customerio/customerio-reactnative/issues/283)) ([84d7259](https://github.com/customerio/customerio-reactnative/commit/84d7259045affbc84c00512711c39cee6f7b3d31))
* ### 3.7.1[](#371)
June 13, 2024[code changes](https://github.com/customerio/customerio-reactnative/compare/3.7.0...3.7.1)
### Bug Fixes
* References the [2.13.1](https://github.com/customerio/customerio-ios/releases/tag/2.13.1) version of the iOS SDK. This resolves a compatibility issue with 3rd party FCM Flutter and React Native SDKs. In some cases, the issue prevented push notifications from showing while the app was in the foreground when the 3rd party SDK and CIO SDK were both installed. ([#273](https://github.com/customerio/customerio-reactnative/issues/273)) ([456da1e](https://github.com/customerio/customerio-reactnative/commit/456da1ee961eccaa379cc8e8aee60e4937f6a30d))
* ### 3.7.0[](#370)
April 18, 2024[code changes](https://github.com/customerio/customerio-reactnative/compare/3.6.0...3.7.0)
### Features
* support for android gradle plugin 8 ([#258](https://github.com/customerio/customerio-reactnative/issues/258)) ([3544e66](https://github.com/customerio/customerio-reactnative/commit/3544e6626c90e0fb4f75e9bf7d94ea8e5da73906))
*Note:*
* Android Gradle plugin version 7.4 or later is required.
* JDK 17 is also required for Gradle 8.
* ### 3.6.0[](#360)
April 10, 2024[code changes](https://github.com/customerio/customerio-reactnative/compare/3.5.4...3.6.0)
### Features
* privacy manifest files ([#254](https://github.com/customerio/customerio-reactnative/issues/254)) ([6a7c1f1](https://github.com/customerio/customerio-reactnative/commit/6a7c1f1c33188b15edc54fea60650fb58361a3ff))
* ### 3.5.4[](#354)
April 9, 2024[code changes](https://github.com/customerio/customerio-reactnative/compare/3.5.3...3.5.4)
### Bug Fixes
* added proguard rules for R8 strict mode ([#253](https://github.com/customerio/customerio-reactnative/issues/253)) ([6686206](https://github.com/customerio/customerio-reactnative/commit/6686206b4e08dda711ea304d2fc291f27152b0c6))
* ### 3.5.3[](#353)
March 19, 2024[code changes](https://github.com/customerio/customerio-reactnative/compare/3.5.2...3.5.3)
### Bug Fixes
* do not bundle .md files in cocoapods deployments ([a815336](https://github.com/customerio/customerio-reactnative/commit/a8153361ef08cff75d25e1eb3e4ed1da83fbc30a))
* ### 3.5.2[](#352)
March 5, 2024[code changes](https://github.com/customerio/customerio-reactnative/compare/3.5.1...3.5.2)
### Bug Fixes
* expo users reported app crash on didFailToRegisterForRemoteNotificationsWithError ([#244](https://github.com/customerio/customerio-reactnative/issues/244)) ([fb9a464](https://github.com/customerio/customerio-reactnative/commit/fb9a46438717b34c98c9f356daa9477bb2c7d95f))
* ### 3.5.1[](#351)
February 26, 2024[code changes](https://github.com/customerio/customerio-reactnative/compare/3.5.0...3.5.1)
### Bug Fixes
* edge case for image download in rich push ([#242](https://github.com/customerio/customerio-reactnative/issues/242)) ([04b63f8](https://github.com/customerio/customerio-reactnative/commit/04b63f86a303708585fcc365d465d02321fd1a3e))
* ### 3.5.0[](#350)
February 22, 2024[code changes](https://github.com/customerio/customerio-reactnative/compare/3.4.0...3.5.0)
### Features
* new header for polling interval and enable ios logging for in-app ([#237](https://github.com/customerio/customerio-reactnative/issues/237)) ([f77ff0e](https://github.com/customerio/customerio-reactnative/commit/f77ff0ecccab2d704d283e5e383065164ccd67e0))
* ### 3.4.0[](#340)
February 12, 2024[code changes](https://github.com/customerio/customerio-reactnative/compare/3.3.2...3.4.0)
### Features
* increase opened metrics reliability and 3rd party push SDK compatibility ([#236](https://github.com/customerio/customerio-reactnative/issues/236)) ([514b719](https://github.com/customerio/customerio-reactnative/commit/514b7197b7a252bc835eec2c000ae7669f4fb391))
* ### 3.3.2[](#332)
November 14, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/3.3.1...3.3.2)
### Bug Fixes
* improve android push click behavior ([#203](https://github.com/customerio/customerio-reactnative/issues/203)) ([8b1a836](https://github.com/customerio/customerio-reactnative/commit/8b1a836771811b408779d71362f11ab7a528a04e))
* ### 3.3.1[](#331)
November 8, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/3.3.0...3.3.1)
### Bug Fixes
* **ios:** memory exception during SDK initialization async tasks ([#217](https://github.com/customerio/customerio-reactnative/issues/217)) ([7e420cf](https://github.com/customerio/customerio-reactnative/commit/7e420cfad70e194fa0ae08c4778791480a7f92f3))
* ### 3.3.0[](#330)
November 1, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/3.2.1...3.3.0)
### Features
* fetch currently stored device token using JS method ([#216](https://github.com/customerio/customerio-reactnative/issues/216)) ([482f780](https://github.com/customerio/customerio-reactnative/commit/482f7809866fb32fc0834856f891345d3b7cb25f))
* ### 3.2.1[](#321)
October 27, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/3.2.0...3.2.1)
### Bug Fixes
* **ios:** when queue cannot find task, expect queue runs next task ([#213](https://github.com/customerio/customerio-reactnative/issues/213)) ([fc747a5](https://github.com/customerio/customerio-reactnative/commit/fc747a52072e43e1ef03fccd655671508f720167))
* ### 3.2.0[](#320)
October 25, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/3.1.13...3.2.0)
### Features
* in-app persistant messages ([#210](https://github.com/customerio/customerio-reactnative/issues/210)) ([ea2ed3f](https://github.com/customerio/customerio-reactnative/commit/ea2ed3fd896d8d17b6e9b99f239f8b8a65b5576c))
* ### 3.1.13[](#3113)
October 18, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/3.1.12...3.1.13)
### Bug Fixes
* in-app positioning issue ([#208](https://github.com/customerio/customerio-reactnative/issues/208)) ([465b107](https://github.com/customerio/customerio-reactnative/commit/465b107eb192578ae02a4396746ce16b8c09372f))
* ### 3.1.12[](#3112)
October 11, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/3.1.11...3.1.12)
### Bug Fixes
* remove BQ tasks register device with empty profile identifier ([#207](https://github.com/customerio/customerio-reactnative/issues/207)) ([4cb9cbc](https://github.com/customerio/customerio-reactnative/commit/4cb9cbc9ec061f7129eded83db3e4ea946cf490b))
* ### 3.1.11[](#3111)
September 28, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/3.1.10...3.1.11)
### Bug Fixes
* stack-overflow caused by BQ recursion ([#204](https://github.com/customerio/customerio-reactnative/issues/204)) ([49dba31](https://github.com/customerio/customerio-reactnative/commit/49dba318d9706f50005ccd16aeab3f99d6ad044b))
* ### 3.1.10[](#3110)
September 7, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/3.1.9...3.1.10)
### Bug Fixes
* concurrency issue in-app ([#197](https://github.com/customerio/customerio-reactnative/issues/197)) ([eb2d1fb](https://github.com/customerio/customerio-reactnative/commit/eb2d1fbff2fb17f9b3569f4b843817991de3de7c))
* ### 3.1.9[](#319)
September 5, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/3.1.8...3.1.9)
### Bug Fixes
* added url path encoding ([#194](https://github.com/customerio/customerio-reactnative/issues/194)) ([cd83f4b](https://github.com/customerio/customerio-reactnative/commit/cd83f4bb42160964d53c05c9afa58888791b0f92))
* ### 3.1.8[](#318)
August 28, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/3.1.7...3.1.8)
### Bug Fixes
* include required messagingpush dependency for no push configuration ([#187](https://github.com/customerio/customerio-reactnative/issues/187)) ([78bbc63](https://github.com/customerio/customerio-reactnative/commit/78bbc63035743714c9fc6a3ad7ad046312e20ca6))
* ### 3.1.7[](#317)
July 26, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/3.1.6...3.1.7)
### Bug Fixes
* support json array in attributes ([#180](https://github.com/customerio/customerio-reactnative/issues/180)) ([eb667a6](https://github.com/customerio/customerio-reactnative/commit/eb667a6d70a6a23afda440b42b357f07af00f07b))
* ### 3.1.6[](#316)
July 24, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/3.1.5...3.1.6)
### Bug Fixes
* in-app messages not displaying for release builds on Android ([#174](https://github.com/customerio/customerio-reactnative/issues/174)) ([973d1cc](https://github.com/customerio/customerio-reactnative/commit/973d1cc811c178c75e50a0016342b0e8bc066114))
* ### 3.1.5[](#315)
July 21, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/3.1.4...3.1.5)
### Bug Fixes
* sdk ignores requests when initializing the SDK from react native and from native iOS ([#173](https://github.com/customerio/customerio-reactnative/issues/173)) ([8bc7beb](https://github.com/customerio/customerio-reactnative/commit/8bc7beb338869bd69eb06f60f8f101a8106ee9dc))
* ### 3.1.4[](#314)
July 20, 2023[code changes](https://customer.io/docs/sdk/react-native/push-notifications/push/%29.)
**Note: 3.1.4 contains a known issue when the Customer.io native iOS SDK is initialized in the `AppDelegate` using `CustomerIO.initialize()` or `[pnHandlerObj initializeCioSdk];` as documented in our [React Native push setup documentation](https://customer.io/docs/sdk/react-native/push-notifications/push/). Itâs recommended to use 3.1.3 until a newer version has been released.**
### Bug Fixes
* deinit cleanup repo bad memory access ([#171](https://github.com/customerio/customerio-reactnative/issues/171)) ([25f10b1](https://github.com/customerio/customerio-reactnative/commit/25f10b1c07796cb8931b9865bb05ee2d10cffe3f))
* ### 3.1.3[](#313)
July 14, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/3.1.2...3.1.3)
### Bug Fixes
* hardcode android native SDK version ([#167](https://github.com/customerio/customerio-reactnative/issues/167)) ([be03bd5](https://github.com/customerio/customerio-reactnative/commit/be03bd5dfff7fdf142958c8ddf5f2f9c1ad21e2b))
* ### 3.1.2[](#312)
July 12, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/3.1.1...3.1.2)
### Bug Fixes
* gist migration and crash fix ([#165](https://github.com/customerio/customerio-reactnative/issues/165)) ([01a3074](https://github.com/customerio/customerio-reactnative/commit/01a3074b2e7f1897b55d4e28b825e849e7e1e693))
> Note: Weâve made updates to our [installation instructions for in-app for Android](https://www.customer.io/docs/sdk/android/getting-started/#install). Please refer to them as they reflect our new streamlined process which no longer necessitates a previously required dependency for in-app messages.
* ### 3.1.1[](#311)
July 10, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/3.1.0...3.1.1)
### Bug Fixes
* iOS bad memory access crash ([#164](https://github.com/customerio/customerio-reactnative/issues/164)) ([55b72a7](https://github.com/customerio/customerio-reactnative/commit/55b72a7d128b97eed4577a73132e7c8d32423cd5))
* ### 3.1.0[](#310)
July 5, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/3.0.0...3.1.0)
### Features
* tracking push metrics from js ([#152](https://github.com/customerio/customerio-reactnative/issues/152)) ([6f51703](https://github.com/customerio/customerio-reactnative/commit/6f5170375498142f557870fdbb11104e91a74b83))
* ### 3.0.0[](#300)
July 3, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/2.5.1...3.0.0)
Installing and updating our React Native SDK got easier. Follow our [migration docs](https://customer.io/docs/sdk/react-native/updates-and-troubleshooting/migrate-upgrade/#upgrade-from-2x-to-3x) (it only requires modifications to your `Podfile`) to use version 3 our React Native SDK!
### â BREAKING CHANGES
* auto-update native SDK and easier rich push install (#149)
### Bug Fixes
* auto-update native SDK and easier rich push install ([#149](https://github.com/customerio/customerio-reactnative/issues/149)) ([7e56d1e](https://github.com/customerio/customerio-reactnative/commit/7e56d1e95752fe267b286e9e52b1d11cf0c2fd12))
# 2.x Releases[](#2x-releases)
* * *
* ### 2.5.1[](#251)
July 3, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/2.5.0...2.5.1)
### Bug Fixes
* android opened metrics when app in background ([#156](https://github.com/customerio/customerio-reactnative/issues/156)) ([fb14cce](https://github.com/customerio/customerio-reactnative/commit/fb14ccee755fefa004a4a55e7083d8d237f0a3b8))
* ### 2.5.0[](#250)
June 28, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/2.4.2...2.5.0)
### Features
* delete device token from profile ([#158](https://github.com/customerio/customerio-reactnative/issues/158)) ([0ff0eac](https://github.com/customerio/customerio-reactnative/commit/0ff0eac9e4ef44f9342cadc3b5669da3ec94e7e1))
* ### 2.4.2[](#242)
June 6, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/2.4.1...2.4.2)
### Bug Fixes
* update import from common to CioInternalCommon ([#147](https://github.com/customerio/customerio-reactnative/issues/147)) ([f0382a4](https://github.com/customerio/customerio-reactnative/commit/f0382a4823986be78680ce2dab468b2ea47d66fd))
* ### 2.4.1[](#241)
June 5, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/2.4.0...2.4.1)
### Bug Fixes
* installation breaks due to lefthook in postinstall ([#144](https://github.com/customerio/customerio-reactnative/issues/144)) ([e451443](https://github.com/customerio/customerio-reactnative/commit/e451443c655c7c7c2882778cc4bf05020107196a))
* ### 2.4.0[](#240)
June 1, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/2.3.3...2.4.0)
### Features
* dismiss in-app message ([#138](https://github.com/customerio/customerio-reactnative/issues/138)) ([55d4c62](https://github.com/customerio/customerio-reactnative/commit/55d4c6245b57a7090e0c0157996d2f43874b5297))
* ### 2.3.3[](#233)
May 3, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/2.3.2...2.3.3)
### Bug Fixes
* autoupdate to latest major version of iOS SDK ([#124](https://github.com/customerio/customerio-reactnative/issues/124)) ([7904c50](https://github.com/customerio/customerio-reactnative/commit/7904c5079df06776e603b9741bd8831170724041))
* ### 2.3.2[](#232)
April 20, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/2.3.1...2.3.2)
### Bug Fixes
* push opened metrics tracked on Android 12 ([#119](https://github.com/customerio/customerio-reactnative/issues/119)) ([dfd6fbd](https://github.com/customerio/customerio-reactnative/commit/dfd6fbdc1131b4f0f226480ef0ab8d67b16d4837))
* ### 2.3.1[](#231)
April 20, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/2.3.0...2.3.1)
### Bug Fixes
* typescript definition for optional data attributes ([#51](https://github.com/customerio/customerio-reactnative/issues/51)) ([4cec62a](https://github.com/customerio/customerio-reactnative/commit/4cec62abcf45f14f060d60fb2239c94d50f0a9a9))
* ### 2.3.0[](#230)
April 4, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/2.2.1...2.3.0)
### Features
* process push notifications received outside CIO SDK ([#117](https://github.com/customerio/customerio-reactnative/issues/117)) ([458472d](https://github.com/customerio/customerio-reactnative/commit/458472db12a0e7fa85ed920dc6810bd9697b0902))
* ### 2.2.1[](#221)
March 28, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/2.2.0...2.2.1)
### Bug Fixes
* native push permission status match typescript enum value ([#118](https://github.com/customerio/customerio-reactnative/issues/118)) ([38b7349](https://github.com/customerio/customerio-reactnative/commit/38b7349fbf06dfae16e374bdae7e0780dc153c01))
* ### 2.2.0[](#220)
March 3, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/2.1.0...2.2.0)
### Features
* push permission prompt ([#101](https://github.com/customerio/customerio-reactnative/issues/101)) ([1abe9b3](https://github.com/customerio/customerio-reactnative/commit/1abe9b33f05d125e4180a77207fad23080774550))
* ### 2.1.0[](#210)
February 23, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/2.0.1...2.1.0)
### Features
* deprecated organization id for in-app ([#71](https://github.com/customerio/customerio-reactnative/issues/71)) ([691f324](https://github.com/customerio/customerio-reactnative/commit/691f3240288672d59d915fa6998271591d4fef03))
* in-app event handler ([#89](https://github.com/customerio/customerio-reactnative/issues/89)) ([07e38f7](https://github.com/customerio/customerio-reactnative/commit/07e38f721acb7a613371c2fc3ac6872c3ea8cb38))
### Bug Fixes
* kotlin version in gradle props ([#96](https://github.com/customerio/customerio-reactnative/issues/96)) ([35dceb2](https://github.com/customerio/customerio-reactnative/commit/35dceb293b746845425e977df42e07959df8b60b))
* remove return from init ([#93](https://github.com/customerio/customerio-reactnative/issues/93)) ([d54174b](https://github.com/customerio/customerio-reactnative/commit/d54174bd48586b35033d18c90290e927f0fee970))
* update android sdk to range ([#94](https://github.com/customerio/customerio-reactnative/issues/94)) ([5a5a012](https://github.com/customerio/customerio-reactnative/commit/5a5a012d5c18eb25f54dd2211173ac948c75a989))
* ### 2.0.1[](#201)
January 30, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/2.0.0...2.0.1)
### Bug Fixes
* add register device token ([f865261](https://github.com/customerio/customerio-reactnative/commit/f86526193267692f68291406a61b75c5c700b2b6))
* ### 2.0.0[](#200)
January 18, 2023[code changes](https://github.com/customerio/customerio-reactnative/compare/1.0.0...2.0.0)
### â BREAKING CHANGES
* major iOS version bump to 2.0.0 - Detailed changes and migration guides are available [here](https://customer.io/docs/sdk/react-native/migrate-upgrade/#upgrade-from-1x-to-2x).
### Features
* expo user agent ([#49](https://github.com/customerio/customerio-reactnative/issues/49)) ([377300c](https://github.com/customerio/customerio-reactnative/commit/377300c76d1e8856908c0b231bb8cc2c4b51f97c))
### Bug Fixes
* added support for android sdk 3.2.0 ([c0407be](https://github.com/customerio/customerio-reactnative/commit/c0407bed6df4582be8646ed44662d80a5868ad17))
* bumped ios version to 2.0.0 ([95f74b9](https://github.com/customerio/customerio-reactnative/commit/95f74b9c5e25e7d60e688de11459c83e30b50ae6))
* for test case failure due to file path ([ce43198](https://github.com/customerio/customerio-reactnative/commit/ce431983f45dcb0503e248a36393fff6fb12db15))
* support for expo client ([b641fd8](https://github.com/customerio/customerio-reactnative/commit/b641fd8b94ab023c8a3577dd47ebdf02e431a4d4))
# 1.x Releases[](#1x-releases)
* * *
* ### 1.0.0[](#100)
October 24, 2022[code changes](https://github.com/customerio/customerio-reactnative/issues/13)
### Features
* android updates in package ([#13](https://github.com/customerio/customerio-reactnative/issues/13)) ([b708fce](https://github.com/customerio/customerio-reactnative/commit/b708fcea40ae7a6586742fc39ec1c2e06eb7a559))
* android: package updates ([e87c6eb](https://github.com/customerio/customerio-reactnative/commit/e87c6eb05f5d357a626e982702dc1ff7171e4e09))
* android: setup config with initialization ([e915343](https://github.com/customerio/customerio-reactnative/commit/e915343be1e9967e3d3f5214b2ebcf313486c531))
* creating customerio react native package ([#1](https://github.com/customerio/customerio-reactnative/issues/1)) ([2d2bdae](https://github.com/customerio/customerio-reactnative/commit/2d2bdae0af337a488300e9196dcac07b058fe3d2))
* device attributes and other configurable properties ([#4](https://github.com/customerio/customerio-reactnative/issues/4)) ([bd79d96](https://github.com/customerio/customerio-reactnative/commit/bd79d96ecfd8724e979241d6f58324d26b5e1748))
* identify and clear user identity ([#2](https://github.com/customerio/customerio-reactnative/issues/2)) ([4430b66](https://github.com/customerio/customerio-reactnative/commit/4430b66fc6491a4ba7e1c571eb357a318366af3f))
* in-app functionality in react native package ([488c0c0](https://github.com/customerio/customerio-reactnative/commit/488c0c06b852a220c13c9bbffb3d5b254c7fb9ff))
* update package version ([8526a69](https://github.com/customerio/customerio-reactnative/commit/8526a6979405d19922a18f0bf281298b850f3e27))
* updated to android sdk 3.0.0-alpha.2 ([91978d7](https://github.com/customerio/customerio-reactnative/commit/91978d78cb5b10c90caeb088af2fc4f2a15e952b))
* updating ios sdk version in podspec ([1b1c26f](https://github.com/customerio/customerio-reactnative/commit/1b1c26fdedd14e837df2d117a6ab59a4916dc227))
* user-agent updates in package ([efff4fc](https://github.com/customerio/customerio-reactnative/commit/efff4fc30d3050e410b5e56cdd2464b7c2f6de41))
### Bug Fixes
* added support for android sdk 3.1.0 ([44a1b91](https://github.com/customerio/customerio-reactnative/commit/44a1b91c1ccdedecea526c09ff04654333137daa))
* change in way to update config in ios ([#15](https://github.com/customerio/customerio-reactnative/issues/15)) ([8680b28](https://github.com/customerio/customerio-reactnative/commit/8680b2850cf6f1823514ece8205ac8b354d17c04))
* initialized sdk from storage using context ([e3e609a](https://github.com/customerio/customerio-reactnative/commit/e3e609ae2fee6183050d993624bd4bd287a7ec97))
* push notifications integration ([#10](https://github.com/customerio/customerio-reactnative/issues/10)) ([5d7752d](https://github.com/customerio/customerio-reactnative/commit/5d7752d9732bd591be5153df6c5e46aed615bb04))
* updating gist dependency version ([9f8ac3f](https://github.com/customerio/customerio-reactnative/commit/9f8ac3f1d0ea0320d94f000a91b1bc482eba2b1d))
### Reverts
* update package version ([e02e6e5](https://github.com/customerio/customerio-reactnative/commit/e02e6e55cc569f1434efa523781e6805c27d4e2b))
* [6.x Releases](#6x-releases)
* [6.1](#61x-releases)
* [6.1.0](#610)
* [6.0](#60x-releases)
* [6.0.0](#600)
* [5.x Releases](#5x-releases)
* [5.3](#53x-releases)
* [5.3.0](#530)
* [5.2](#52x-releases)
* [5.2.0](#520)
* [5.1](#51x-releases)
* [5.1.1](#511)
* [5.1.0](#510)
* [5.0](#50x-releases)
* [5.0.1](#501)
* [5.0.0](#500)
* [4.x Releases](#4x-releases)
* [4.11](#411x-releases)
* [4.11.0](#4110)
* [4.10](#410x-releases)
* [4.10.0](#4100)
* [4.9](#49x-releases)
* [4.9.0](#490)
* [4.8](#48x-releases)
* [4.8.3](#483)
* [4.8.2](#482)
* [4.8.1](#481)
* [4.8.0](#480)
* [4.7](#47x-releases)
* [4.7.0](#470)
* [4.6](#46x-releases)
* [4.6.0](#460)
* [4.5](#45x-releases)
* [4.5.2](#452)
* [4.5.1](#451)
* [4.5.0](#450)
* [4.4](#44x-releases)
* [4.4.3](#443)
* [4.4.2](#442)
* [4.4.1](#441)
* [4.4.0](#440)
* [4.3](#43x-releases)
* [4.3.1](#431)
* [4.3.0](#430)
* [4.2](#42x-releases)
* [4.2.7](#427)
* [4.2.6](#426)
* [4.2.5](#425)
* [4.2.4](#424)
* [4.2.3](#423)
* [4.2.2](#422)
* [4.2.1](#421)
* [4.2.0](#420)
* [4.1](#41x-releases)
* [4.1.1](#411)
* [4.1.0](#410)
* [4.0](#40x-releases)
* [4.0.2](#402)
* [4.0.1](#401)
* [4.0.0](#400)
* [3.x Releases](#3x-releases)
* [3.9](#39x-releases)
* [3.9.1](#391)
* [3.9.0](#390)
* [3.8](#38x-releases)
* [3.8.0](#380)
* [3.7](#37x-releases)
* [3.7.2](#372)
* [3.7.1](#371)
* [3.7.0](#370)
* [3.6](#36x-releases)
* [3.6.0](#360)
* [3.5](#35x-releases)
* [3.5.4](#354)
* [3.5.3](#353)
* [3.5.2](#352)
* [3.5.1](#351)
* [3.5.0](#350)
* [3.4](#34x-releases)
* [3.4.0](#340)
* [3.3](#33x-releases)
* [3.3.2](#332)
* [3.3.1](#331)
* [3.3.0](#330)
* [3.2](#32x-releases)
* [3.2.1](#321)
* [3.2.0](#320)
* [3.1](#31x-releases)
* [3.1.13](#3113)
* [3.1.12](#3112)
* [3.1.11](#3111)
* [3.1.10](#3110)
* [3.1.9](#319)
* [3.1.8](#318)
* [3.1.7](#317)
* [3.1.6](#316)
* [3.1.5](#315)
* [3.1.4](#314)
* [3.1.3](#313)
* [3.1.2](#312)
* [3.1.1](#311)
* [3.1.0](#310)
* [3.0](#30x-releases)
* [3.0.0](#300)
* [2.x Releases](#2x-releases)
* [2.5](#25x-releases)
* [2.5.1](#251)
* [2.5.0](#250)
* [2.4](#24x-releases)
* [2.4.2](#242)
* [2.4.1](#241)
* [2.4.0](#240)
* [2.3](#23x-releases)
* [2.3.3](#233)
* [2.3.2](#232)
* [2.3.1](#231)
* [2.3.0](#230)
* [2.2](#22x-releases)
* [2.2.1](#221)
* [2.2.0](#220)
* [2.1](#21x-releases)
* [2.1.0](#210)
* [2.0](#20x-releases)
* [2.0.1](#201)
* [2.0.0](#200)
* [1.x Releases](#1x-releases)
* [1.0](#10x-releases)
* [1.0.0](#100)
---
## Whats new > Update to 3.4
**Source:** /integrations/sdk/react-native/whats-new/update-to-3.4
# 3.x -> 3.4
This page explains how to update your SDK install to latest versions that may not require a breaking change. While these changes arenât breakingâyou donât *need* to make these changesâthey will simplify your integration, improve the reliability of your metrics, and improve deep link handling on iOS devices.
# Upgrade from 3.3 to 3.4+
As of version 3.4, the Customer.io SDK automatically registers push device tokens to identified people and handles push clicks. These features simplify your SDK integration while improving compatibility with apps that use multiple push SDKs.
After you install a version of the SDK that is `3.4` or higher, follow these steps to upgrade.
 Do you have a swift app? Skip ahead!
If youâve got a Swift app containing the `AppDelegate.swift` file, ignore the steps below and go to the [Swift upgrade section](#upgrade-34-swift).
1. Open your push notification handler file (In our examples, we call this file `MyAppPushNotificationsHandler.swift`) and review all of the highlighted code below. Weâve highlighted the most relevant lines.
```swift
import Foundation
import CioMessagingPushAPN
import UserNotifications // Delete this line
import CioTracking
@objc
public class MyAppPushNotificationsHandler : NSObject {
public override init() {}
// Replace these 2 lines
@objc(setupCustomerIOClickHandling:)
public func setupCustomerIOClickHandling(withNotificationDelegate notificationDelegate: UNUserNotificationCenterDelegate) {
// With these 2 lines
@objc(setupCustomerIOClickHandling)
public func setupCustomerIOClickHandling() {
// This line of code is required in order for the Customer.io SDK to handle push notification click events.
// We are working on removing this requirement in a future release.
// Remember to modify the siteId and apiKey with your own values.
// let siteId = "YOUR SITE ID HERE"
// let apiKey = "YOUR API KEY HERE"
CustomerIO.initialize(siteId: siteId, apiKey: apiKey, region: Region.US) { config in
config.autoTrackDeviceAttributes = true
}
// Delete these 2 lines:
let center = UNUserNotificationCenter.current()
center.delegate = notificationDelegate
}
// Delete this function:
@objc(userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:)
public 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()
}
}
}
}
```
2. Open your `AppDelegate.h` file and review all of the highlighted code below.
APN
#### APN[](#APN)
```Objective-C
#import
#import
#import // Delete this line
// Remove `UNUserNotificationCenterDelegate` from this line:
@interface AppDelegate: RCTAppDelegate
// After this change, the line will look like this:
@interface AppDelegate: RCTAppDelegate
@end
```
FCM
#### FCM[](#FCM)
```Objective-C
#import
#import
#import
#import // Delete this line
// Remove `UNUserNotificationCenterDelegate` from this line:
@interface AppDelegate: RCTAppDelegate
// After this change, the line will look like this:
@interface AppDelegate: RCTAppDelegate
@end
```
3. Open your `AppDelegate.m` file and review all of the highlighted code below.
```Objective-C
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
...
// Replace this line
[pnHandlerObj setupCustomerIOClickHandling:self];
// With this line:
[pnHandlerObj setupCustomerIOClickHandling];
return YES;
}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler {
// Remove the line below:
[pnHandlerObj userNotificationCenter:center didReceiveNotificationResponse:response withCompletionHandler:completionHandler];
}
```
4. Now that your appâs code has been simplified, [follow the latest push notification](/integrations/sdk/react-native/push-notifications/push/#integrate-push-capabilities-in-your-app) setup documentation to enable these new features.
### Upgrade from 3.3 to 3.4+, for Swift[](#upgrade-34-swift)
1. Open your `AppDelegate.swift` file and review all of the highlighted code below. Weâve highlighted the most relevant lines.
```swift
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", region: Region.US, configure: nil)
// Delete this line
UIApplication.shared.registerForRemoteNotifications()
return true
}
}
// Delete this function
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
MessagingPush.shared.application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
}
// Delete this function
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
MessagingPush.shared.application(application, didFailToRegisterForRemoteNotificationsWithError: error)
}
```
2. Now that your appâs code has been simplified, itâs time to enable these new SDK features.
To do this, youâll need to initialize the `MessagingPush` module. [Follow the latest push notification setup documentation](/integrations/sdk/react-native/push-notifications/push/#integrate-push-capabilities-in-your-app) to learn how to do this.
---
## Whats new > Update to 3x
**Source:** /integrations/sdk/react-native/whats-new/update-to-3x
# 2.x -> 3.x
This page details breaking changes from previous versions, so you understand the development effort required to update your app and take advantage of the latest features.
## Versioning[](#versioning)
We try to limit breaking or significant changes to major version increments. The three digits in our versioning scheme represent major, minor, and patch increments respectively.
[](#3d36cb047c69017925ca2f80fd8ee72e-lightbox)
* **Major**: may include breaking changes, and generally introduces significant feature updates.
* **Minor**: may include new features and fixes, but wonât include breaking changes. You may still need to do some development to use new features in your app.
* **Patch**: Increments represent minor fixes that should not require development effort.
## Upgrade from 2.x to 3.x[](#upgrade-from-2x-to-3x)
Installing and updating our React Native SDK got easier.
After you install the CustomerIO React Native SDK version 3.x, open your `ios/Podfile` and follow all 5 steps shown in this code block below:
```ruby
# 1. This line is required by the FCM SDK. If you encounter problems during 'pod install', add this line to your Podfile and try 'pod install' again.
use_frameworks! :linkage => :static
target 'YourApp' do # Note: 'YourApp' is unique to your app. This is here for example purposes, only.
# 2. Remove all 'pod CustomerIO...' lines (such as the example below).
pod 'CustomerIO/MessagingPushAPN', '~> 2' # Remove me
# 3. Add one of these new lines below:
# If you use APN for your push notifications on iOS, install the APN pod:
pod 'customerio-reactnative/apn', :path => '../node_modules/customerio-reactnative'
# If you use FCM for your push notifications on iOS, install the FCM pod:
pod 'customerio-reactnative/fcm', :path => '../node_modules/customerio-reactnative'
end
target 'NotificationServiceExtension' do
# 4. Remove all 'pod CustomerIO...' lines (such as the example below).
pod 'CustomerIO/MessagingPushAPN', '~> 2' # Remove me
pod 'FirebaseMessaging' # Remove me, unless you need to specify a specific version
pod 'Firebase' # Remove me, unless you need to specify a specific version.
# 5. Add one of these new lines below:
# â ï¸ Important: Notice these lines of code include "-richpush" in it making it unique to the host app target above.
# If you use APN for your push notifications on iOS, install the APN pod:
pod 'customerio-reactnative-richpush/apn', :path => '../node_modules/customerio-reactnative'
# If you use FCM for your push notifications on iOS, install the FCM pod:
pod 'customerio-reactnative-richpush/fcm', :path => '../node_modules/customerio-reactnative'
end
```
**After you modify your `Podfile`**, run the command `pod update --repo-update --project-directory=ios` to make your changes to `ios/Podfile` go into effect.
## Upgrade from 1.x to 2.x[](#upgrade-from-1x-to-2x)
### Rich push initialization(iOS)[](#rich-push-initializationios)
If you [followed our docs to setup rich push](/integrations/sdk/react-native/rich-push/) in your app, you should have a `Notification Service Extension` file in your code base.
Due to the behavior of Notification Service Extensions in iOS, you need to initialize the Customer.io SDK in your *Notification Service* Extension. In the case that you use Objective-C, you must add the code snippet below into the Swift handler file that you created in *NotificationService* Extension.
```swift
class NotificationService: UNNotificationServiceExtension {
override func didReceive(
_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
) {
// Make sure to initialize the SDK at the top of this function.
CustomerIO.initialize(siteId: "YOUR SITE ID", apiKey: "YOUR API KEY", region: Region.US) { config in
config.autoTrackPushEvents = true
}
...
}
}
```
See [our docs for rich push](/integrations/sdk/react-native/rich-push/) to learn more about rich push setup, SDK initialization, and SDK configuration.
### Firebase users must manually install Firebase dependencies[](#firebase-users-must-manually-install-firebase-dependencies)
We removed all Firebase SDKs as dependencies from the `CustomerIO/MessagingPushFCM` Cocoapod. If you send messages to your iOS app using FCM, youâll need to install the Firebase Cloud Messaging (FCM) dependencies in your `Podfile` on your own.
```ruby
pod 'Firebase'
pod 'FirebaseMessaging'
```
### We fixed a bug in our iOS modules that may impact your data[](#we-fixed-a-bug-in-our-ios-modules-that-may-impact-your-data)
SDK functions that let you send custom dataâ`trackEvent`, `screen`, `identify` and `deviceAttribute` callsâmay have been impacted by a bug in our iOS v1 modules that converted keys in your custom data to `snake_case`. This bug is fixed in v2 of the SDK. You will see your data in Customer.io exactly as you pass it to the SDK.
*This bug didnât surface with all data; it did not affect you if you already snake-cased your data; and it did not affect your Android users.*.
```Javascript
// If you passed in custom attributes using camelCase keys:
data = {"firstName": "Dana"}
// The SDK v1 may have converted this data into:
data = {"first_name": "Dana"}
// Or, if you used a different format that was not snake_case:
data = {"FIRSTNAME": "Dana"}
// The SDK v1 may have converted this data into:
data = {"f_irstname": "Dana"}
```
You donât *need* to do anything before you update. But we strongly recommend that you go to **Data & Integrations** > **Data Index** and audit your *attributes* and *events* to determine if the v1 SDK reshaped your data. Make sure that updating to the 2.x SDK wonât impact your segments, campaigns, etc by sending data in a different (but expected) format to Customer.io.
[](#84dfc357abd5fbfb83be2ed7dbfd96f3-lightbox)
**If your data was affected, you can either:**
1. [(Recommended) Update your attributes, segments, and other information stored in Customer.io to use your original data format.](#v2-option-1)
2. [Set your app to continue using the snake-cased data passed by the 1.x SDK.](#v2-option-2)
#### Option 1 (Recommended): Update your data in Customer.io[](#v2-option-1)
##### For Events: `trackEvent` and `screen` calls[](#for-events)
Unfortunately, you canât modify past events sent by `trackEvent` or `screen` calls. But, before you move forward with the 2.0 SDK, you can can update your segments, campaigns, and other Customer.io assets to use your original, not-reshaped data format.
For [segmentsA group of people who match a series of conditions. People enter and exit the segment automatically when they match or stop matching conditions.](/journeys/data-driven-segments/), you should use **OR** conditions with the bugged, snake-cased format and your preferred data format. This ensures that people enter your segments and campaigns whether they use your app with the 1.x or 2.x SDKs.
[](#772278f8216fb5a3503d7d6ee73a09fe-lightbox)
##### For Attributes: `identify`, `profileAttributes`, and `deviceAttribute` calls[](#for-attributes)
If your customer data was inappropriately snake-cased by the v1 SDK, you can set up a campaign to apply correctly formatted attributes in Customer.io so you donât need to update your app! If you update your data this way, you may still need to update segments and other assets to use the correct data shape.
1. Create a segment of people possessing the affected, snake-cased attributes.
2. Create a campaign using this segment as a trigger.
3. In the workflow, add two a *Create or Update Person* actions.
[](#66ca68af2a7537e609e3b2935c53bdab-lightbox)
4. Configure the first action to set correctly formatted attributes using the values from your previously-misshaped attributes. Use liquid to identify the attributes in question. Use a liquid or JS *if* statement to set an attribute value if it exists, otherwise your campaign may experience errors.
```fallback
{% if customer.snake_case %}{{customer.snake_case}}{% endif %}
```
[](#dee0192f2f57443e1ce3e8835f160e0a-lightbox)
5. Configure the second *Create or Update Person* action to remove the bugged, snake-case attributes from your audience.
[](#321a67ffd5ced3e073f7f3ac761f93ef-lightbox)
6. Make sure that your [segmentsA group of people who match a series of conditions. People enter and exit the segment automatically when they match or stop matching conditions.](/journeys/data-driven-segments/), filters, and other items that might be based on peopleâs attributes or device attributes are all set to use your preferred format.
#### Option 2: Use snake-cased formats in your app[](#v2-option-2)
```Javascript
// Call the Customer.io SDK and provide custom attributes like this:
CustomerIO.identify("[email protected]", {"first_name": "Dana"})
// Consider sending duplicate data with snake_case
CustomerIO.identify("[email protected]", {
"firstName": "Dana", // Attribute used with v1 of the SDK that got converted to snake_case. Keeping it here as the bug has been fixed.
"first_name": "Dana" // Adding this duplicate attribute for backwards compatibility with customers using old versions of your app.
})
```
Then, after you have determined that all of your appâs customers have updated their app to a version of your app no longer using v1 of the Customer.io SDK, you can remove this duplication:
```Javascript
CustomerIO.identify("[email protected]", {
"firstName": "Dana" // We can remove the snake_case attribute and go back to just camelCase!
})
```
---