Skip to content
This repository was archived by the owner on Apr 7, 2026. It is now read-only.
Open
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
217 changes: 130 additions & 87 deletions docs-react-native/react-native/docs/ios/remote-notification-support.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,41 +5,21 @@ next: /
previous: /react-native/docs/ios/permissions
---

It's possible to display a notification with Notifee features from outside the app using remote notifications (a.k.a push notifications) in two ways:
- using APNs keys
- using `notifee_options` with our notification service extension helper
> It is recommended to only use a notification service extension if you require an image or need to modify the contents of the notification before its displayed.

It is recommended to only use a notification service extension if you require an image or need to modify the contents of the notification before its displayed.
It's possible to display a notification with Notifee features from outside the app using remote notifications (a.k.a push notifications) by using `notifee_options` in APNs keys with our notification service extension helper.
Comment thread
LunatiqueCoder marked this conversation as resolved.
Outdated

### Using APNs keys
Adding a custom key `notifee_options` in the message payload enables Notifee to modify the notification before it is finally displayed to the end user. You can also change it as you please during this time.
Comment thread
LunatiqueCoder marked this conversation as resolved.
Outdated

Notification messages sent through APNs follow the APNs payload format which allows us to be able to specify a category or a custom sound with no extra configuration on the client:
> ⭐️ In order to bring the iOS experience closer to the Android one, you can still send **data-only/local/background** notifications for Android while using **remote/alert** notifications for iOS [by configuring the right payload.](#configure-the-payload)
Comment thread
LunatiqueCoder marked this conversation as resolved.
Outdated

```json
// FCM
{
notification: {
title: 'A notification title!',
body: 'A notification body',
},
apns: {
payload: {
aps: {
category: 'post', // A category that's already been created by your app
sound: 'media/kick.wav', // A local sound file you have inside your app's bundle
... // any other properties
},
},
},
...
};
```

### Using `notifee_options`
To get started, you will need to implement a [Notification Service Extension](https://developer.apple.com/documentation/usernotifications/unnotificationserviceextension) for your iOS app, the following steps will guide you through how to set it up.

By adding a custom key `notifee_options` in the message payload, the notification will be modified by Notifee before it is finally displayed to the end user.
### Expo

To get started, you will need to implement a [Notification Service Extension](https://developer.apple.com/documentation/usernotifications/unnotificationserviceextension) for your iOS app, the following steps will guide you through how to set it up.
You can jump straght to [configuring the payload](#configure-the-payload) if you use Expo with one of the community plugins:
- https://github.com/LunatiqueCoder/expo-notifee-plugin
- https://github.com/evennit/notifee-expo-plugin

## Add the notification service extension

Expand All @@ -48,6 +28,7 @@ To get started, you will need to implement a [Notification Service Extension](ht
* Add a product name (use `NotifeeNotificationService` to follow along) and click Finish
Comment thread
LunatiqueCoder marked this conversation as resolved.
* Make sure that the **deployment target** of your newly added extension (e.g. `NotifeeNotificationService`) matches the **deployment target** of your app. You can check it by selecting you app's target -> `Build Settings` -> `Deployment`


<!-- <Vimeo id="remote-notification-support-1" caption="Step 1 - Add Your Notification Service Extension" /> -->

## Add target to the Podfile
Expand All @@ -64,7 +45,7 @@ target 'NotifeeNotificationService' do
end

```

* Assuming you are using `react-native-firebase`, you'll also have to add `use_frameworks!` inside the target above as [you did when you followed their docs](https://rnfirebase.io/#altering-cocoapods-to-use-frameworks).
Comment thread
LunatiqueCoder marked this conversation as resolved.
Outdated
* Install or update your pods using pod install from the ios folder

`pod install --repo-update`
Expand Down Expand Up @@ -95,79 +76,141 @@ At this point everything should still be running normally. This is the final ste
```
<!-- <Vimeo id="remote-notification-support-3" caption="Step 3 - Edit NotificationService.m" /> -->

Before, moving to the next step, run the app and check it builds successfully – make sure you have the correct target selected.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this suggestion for people to do a build test? We used to get a lot of problems from people where they had a mix of "did not even add the extension correctly" and "added custom code that did not work"

It was useful to separate the concerns and make sure they added things correctly before they jumped in to custom code

Should probably make that suggestion after we suggest to add the import as the build test will then make sure they have altered their Podfile correctly to have the dependency on the notifee pod and everything is linked up correctly

Copy link
Copy Markdown
Author

@LunatiqueCoder LunatiqueCoder Oct 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was useful to separate the concerns and make sure they added things correctly before they jumped in to custom code.

Hmm, that's a fair point.

However I decided to leave this "build and run app" step after all the native code changes because some people have some very slow machines and running multiple builds can be frustrating.

Also I think they might be able to do something wrong here, run the app successfully but realise it at the next steps when they will run the app a second time with the extension.

But it's up to you, reverting this line will be easy 🤞

## Mutate the content with Notifee:
> 🔗 [Apple Docs: Modifying content in newly delivered notifications](https://developer.apple.com/documentation/usernotifications/modifying-content-in-newly-delivered-notifications#Implement-your-extensions-handler-methods)

## Edit the payload
Now everything is setup in your app, you can alter your notification payload in two ways:
#### Objective C:

Comment thread
LunatiqueCoder marked this conversation as resolved.
1. Update the message payload, sent via your backend
2. In a Notification Service Extension in your app when a device receives a remote message
In your `NotifeeNotificationService.m` file you should have method `didReceiveNotificationRequest` where we are calling `NotifeeExtensionHelper`. Now you can modify
`bestAttemptContent` before you send it to `NotifeeExtensionHelper`:

> Make sure that you will set `mutable-content: 1` (mutableContent if you are using firebase admin sdk) when sending notification otherwise Notification Service Extension will NOT be triggered
```objectivec
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];

// You can also modify the payload as you please here.
// You have around 30 seconds

// Notifee will mutate the notification according to notifee_options
[NotifeeExtensionHelper populateNotificationContent:request
withContent: self.bestAttemptContent
withContentHandler:contentHandler];
}
```

> Make sure that you will set `content-available: 1` (contentAvailable if you are using firebase admin sdk) if you want to receive notification when your app is in foreground
#### Swift:

In your `NotifeeNotificationService.swift` file, use the `didReceive(_:withContentHandler:)` method to enable the `NotifeeExtensionHelper`.
```swift
override func didReceive(_ request: UNNotificationRequest,
Comment thread
LunatiqueCoder marked this conversation as resolved.
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
self.bestAttempt = (request.content.mutableCopy() as? UNMutableNotificationContent)

### 1. Update the message payload, sent via your backend
// Here you can also modify the content of the notification as you please here.
// You have around 30 seconds

```json
// FCM
{
notification: {
title: 'A notification title!',
body: 'A notification body',

// Notifee will mutate the notification according to notifee_options
NotifeeExtensionHelper.populateNotificationContent(request,
with: self.bestAttempt!,
withContentHandler: contentHandler)
}
```

<br>

Before moving to the next step, run the app and check it builds successfully – make sure you have the correct target selected.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also like this suggestion here again - so they do a build test once before they try to do custom code, and now they do another build test after trying to make custom code but before moving on


## Configure the payload
> Make sure that you will set `mutable-content: 1` (mutableContent if you are using firebase admin sdk) when sending notification otherwise Notification Service Extension will NOT be triggered
>
> 🔗 [**Apple docs:** Configure-the-payload-for-thenotification](https://developer.apple.com/documentation/usernotifications/modifying-content-in-newly-delivered-notifications#Configure-the-payload-for-thenotification)

### Here's an example of a payload sent from the backend with Firebase Admin Node.js SDK

```ts

import type {Notification} from '@notifee/react-native/src/types/Notification';
import {AndroidImportance} from '@notifee/react-native/src/types/NotificationAndroid';
import {MulticastMessage} from 'firebase-admin/lib/messaging/messaging-api';
import admin from '../src/firebase-admin';

/**
* @link https://notifee.app/react-native/reference/notification
*/
const notifeeOptions: Notification = {
title: 'Title',
subtitle: 'Subtitle',
body: 'Main body content of the notification',
image: 'https://placeimg.com/640/480/any', // URL to pointing to a remote image
android: {
channelId: 'default',
importance: AndroidImportance.HIGH,
lightUpScreen: true,
pressAction: {
id: 'default',
},
apns: {
payload: {
aps: {
// Payloads coming from Admin SDK should specify params in camelCase.
// Payloads from REST API should specify in kebab-case
// see their respective reference documentation
'contentAvailable': 1, // Important, to receive `onMessage` event in the foreground when message is incoming
'mutableContent': 1, // Important, without this the extension won't fire
},
notifee_options: {
image: 'https://placeimg.com/640/480/any', // URL to pointing to a remote image
ios: {
sound: 'media/kick.wav', // A local sound file you have inside your app's bundle
foregroundPresentationOptions: {alert: true, badge: true, sound: true, banner: true, list: true},
categoryId: 'post', // A category that's already been created by your app
attachments: [{url: 'https://placeimg.com/640/480/any', thumbnailHidden: true}] // array of attachments of type `IOSNotificationAttachment`
... // any other api properties for NotificationIOS
},
... // any other api properties for Notification, excluding `id`
},
},
sound: 'default',
},
ios: {
sound: 'media/kick.wav', // A local sound file you have inside your app's bundle
categoryId: 'post', // A category that's already been created by your app
Comment thread
LunatiqueCoder marked this conversation as resolved.
Outdated
attachments: [{url: 'https://placeimg.com/640/480/any', thumbnailHidden: true}] // array of attachments of type `IOSNotificationAttachment`
// 🚧 Adding `foregroundPresentationOptions` controls how to
// 👇 behave when app is UP AND RUNNING, not terminated, AND not in background!
foregroundPresentationOptions: {
badge: true,
banner: true,
list: true,
sound: true,
},
...
},
};
```

### 2. In a Notification Service Extension in your app when a device receives a remote message

In your NotifeeNotificationService.m file you should have method `didReceiveNotificationRequest` where we are calling `NotifeeExtensionHelper`. Now you can modify
`bestAttemptContent` before you send it to `NotifeeExtensionHelper`:

```objectivec
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
self.contentHandler = contentHandler;
self.bestAttemptContent = [request.content mutableCopy];

NSMutableDictionary *userInfoDict = [self.bestAttemptContent.userInfo mutableCopy];
userInfoDict[@"notifee_options"] = [NSMutableDictionary dictionary];
userInfoDict[@"notifee_options"][@"title"] = @"Modified Title";

self.bestAttemptContent.userInfo = userInfoDict;
/**
* @description Firebase Message
* @link https://firebase.google.com/docs/reference/admin/node/firebase-admin.messaging.basemessage.md#basemessage_interface
*/
const message: MulticastMessage = {
// ✅ We can continue using local/data-only notification for Android
// 👍 while triggering iOS remote notifications from `apns`
data: {notifee_options: JSON.stringify(notifeeOptions)},
tokens: [],
android: {
priority: 'high', // Needed to trigger data-only notifications
Comment thread
LunatiqueCoder marked this conversation as resolved.
Outdated
},
apns: {
payload: {
notifee_options: notifeeOptions,
aps: {
// Payloads coming from Admin SDK should specify params in camelCase.
// Payloads from REST API should specify in kebab-case
// see their respective reference documentation
alert: {
// 🚧 This is needed to trigger an alert/remote notification only for iOS
Comment thread
LunatiqueCoder marked this conversation as resolved.
Outdated
// 👍 but Android will continue using data-only notifications
title: 'ANY_DUMMY_STRING', // Or notifeeOptions.title :)
},
'mutableContent': 1, // Important, without this the extension won't fire
},
},
},
};

[NotifeeExtensionHelper populateNotificationContent:request
withContent: self.bestAttemptContent
withContentHandler:contentHandler];
try {
admin.messaging().sendEachForMulticast(message)
res.status(200).end();
} catch (e) {
res.status(400).end();
}
```

Please note, the `id` of the notification is the `request.identifier` and cannot be changed. For this reason, the `id` property in `notifee_options` should be excluded.

> if both `attachments` and `image` are present, `attachments` will take precedence over `image`
> If both `attachments` and `image` are present, `attachments` will take precedence over `image`

### Handling Events

Expand All @@ -176,6 +219,8 @@ Currently, notifee supports the following events for remote notifications:
- `ACTION_PRESSED`
- `DISMISSED`

> On iOS, only notifications with a `categoryId` will receive a `DISMISSED` event.

To know identify when an interaction is from a remote notification, we can check if `notification.remote` is populated:

```jsx
Expand All @@ -198,5 +243,3 @@ function App() {
}, []);
}
```

> On iOS, only notifications with a `categoryId` will receive a `DISMISSED` event.