UNPKG

expo-notifications

Version:

Provides an API to fetch push notification tokens and to present, schedule, receive, and respond to notifications.

1,021 lines (737 loc) 83.7 kB
<p> <a href="https://docs.expo.dev/versions/latest/sdk/notifications/"> <img src="../../.github/resources/expo-notifications.svg" alt="expo-notifications" height="64" /> </a> </p> Provides an API to fetch push notification tokens and to present, schedule, receive and respond to notifications. ## Features - 📣 schedule a one-off notification for a specific date, or some time from now, - 🔁 schedule a notification repeating in some time interval (or a calendar date match on iOS), - 1️⃣ get and set application badge icon number, - 📲 fetch a native device push token so you can send push notifications with FCM and APNS, - 😎 fetch an Expo push token so you can send push notifications with Expo, - 📬 listen to incoming notifications in the foreground and background, - 👆 listen to interactions with notifications (tapping or dismissing), - 🎛 handle notifications when the app is in foreground, - 🔕 imperatively dismiss notifications from Notification Center/tray, - 🗂 create, update, delete Android notification channels, - 🎨 set custom icon and color for notifications on Android. # Installation in managed Expo projects Please refer to the [installation instructions in the Expo documentation](https://docs.expo.dev/versions/latest/sdk/notifications/#installation). # Installation in bare React Native projects For bare React Native projects, you must ensure that you have [installed and configured the `expo` package](https://docs.expo.dev/bare/installing-expo-modules/) before continuing. ### Add the package to your npm dependencies ``` npx expo install expo-notifications ``` ### Configure for iOS Run `npx pod-install` after installing the npm package. In order to be able to receive push notifications on the device: - open Xcode workspace from your `ios` folder - select your project from the _Navigator_ pane - switch to _Signing & Capabilities_ tab - ensure that the _Push notifications_ capability is present (if it's not, click the "+ Capability" button and add the capability to the project). ### Configure for Android In order to be able to receive push notifications on the device ensure that your project is set up for Firebase. For more information on how to do it, see [this guide](https://docs.expo.dev/guides/using-firebase/#install-and-initialize-react-native-firebase). This module requires permission to subscribe to device boot. It's used to setup the scheduled notifications right after the device (re)starts. The `RECEIVE_BOOT_COMPLETED` permission is added automatically. **Note:** Starting from Android 12 (API level 31), to schedule the notification that triggers at the exact time, you need to add `<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>` to **AndroidManifest.xml**. You can read more about the exact alarm permission [here](https://developer.android.com/about/versions/12/behavior-changes-12#exact-alarm-permission). <details><summary><strong>Expand to view how the notification icon and the default color can be customized in a plain React Native app</strong></summary> <p> - **To customize the icon**: You can customize two icons: the default and the large one. See [the Android documentation](https://developer.android.com/guide/topics/ui/notifiers/notifications#Templates) for more details. The steps for them are very similar. The only difference is the tag in the second step. 1. You will need to ensure that the icon is properly set up and added the project. To read more on how to create a notification icon and add it to the project check out the [“Create notification icon” section](https://developer.android.com/studio/write/image-asset-studio#create-notification) at the official Android guide. Remember the name you use for the icon asset, you will need it later! 2. Then head over to `android/app/src/main/AndroidManifest.xml` and add a `<meta-data>` tag of `android:name="expo.modules.notifications.default_notification_icon"` (or `android:name="expo.modules.notifications.large_notification_icon"` if you are changing the large icon) inside the `<application>` node referencing the custom icon with `@drawable/<notification_icon_name_you_used_in_step_1>`, like [here](https://github.com/expo/expo/blob/335e67a1a3a91598c02061f3318a881541d0d57a/apps/bare-expo/android/app/src/main/AndroidManifest.xml#L44-L46). 3. In the end your `AndroidManifest.xml` should look more or less like this: ```xml <manifest xmlns:android="http://schemas.android.com/apk/res/android" ...> ... <application ...> ... <meta-data android:name="expo.modules.notifications.default_notification_icon" android:resource="@drawable/ic_stat_notifications" /> <!-- @drawable/<insert_notification_icon_name> --> ... </application> </manifest> ``` - **To customize the default color of the notification**: 1. you will need a color resource added to the native project's resources. Some information on how to do this can be found in [the official Android guide](https://developer.android.com/guide/topics/resources/more-resources#Color). The most simple and fail-safe instructions would be to: 1. ensure that there is a file under `android/app/src/main/res/values/colors.xml` (if there is none, create it) 2. ensure that it's a valid resources XML file (it should start with a `<?xml version="1.0" encoding="utf-8"?>` declaration and have a root node of `<resources>`) 3. inside the `<resources>` node add a `<color>` node with an arbitrary name (like `notification_icon_color`) containing the color in HEX format inside, like [here](https://github.com/expo/expo/blob/335e67a1a3a91598c02061f3318a881541d0d57a/apps/bare-expo/android/app/src/main/res/values/colors.xml#L3). 4. in the end your `colors.xml` should look more or less like this: ```java <?xml version="1.0" encoding="utf-8"?> <resources> <color name="notification_icon_color">#4630EB</color> </resources> ``` 2. now, when the color is added to the project, we need to configure `expo-notifications` to use it when it displays a notification — head over to `android/app/src/main/AndroidManifest.xml` and add a `<meta-data>` tag of `android:name="expo.modules.notifications.default_notification_color"` inside the `<application>` node referencing the custom icon with `@color/<notification_icon_color_name>`, like [here](https://github.com/expo/expo/blob/335e67a1a3a91598c02061f3318a881541d0d57a/apps/bare-expo/android/app/src/main/AndroidManifest.xml#L47-L49). 3. In the end your `AndroidManifest.xml` should look more or less like this: ```xml <manifest xmlns:android="http://schemas.android.com/apk/res/android" ...> ... <application ...> ... <meta-data android:name="expo.modules.notifications.default_notification_color" android:resource="@color/notification_icon_color" /> <!-- @color/<insert_notification_icon_color_name> --> ... </application> </manifest> ``` - An `AndroidManifest.xml` with both color (of name `notification_icon_color`) and an icon (of name `ic_stat_notifications`) name would look like this: ```xml <manifest xmlns:android="http://schemas.android.com/apk/res/android" ...> <application ...> ... <meta-data android:name="expo.modules.notifications.default_notification_icon" android:resource="@drawable/ic_stat_notifications" /> <meta-data android:name="expo.modules.notifications.default_notification_color" android:resource="@color/notification_icon_color" /> ... </application> </manifest> ``` </p> </details> ### Config plugin setup (optional) If you're using EAS Build, you can set your Android notification icon and color tint, add custom push notification sounds, and set your iOS notification environment using the expo-notifications config plugin ([what's a config plugin?](https://docs.expo.dev/config-plugins/introduction)). To setup, just add the config plugin to the plugins array of your `app.json` or `app.config.js` as shown below, then rebuild the app. ```json { "expo": { ... "plugins": [ [ "expo-notifications", { "icon": "./local/path/to/myNotificationIcon.png", "color": "#ffffff", "sounds": ["./local/path/to/mySound.wav", "./local/path/to/myOtherSound.wav"], "mode": "production" } ] ], } } ``` <details><summary><strong>Expand to view property descriptions and default values</strong></summary> <p> - **icon**: Android only. Local path to an image to use as the icon for push notifications. 96x96 all-white png with transparency. - **color**: Android only. Tint color for the push notification image when it appears in the notification tray. Default: "#ffffff". - **sounds**: Array of local paths to sound files (.wav recommended) that can be used as custom notification sounds. - **mode**: iOS only. Environment of the app: either 'development' or 'production'. Default: 'development'. </p> </details> ### Add your project's credentials to Expo server (optional) If you would like to send notifications with Expo servers, the servers will need to have a way to authenticate with APNS/FCM that they are authorized to send notifications on your behalf. To do this: - for Firebase Cloud Messaging, check out this guide: _[Uploading Server Credentials](https://docs.expo.dev/push-notifications/using-fcm/#uploading-server-credentials)_, - for APNS: - run `expo credentials:manager` in the root of your application, - if you've already uploaded a Push Notifications Key in another project and would like to reuse it in the current project, select _Use existing Push Notifications Key in current project_ (you may need to set `slug` and `ios.bundleIdentifier` fields in `app.json` so that the server knows to which `experienceId` and `bundleIdentifier` the key should be attributed), - if you've never uploaded a Push Notifications Key or would like to add a new one - select _Add new Push Notifications Key_ - if you'd like to let Expo handle the process, select _Let Expo handle the process_ - if you can't let Expo handle the process or you want to upload your own key, select _I want to upload my own file_ - provide a path to the P8 file you have downloaded from [developer.apple.com](https://developer.apple.com/) website. # Common gotchas / known issues ### Fetching a push token takes a long time on iOS `getDevicePushTokenAsync` and `getExpoPushTokenAsync` can sometimes take a long time to resolve on iOS. This is outside of `expo-notifications`'s control, as stated in Apple's [“Troubleshooting Push Notifications” technical note](https://developer.apple.com/library/archive/technotes/tn2265/_index.html): > This is not necessarily an error condition. The system may not have Internet connectivity at all because it is out of range of any cell towers or Wi-Fi access points, or it may be in airplane mode. Instead of treating this as an error, your app should continue normally, disabling only that functionality that relies on push notifications. As mentioned, the most common reasons for this this are either an invalid Internet connection (fetching a push token requires an Internet connection to register the device with the service provider) or an invalid configuration of your App ID or Provisioning Profile. Here are a few ways people claim to have solved this problem, maybe one of these will help you solve it, too! <details><summary><strong>Read the Apple's <a href="https://developer.apple.com/library/archive/technotes/tn2265/_index.html">Technical Note on troubleshooting push notifications</a></strong></summary> <p> Go read the Apple's [Technical Note on troubleshooting push notifications](https://developer.apple.com/library/archive/technotes/tn2265/_index.html)! This the single most reliable source of information on this problem. To help you grasp what they're suggesting: - Make sure the device has a reliable connection to the Internet (try turning off Wi-Fi or switching to another network, and disabling firewall block on port 5223, as suggested in [this SO answer](https://stackoverflow.com/a/34332047/1123156)). - Make sure your app configuration is set properly for registering for push notifications (for bare workflow check out [this guide](https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/AppDistributionGuide/AddingCapabilities/AddingCapabilities.html), for managed workflow this is done automatically for you by `expo-cli`) as also suggested by [this StackOverflow answer](https://stackoverflow.com/a/10791240/1123156). - If you're in bare workflow you may want to try to debug this even further by logging persistent connection debug information as outlined by [this StackOverflow answer](https://stackoverflow.com/a/8036052/1123156). </p> </details> <details><summary><strong>Try again in a little while</strong></summary> <p> - APNS servers near the device may be down as indicated by [this forum thread](https://developer.apple.com/forums/thread/52224). Take a walk and try again later! - Try again in a few days time as suggested by [this GitHub comment](https://github.com/expo/expo/issues/10369#issuecomment-717872956). </p> </details> <details><summary><strong>Disable network sharing on your device</strong></summary> <p> You may need to disable network sharing as this may impact the registration as suggested by [this StackOverflow answer](https://stackoverflow.com/a/59156989/1123156). </p> </details> <details><summary><strong>Restart your device</strong></summary> <p> If you just changed the APNS servers where the app should be registering (by installing a TestFlight build over an Xcode build on the same device) you may need to restart your device as suggested by [this StackOverflow answer](https://stackoverflow.com/a/59864028/1123156). </p> </details> <details><summary><strong>Setup your device with a SIM card</strong></summary> <p> If the device you're experiencing this on hasn't been setup with a SIM card it looks like configuring it may help mitigate this bug as suggested by [this StackOverflow answer](https://stackoverflow.com/a/19432504/1123156). </p> </details> ### Setting custom notification sounds Custom notification sounds are only supported when using [EAS Build](https://docs.expo.dev/build/introduction/), or in the bare workflow. To add custom push notification sounds to your app, add the `expo-notifications` plugin to your `app.json` file: ```json { "expo": { "plugins": [ [ "expo-notifications", { "sounds": ["local/path/to/mySoundFile.wav"] } ] ] } } ``` After building your app, the array of files will be available for use in both [`NotificationContentInput`](#notificationcontentinput) and [`NotificationChannelInput`](#notificationchannelinput). You _only_ need to provide the base filename- here's an example using the config above: ```ts await Notifications.setNotificationChannelAsync('new-emails', { name: 'E-mail notifications', sound: 'mySoundFile.wav', // Provide ONLY the base filename }); await Notifications.scheduleNotificationAsync({ content: { title: "You've got mail! 📬", sound: 'mySoundFile.wav', // Provide ONLY the base filename }, trigger: { seconds: 2, channelId: 'new-emails', }, }); ``` You can also manually add notification files to your Android and iOS projects if you prefer: <details><summary><strong>Manually adding notification sounds on Android</strong></summary> <p> On Androids 8.0+, playing a custom sound for a notification requires more than setting the `sound` property on the `NotificationContentInput`. You will _also_ need to configure the `NotificationChannel` with the appropriate `sound`, and use it when sending/scheduling the notification. For the example below to work, you would place your `email-sound.wav` file in `android/app/src/main/res/raw/`. ```ts // Prepare the notification channel await Notifications.setNotificationChannelAsync('new-emails', { name: 'E-mail notifications', importance: Notifications.AndroidImportance.HIGH, sound: 'email-sound.wav', // <- for Android 8.0+, see channelId property below }); // Eg. schedule the notification await Notifications.scheduleNotificationAsync({ content: { title: "You've got mail! 📬", body: 'Open the notification to read them all', sound: 'email-sound.wav', // <- for Android below 8.0 }, trigger: { seconds: 2, channelId: 'new-emails', // <- for Android 8.0+, see definition above }, }); ``` </p> </details> <details><summary><strong>Manually adding notification sounds on iOS</strong></summary> <p> On iOS, all that's needed is to place your sound file in your Xcode project, and then specify the sound file in your `NotificationContentInput`, like this: ```ts await Notifications.scheduleNotificationAsync({ content: { title: "You've got mail! 📬", body: 'Open the notification to read them all', sound: 'notification.wav', }, trigger: { // ... }, }); ``` </p> </details> # Contributing Contributions are very welcome! Please refer to guidelines described in the [contributing guide](https://github.com/expo/expo#contributing). --- # API The following methods are exported by the `expo-notifications` module: - **fetching token for sending push notifications** - [`getExpoPushTokenAsync`](#getexpopushtokenasyncoptions-expotokenoptions-expopushtoken) -- resolves with an Expo push token - [`getDevicePushTokenAsync`](#getdevicepushtokenasync-devicepushtoken) -- resolves with a device push token - [`addPushTokenListener`](#addpushtokenlistenerlistener-pushtokenlistener-subscription) -- adds a listener called when a new push token is issued - [`removePushTokenSubscription`](#removepushtokensubscriptionsubscription-subscription-void) -- removes the listener registered with `addPushTokenListener` - **listening to notification events** - [`useLastNotificationResponse`](#uselastnotificationresponse-undefined--notificationresponse--null) -- a React hook returning the most recently received notification response - [`addNotificationReceivedListener`](#addnotificationreceivedlistenerlistener-event-notification--void-void) -- adds a listener called whenever a new notification is received - [`addNotificationsDroppedListener`](#addnotificationsdroppedlistenerlistener---void-void) -- adds a listener called whenever some notifications have been dropped - [`addNotificationResponseReceivedListener`](#addnotificationresponsereceivedlistenerlistener-event-notificationresponse--void-void) -- adds a listener called whenever user interacts with a notification - [`removeNotificationSubscription`](#removenotificationsubscriptionsubscription-subscription-void) -- removes the listener registered with `addNotification*Listener()` - **handling incoming notifications when the app is in foreground** - [`setNotificationHandler`](#setnotificationhandlerhandler-notificationhandler--null-void) -- sets the handler function responsible for deciding what to do with a notification that is received when the app is in foreground - **fetching permissions information** - [`getPermissionsAsync`](#getpermissionsasync-promisenotificationpermissionsstatus) -- fetches current permission settings related to notifications - [`requestPermissionsAsync`](#requestpermissionsasyncrequest-notificationpermissionsrequest-promisenotificationpermissionsstatus) -- requests permissions related to notifications - **managing application badge icon** - [`getBadgeCountAsync`](#getbadgecountasync-promisenumber) -- fetches the application badge number value - [`setBadgeCountAsync`](#setbadgecountasyncbadgecount-number-options-setbadgecountoptions-promiseboolean) -- sets the application badge number value - **scheduling notifications** - [`getAllScheduledNotificationsAsync`](#getallschedulednotificationsasync-promisenotification) -- fetches information about all scheduled notifications - [`presentNotificationAsync`](#presentnotificationasynccontent-notificationcontentinput-identifier-string-promisestring) -- schedules a notification for immediate trigger - [`scheduleNotificationAsync`](#schedulenotificationasyncnotificationrequest-notificationrequestinput-promisestring) -- schedules a notification to be triggered in the future - [`cancelScheduledNotificationAsync`](#cancelschedulednotificationasyncidentifier-string-promisevoid) -- removes a specific scheduled notification - [`cancelAllScheduledNotificationsAsync`](#cancelallschedulednotificationsasync-promisevoid) -- removes all scheduled notifications - [`getNextTriggerDateAsync`](#getnexttriggerdateasynctrigger-schedulablenotificationtriggerinput-promisenumber--null) -- calculates next trigger date for a notification trigger - **dismissing notifications** - [`getPresentedNotificationsAsync`](#getpresentednotificationsasync-promisenotification) -- fetches information about all notifications present in the notification tray (Notification Center) - [`dismissNotificationAsync`](#dismissnotificationasyncidentifier-string-promisevoid) -- removes a specific notification from the notification tray - [`dismissAllNotificationsAsync`](#dismissallnotificationsasync-promisevoid) -- removes all notifications from the notification tray - **managing notification channels (Android-specific)** - [`getNotificationChannelsAsync`](#getnotificationchannelsasync-promisenotificationchannel) -- fetches information about all known notification channels - [`getNotificationChannelAsync`](#getnotificationchannelasyncidentifier-string-promisenotificationchannel--null) -- fetches information about a specific notification channel - [`setNotificationChannelAsync`](#setnotificationchannelasyncidentifier-string-channel-notificationchannelinput-promisenotificationchannel--null) -- saves a notification channel configuration - [`deleteNotificationChannelAsync`](#deletenotificationchannelasyncidentifier-string-promisevoid) -- deletes a notification channel - [`getNotificationChannelGroupsAsync`](#getnotificationchannelgroupsasync-promisenotificationchannelgroup) -- fetches information about all known notification channel groups - [`getNotificationChannelGroupAsync`](#getnotificationchannelgroupasyncidentifier-string-promisenotificationchannelgroup--null) -- fetches information about a specific notification channel group - [`setNotificationChannelGroupAsync`](#setnotificationchannelgroupasyncidentifier-string-channel-notificationchannelgroupinput-promisenotificationchannelgroup--null) -- saves a notification channel group configuration - [`deleteNotificationChannelGroupAsync`](#deletenotificationchannelgroupasyncidentifier-string-promisevoid) -- deletes a notification channel group - **managing notification categories (interactive notifications)** - [`setNotificationCategoryAsync`](#setnotificationcategoryasyncidentifier-string-actions-notificationaction-options-categoryoptions-promisenotificationcategory--null) -- creates a new notification category for interactive notifications - [`getNotificationCategoriesAsync`](#getnotificationcategoriesasync-promisenotificationcategory) -- fetches information about all active notification categories - [`deleteNotificationCategoryAsync`](#deletenotificationcategoryasyncidentifier-string-promiseboolean) -- deletes a notification category ## Custom notification icon and colors (Android only) Setting a default icon and color for all of your app's notifications is almost too easy. In the managed workflow, just set your [`notification.icon`](https://docs.expo.dev/versions/latest/config/app/#notification) and [`notification.color`](https://docs.expo.dev/versions/latest/config/app/#notification) keys in `app.json`, and rebuild your app! In the bare workflow, you'll need to follow [these instructions](https://github.com/expo/expo/tree/main/packages/expo-notifications#configure-for-android). For your notification icon, make sure you follow [Google's design guidelines](https://material.io/design/iconography/product-icons.html#design-principles) (the icon must be all white with a transparent background) or else it may not be displayed as intended. In both the managed and bare workflow, you can also set a custom notification color _per-notification_ directly in your [`NotificationContentInput`](#notificationcontentinput) under the `color` attribute. ## Android push notification payload specification When sending a push notification, put an object conforming to the following type as `data` of the notification: ```ts export interface FirebaseData { title?: string; message?: string; subtitle?: string; sound?: boolean | string; vibrate?: boolean | number[]; priority?: AndroidNotificationPriority; badge?: number; } ``` ## Fetching tokens for push notifications ### `getExpoPushTokenAsync(options: ExpoTokenOptions): ExpoPushToken` Returns an Expo token that can be used to send a push notification to this device using Expo push notifications service. [Read more in the Push Notifications guide](https://docs.expo.dev/guides/push-notifications/). > **Note:** For Expo's backend to be able to send notifications to your app, you will need to provide it with push notification keys. This can be done using `expo-cli` (`expo credentials:manager`). [Read more in the “Upload notifications credentials” guide](https://docs.expo.dev/push-notifications/push-notifications-setup/#credentials). > **Note:** Especially on iOS, `Promise`s returned by this method may take longer periods of time to fulfill. For more information see [Fetching a push token takes a long time on iOS](#fetching-a-push-token-takes-a-long-time-on-ios). #### Arguments This function accepts an optional object allowing you to pass in configuration, consisting of fields (all are optional, but some may have to be defined if configuration cannot be inferred): - **experienceId (_string_)** -- The ID of the experience to which the token should be attributed. Defaults to [`Constants.manifest.id`](https://docs.expo.dev/versions/latest/sdk/constants/#constantsmanifest) exposed by `expo-constants`. In the bare workflow, you must provide a value which takes the shape `@username/projectSlug`, where `username` is the Expo account that the project is associated with, and `projectSlug` is your [`slug` from `app.json`](https://docs.expo.dev/versions/latest/config/app/#slug). - **devicePushToken ([_DevicePushToken_](#devicepushtoken))** -- The device push token with which to register at the backend. Defaults to a token fetched with [`getDevicePushTokenAsync()`](#getdevicepushtokenasync-devicepushtoken). - **applicationId (_string_)** -- The ID of the application to which the token should be attributed. Defaults to [`Application.applicationId`](https://docs.expo.dev/versions/latest/sdk/application/#applicationapplicationid) exposed by `expo-application`. - **development (_boolean_)** -- Makes sense only on iOS, where there are two push notification services: sandbox and production. This defines whether the push token is supposed to be used with the sandbox platform notification service. Defaults to [`Application.getIosPushNotificationServiceEnvironmentAsync()`](https://docs.expo.dev/versions/latest/sdk/application/#applicationgetiospushnotificationserviceenvironmentasync) exposed by `expo-application` or `false`. Most probably you won't need to customize that. You may want to customize that if you don't want to install `expo-application` and still use the sandbox APNS. #### Returns Returns a `Promise` that resolves to an object with the following fields: - **type (_string_)** -- Always `expo`. - **data (_string_)** -- The push token as a string. #### Examples ##### Fetching the Expo push token and uploading it to a server ```ts import Constants from 'expo-constants'; import * as Notifications from 'expo-notifications'; export async function registerForPushNotificationsAsync(userId: string) { let experienceId = undefined; if (!Constants.manifest) { // Absence of the manifest means we're in bare workflow experienceId = '@username/example'; } const expoPushToken = await Notifications.getExpoPushTokenAsync({ experienceId, }); await fetch('https://example.com/', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ userId, expoPushToken, }), }); } ``` ### `getDevicePushTokenAsync(): DevicePushToken` Returns a native APNS, FCM token or a [`PushSubscription` data](https://developer.mozilla.org/en-US/docs/Web/API/PushSubscription) that can be used with another push notification service. > **Note:** Especially on iOS, `Promise`s returned by this method may take longer periods of time to fulfill. For more information see [Fetching a push token takes a long time on iOS](#fetching-a-push-token-takes-a-long-time-on-ios) section of the documentation. #### Returns A `Promise` that resolves to an object with the following fields: - **type (_string_)** -- Either `ios`, `android` or `web`. - **data (_string_ or _object_)** -- Either the push token as a string (for `type == "ios" | "android"`) or an object conforming to the type below (for `type == "web"`): ```ts { endpoint: string; keys: { p256dh: string; auth: string; } } ``` ### `addPushTokenListener(listener: PushTokenListener): Subscription` In rare situations a push token may be changed by the push notification service while the app is running. When a token is rolled, the old one becomes invalid and sending notifications to it will fail. A push token listener will let you handle this situation gracefully by registering the new token with your backend right away. #### Arguments A single and required argument is a function accepting a push token as an argument. It will be called whenever the push token changes. #### Returns A [`Subscription`](#subscription) object representing the subscription of the provided listener. #### Examples Registering a push token listener using a React hook ```tsx import React from 'react'; import * as Notifications from 'expo-notifications'; import { registerDevicePushTokenAsync } from '../api'; export default function App() { React.useEffect(() => { const subscription = Notifications.addPushTokenListener(registerDevicePushTokenAsync); return () => subscription.remove(); }, []); return ( // Your app content ); } ``` ### `removePushTokenSubscription(subscription: Subscription): void` Removes a push token subscription returned by a `addPushTokenListener` call. #### Arguments A single and required argument is a subscription returned by `addPushTokenListener`. ## Listening to notification events Notification events include incoming notifications, interactions your users perform with notifications (this can be tapping on a notification, or interacting with it via [notification categories](#manage-notification-categories-interactive-notifications)), and rare occasions when your notifications may be dropped. A few different listeners are exposed, so we've provided a chart below which will hopefully help you understand when you can expect each one to be triggered: | User interacted with notification? | App state | Listener(s) triggered | | :--------------------------------- | :--------: | ----------------------------------------------------------------------- | | false | Foreground | `NotificationReceivedListener` | | false | Background | `BackgroundNotificationTask` | | false | Killed | none | | true | Foreground | `NotificationReceivedListener` & `NotificationResponseReceivedListener` | | true | Background | `NotificationResponseReceivedListener` | | true | Killed | `NotificationResponseReceivedListener` | > In the chart above, whenever `NotificationResponseReceivedListener` is triggered, the same would apply to the `useLastNotificationResponse` hook. ### `useLastNotificationResponse(): undefined | NotificationResponse | null` A React hook always returning the notification response that was received most recently (a notification response designates an interaction with a notification, such as tapping on it). > If you don't want to use a hook, you can use `Notifications.getLastNotificationResponseAsync()` instead. #### Returns The hook may return one of these three types/values: - `undefined` -- until we're sure of what to return - `null` -- if no notification response has been received yet - a [`NotificationResponse`](#notificationresponse) object -- if a notification response was received #### Examples Responding to a notification tap by opening a URL that could be put into the notification's `data` (opening the URL is your responsibility and is not a part of the `expo-notifications` API): ```ts import * as Notifications from 'expo-notifications'; import { Linking } from 'react-native'; export default function App() { const lastNotificationResponse = Notifications.useLastNotificationResponse(); React.useEffect(() => { if ( lastNotificationResponse && lastNotificationResponse.notification.request.content.data.url && lastNotificationResponse.actionIdentifier === Notifications.DEFAULT_ACTION_IDENTIFIER ) { Linking.openURL(lastNotificationResponse.notification.request.content.data.url); } }, [lastNotificationResponse]); return ( /* * your app */ ); } ``` ### `addNotificationReceivedListener(listener: (event: Notification) => void): void` Listeners registered by this method will be called whenever a notification is received while the app is running. #### Arguments A single and required argument is a function accepting a notification ([`Notification`](#notification)) as an argument. #### Returns A [`Subscription`](#subscription) object representing the subscription of the provided listener. #### Examples Registering a notification listener using a React hook ```tsx import React from 'react'; import * as Notifications from 'expo-notifications'; export default function App() { React.useEffect(() => { const subscription = Notifications.addNotificationReceivedListener(notification => { console.log(notification); }); return () => subscription.remove(); }, []); return ( // Your app content ); } ``` ### `addNotificationsDroppedListener(listener: () => void): void` Listeners registered by this method will be called whenever some notifications have been dropped by the server. Applicable only to Firebase Cloud Messaging which we use as notifications service on Android. It corresponds to `onDeletedMessages()` callback. [More information can be found in Firebase docs](https://firebase.google.com/docs/cloud-messaging/android/receive#override-ondeletedmessages). #### Arguments A single and required argument is a function–callback. #### Returns A [`Subscription`](#subscription) object representing the subscription of the provided listener. ### `addNotificationResponseReceivedListener(listener: (event: NotificationResponse) => void): void` Listeners registered by this method will be called whenever a user interacts with a notification (eg. taps on it). #### Arguments A single and required argument is a function accepting notification response ([`NotificationResponse`](#notificationresponse)) as an argument. #### Returns A [`Subscription`](#subscription) object representing the subscription of the provided listener. #### Examples ##### Registering a notification listener using a React hook ```tsx import React from 'react'; import { Linking } from 'react-native'; import * as Notifications from 'expo-notifications'; export default function Container() { React.useEffect(() => { const subscription = Notifications.addNotificationResponseReceivedListener(response => { const url = response.notification.request.content.data.url; Linking.openUrl(url); }); return () => subscription.remove(); }, []); return ( // Your app content ); } ``` ##### Handling push notifications with React Navigation If you'd like to deep link to a specific screen in your app when you receive a push notification, you can configure React Navigation's [linking](https://reactnavigation.org/docs/navigation-container#linking) prop to do that: ```tsx import React from 'react'; import { Linking } from 'react-native'; import * as Notifications from 'expo-notifications'; import { NavigationContainer } from '@react-navigation/native'; export default function App() { return ( <NavigationContainer linking={{ config: { // Configuration for linking }, subscribe(listener) { const onReceiveURL = ({ url }: { url: string }) => listener(url); // Listen to incoming links from deep linking Linking.addEventListener('url', onReceiveURL); // Listen to expo push notifications const subscription = Notifications.addNotificationResponseReceivedListener((response) => { const url = response.notification.request.content.data.url; // Any custom logic to see whether the URL needs to be handled //... // Let React Navigation handle the URL listener(url); }); return () => { // Clean up the event listeners Linking.removeEventListener('url', onReceiveURL); subscription.remove(); }; }, }}> {/* Your app content */} </NavigationContainer> ); } ``` See more details on [React Navigation documentation](https://reactnavigation.org/docs/deep-linking/#third-party-integrations). ### `removeNotificationSubscription(subscription: Subscription): void` Removes a notification subscription returned by a `addNotification*Listener` call. #### Arguments A single and required argument is a subscription returned by `addNotification*Listener`. ## Handling incoming notifications when the app is in foreground ### `setNotificationHandler(handler: NotificationHandler | null): void` When a notification is received while the app is running, using this function you can set a callback that will decide whether the notification should be shown to the user or not. When a notification is received, `handleNotification` is called with the incoming notification as an argument. The function should respond with a behavior object within 3 seconds, otherwise the notification will be discarded. If the notification is handled successfully, `handleSuccess` is called with the identifier of the notification, otherwise (or on timeout) `handleError` will be called. The default behavior when the handler is not set or does not respond in time is not to show the notification. #### Arguments The function receives a single argument which should be either `null` (if you want to clear the handler) or an object of fields: - **handleNotification (_(Notification) => Promise<NotificationBehavior>_**) -- (required) a function accepting an incoming notification returning a `Promise` resolving to a behavior ([`NotificationBehavior`](#notificationbehavior)) applicable to the notification - **handleSuccess (_(notificationId: string) => void_)** -- (optional) a function called whenever an incoming notification is handled successfully - **handleError (_(error: Error) => void_)** -- (optional) a function called whenever handling of an incoming notification fails #### Examples Implementing a notification handler that always shows the notification when it is received ```ts import * as Notifications from 'expo-notifications'; Notifications.setNotificationHandler({ handleNotification: async () => ({ shouldShowAlert: true, shouldPlaySound: false, shouldSetBadge: false, }), }); ``` ## Handling incoming notifications when the app is not in the foreground (not supported in Expo Go) > **Please note:** In order to handle notifications while the app is backgrounded on iOS, you _must_ add `remote-notification` to the `ios.infoPlist.UIBackgroundModes` key in your app.json, **and** add `"content-available": 1` to your push notification payload. Under normal circumstances, the “content-available” flag should launch your app if it isn’t running and wasn’t killed by the user, _however_, this is ultimately decided by the OS so it might not always happen. ### `registerTaskAsync(taskName: string): void` When a notification is received while the app is backgrounded, using this function you can set a callback that will be run in response to that notification. Under the hood, this function is run using `expo-task-manager`. You **must** define the task _first_, with [`TaskManager.defineTask`](https://docs.expo.dev/versions/latest/sdk/task-manager/#taskmanagerdefinetasktaskname-task). Make sure you define it in the global scope. The `taskName` argument is the string you passed to `TaskManager.defineTask` as the "taskName". The callback function you define with `TaskManager.defineTask` will receive the following arguments: - **data**: The remote payload delivered by either FCM (Android) or APNs (iOS). [See here for details](#pushnotificationtrigger). - **error**: The error (if any) that occurred during execution of the task. - **executionInfo**: JSON object of additional info related to the task, including the `taskName`. #### Examples ```ts import * as TaskManager from 'expo-task-manager'; import * as Notifications from 'expo-notifications'; const BACKGROUND_NOTIFICATION_TASK = 'BACKGROUND-NOTIFICATION-TASK'; TaskManager.defineTask(BACKGROUND_NOTIFICATION_TASK, ({ data, error, executionInfo }) => { console.log('Received a notification in the background!'); // Do something with the notification data }); Notifications.registerTaskAsync(BACKGROUND_NOTIFICATION_TASK); ``` ### `unregisterTaskAsync(taskName: string): void` Used to unregister tasks registered with `registerTaskAsync`. ## Fetching information about notifications-related permissions ### `getPermissionsAsync(): Promise<NotificationPermissionsStatus>` Calling this function checks current permissions settings related to notifications. It lets you verify whether the app is currently allowed to display alerts, play sounds, etc. There is no user-facing effect of calling this. #### Returns It returns a `Promise` resolving to an object representing permission settings (`NotificationPermissionsStatus`). #### Examples Check if the app is allowed to send any type of notifications (interrupting and non-interrupting–provisional on iOS) ```ts import * as Notifications from 'expo-notifications'; export async function allowsNotificationsAsync() { const settings = await Notifications.getPermissionsAsync(); return ( settings.granted || settings.ios?.status === Notifications.IosAuthorizationStatus.PROVISIONAL ); } ``` ### `requestPermissionsAsync(request?: NotificationPermissionsRequest): Promise<NotificationPermissionsStatus>` Prompts the user for notification permissions according to request. **Request defaults to asking the user to allow displaying alerts, setting badge count and playing sounds**. #### Arguments An optional object of conforming to the following interface: ```ts { android?: {}; ios?: { allowAlert?: boolean; allowBadge?: boolean; allowSound?: boolean; allowDisplayInCarPlay?: boolean; allowCriticalAlerts?: boolean; provideAppNotificationSettings?: boolean; allowProvisional?: boolean; } } ``` Each option corresponds to a different native platform authorization option (a list of iOS options is available [here](https://developer.apple.com/documentation/usernotifications/unauthorizationoptions), on Android all available permissions are granted by default and if a user declines any permission an app can't prompt the user to change). #### Returns It returns a `Promise` resolving to an object representing permission settings (`NotificationPermissionsStatus`). #### Examples Prompts the user to allow the app to show alerts, play sounds, set badge count and let Siri read out messages through AirPods ```ts import * as Notifications from 'expo-notifications'; export function requestPermissionsAsync() { return await Notifications.requestPermissionsAsync({ ios: { allowAlert: true, allowBadge: true, allowSound: true, }, }); } ``` ## Managing application badge icon ### `getBadgeCountAsync(): Promise<number>` Fetches the number currently set as the badge of the app icon on device's home screen. A `0` value means that the badge is not displayed. > **Note:** Not all Android launchers support application badges. If the launcher does not support icon badges, the method will always resolve to `0`. #### Returns It returns a `Promise` resolving to a number representing current badge of the app icon. ### `setBadgeCountAsync(badgeCount: number, options?: SetBadgeCountOptions): Promise<boolean>` Sets the badge of the app's icon to the specified number. Setting to `0` clears the badge. > **Note:** Not all Android launchers support application badges. If the launcher does not support icon badges, the method will resolve to `false`. #### Arguments The function accepts a number as the first argument. A value of `0` will clear the badge. As a second, optional argument you can pass in an object of options configuring behavior applied in Web environment. The object should be of format: ```ts { web?: badgin.Options } ``` where the type `badgin.Options` is an object described [in the `badgin`'s documentation](https://github.com/jaulz/badgin#options). #### Returns It returns a `Promise` resolving to a boolean representing whether setting of the badge succeeded. ## Scheduling notifications ### `getAllScheduledNotificationsAsync(): Promise<Notification[]>` Fetches information about all scheduled notifications. #### Returns It returns a `Promise` resolving to an array of objects conforming to the [`Notification`](#notification) interface. ### `presentNotificationAsync(content: NotificationContentInput, identifier?: string): Promise<string>` Schedules a notification for immediate trigger. > **Note:** This method has been deprecated in favor of using an explicit `NotificationHandler` and the `scheduleNotificationAsync` method. More info may be found at https://expo.fyi/presenting-notifications-deprecated. #### Arguments The only argument to this function is a [`NotificationContentInput`](#notificationcontentinput). #### Returns It returns a `Promise` resolving with the notification's identifier once the notification is successfully scheduled for immediate display. #### Examples ##### Presenting the notification to the user (deprecated way) ```ts import * as Notifications from 'expo-notifications'; Notifications.presentNotificationAsync({ title: 'Look at that notification', body: "I'm so proud of myself!", }); ``` ##### Presenting the notification to the user (recommended way) ```ts import * as Notifications from 'expo-notifications'; // First, set the handler that will cause the notification // to show the alert Notifications.setNotificationHandler({ handleNotification: async () => ({ shouldShowAlert: true, shouldPlaySound: false, shouldSetBadge: false, }), }); // Second, call the method Notifications.scheduleNotificationAsync({ content: { title: 'Look at that notification', body: "I'm so proud of myself!", }, trigger: null, }); ``` ### `scheduleNotificationAsync(notificationRequest: NotificationRequestInput): Promise<string>` Schedules a notification to be triggered in the future. > **Note:** Please note that this does not mean that the notification will be presented when it is triggered. For the notification to be presented you have to set a notification handler with [`setNotificationHandler`](#setnotificationhandlerhandler-notificationhandler--null-void) that will return an appropriate notification behavior. For more information see the example below. #### Arguments The one and only argument to this method is a [`NotificationRequestInput`](#notificationrequestinput) describing the notification to be triggered. #### Returns It returns a `Promise` resolving to a string --- a notification identifier you can later use to cancel the notification or to identify an incoming notification. #### Examples ##### Scheduling the notification that will trigger once, in one minute from now ```ts import * as Notifications from 'expo-notifications'; Notifications.scheduleNotificationAsync({ content: { title: "Time's up!", body: 'Change sides!', }, trigger: { seconds: 60, }, }); ``` ##### Scheduling the notification that will trigger repeatedly, every 20 minutes ```ts import * as Notifications from 'expo-notifications'; Notifications.scheduleNotificationAsync({ content: { title: 'Remember to drink water!, }, trigger: { seconds: 60 * 20, repeats: true }, }); ``` ##### Scheduling the notification that will trigger once, at the beginning of next hour ```ts import * as Notifications from 'expo-notifications'; const trigger = new Date(Date.now() + 60 * 60 * 1000); trigger.setMinutes(0); trigger.setSeconds(0); Notifications.scheduleNotificationAsync({ content: { title: 'Happy new hour!', }, trigger, }); ``` ### `cancelScheduledNotificationAsync(identifier: string): Promis