# Set up push notifications

The Expo plugin supports push notifications over APN for iOS and FCM for Android. Use this page to get started with push notification services.

## How it works[](#how-it-works)

When you use our Expo plugin, you’re already set up to support push notifications, including images and deep links.

Before a device can receive a push notification, you must:

1.  [Identify a person](/integrations/sdk/expo/tracking/identify/). This associates a token with the person; you can’t send push notifications to a device until you identify the recipient.
2.  (Optional) Set up your app to report push metrics back to Customer.io.

## Before you begin[](#before-you-begin)

You need to enable Customer.io to send push notifications through your preferred service: [Apple Push Notification Service (APNs)](/journeys/push-getting-started/#for-ios) and/or [Firebase Cloud Messaging (FCM)](/journeys/push-getting-started/#for-android) before you can send push notifications through Customer.io.

 We don’t support Expo’s Push Service

You can’t use push tokens obtained through [Expo’s Push Service](https://docs.expo.dev/push-notifications/sending-notifications/) to send push notifications through Customer.io. If your app previously used Expo’s push service, you’ll have to obtain new FCM or APN tokens to send push notifications through Customer.io.

Luckily, you don’t need to do anything to support your app’s users when you implement our SDK! If your app can obtain Expo push tokens, it should also be set up to receive APN or FCM device tokens. You’ll simply configure your app to [fetch a token](#fetch-the-current-device-token) and send that token to Customer.io using `CustomerIO.registerDeviceToken("<apn or fcm token here>")`.

## Set up push for Android[](#android-push)

You don’t need to do anything to support Android devices. When you use our Expo plugin, your Android audience is automatically set up to receive push notifications.

## Set up push on iOS[](#register-push)

For the most part, we’ll set up push notifications for you when you run `expo prebuild`. But before you can support push notifications for iOS devices, you need to add Push Notification capabilities to your project in XCode.

1.  Open your React Native project in XCode, go to the `ios` subfolder and open `<yourAppName>.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.
    
    [![add push notification capabilities to your app](https://docs.customer.io/images/react-native-xcode-push.png)](#b837646bba75943a4f08d0fee059210c-lightbox)
    

When you’re done, you’ll see **Push Notifications** added to your app’s capabilities. Now you’re ready to run the `expo prebuild`.

[![this is what it looks like when you've successfully added push notifications to your app](https://docs.customer.io/images/react-native-xcode-push-added.png)](#8a72141293cedd80f2bd0345069d4f43-lightbox)

### Update iOS dependencies[](#update-ios-dependencies)

In some cases, we may make fixes in our iOS push packages that fix downstream issues in the Expo plugin. The command to update these packages depends on whether your Expo project uses a managed or bare workflow. For managed workflows, you can simply re-run the prebuild.

 Managed workflow

#### Managed workflow[](#Managed workflow)

```shell
expo prebuild --clean
# the --clean option is optional
```

 Bare workflow

#### Bare workflow[](#Bare workflow)

```shell
pod update --repo-update --project-directory=ios
```

### EAS Build with Rich Push[](#eas-build-rich-push)

If you use [EAS Build](https://docs.expo.dev/build/introduction/) with `useRichPush: true`, the Customer.io plugin automatically creates a notification service extension during `expo prebuild`. You need to tell EAS Build about this extension by adding the `appExtensions` configuration to your `app.config.js`.

 The `appExtensions` configuration uses an [experimental Expo feature](https://docs.expo.dev/build-reference/app-extensions/). The configuration structure may change in future Expo versions.

```javascript
export default {
  expo: {
    ios: {
      bundleIdentifier: "com.yourcompany.yourapp"
    },
    extra: {
      eas: {
        build: {
          experimental: {
            ios: {
              appExtensions: [
                {
                  targetName: "NotificationService",
                  bundleIdentifier: "com.yourcompany.yourapp" + ".richpush",
                  entitlements: {
                    "com.apple.developer.usernotifications.service": true
                  }
                }
              ]
            }
          }
        }
      }
    }
  }
}
```

**Finding the extension bundle identifier**: After running `expo prebuild`, you can find the exact bundle identifier in your `ios/<YourAppName>.xcworkspace` project. Open the project in Xcode, look for the `NotificationService` target, and check its bundle identifier in the project settings. It should be your main app’s bundle identifier with `.richpush` appended (e.g., `com.yourcompany.yourapp.richpush`).

### Sound in push iOS push notifications[](#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.

## Prompt users to opt-into push notifications[](#prompt-users-to-opt-into-push-notifications)

Your audience has to opt into push notifications. To display native iOS and Android push notification permission prompts, you’ll use the `CustomerIO.pushMessaging.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.pushMessaging.showPromptForPushNotifications` method will return `Granted`, otherwise it returns `Denied` as a `string`. If the user already responded to the push authorization prompt, the current authorization status is returned as a string.

```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)
})
```

### 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[](#test-rich-push)

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

[![the rich push editor](https://docs.customer.io/images/push-preview.png)](#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.
        

 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.
        
    *   data object
        
        Required Contains all properties interpreted by the SDK.
        
    
    *   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.
        
    *   data object
        
        Contains the `link` property (interpreted by the SDK) and additional properties that you want to pass to your app.
        
    *   notification object
        
        Required Contains properties interpreted by the SDK except for the `link`.