-
Notifications
You must be signed in to change notification settings - Fork 276
docs(ios): - Improve Remote Notification Support section #1120
base: main
Are you sure you want to change the base?
Changes from 4 commits
4b9e837
480b3b0
87643e5
aa5336b
be32ba2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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. | ||
|
|
||
| ### 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. | ||
|
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) | ||
|
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 | ||
|
|
||
|
|
@@ -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 | ||
|
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 | ||
|
|
@@ -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). | ||
|
LunatiqueCoder marked this conversation as resolved.
Outdated
|
||
| * Install or update your pods using pod install from the ios folder | ||
|
|
||
| `pod install --repo-update` | ||
|
|
@@ -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. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
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: | ||
|
|
||
|
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, | ||
|
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. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
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 | ||
|
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 | ||
|
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 | ||
|
|
||
|
|
@@ -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 | ||
|
|
@@ -198,5 +243,3 @@ function App() { | |
| }, []); | ||
| } | ||
| ``` | ||
|
|
||
| > On iOS, only notifications with a `categoryId` will receive a `DISMISSED` event. | ||
Uh oh!
There was an error while loading. Please reload this page.