Startseite / Artikel

React Native-Benachrichtigungen: Berechtigungen, Kanäle und der FCM-Lebenszyklus

Erfahren Sie, wie Berechtigungen, Android-Kanäle, FCM-Tokens sowie Handler für den Vordergrund-, Hintergrund- und Beendigungsmodus in einem React Native-Benachrichtigungssystem mit Notifee zusammenwirken.

Verlag: REACTAPP.TOP19 Min. Lesezeit4332 Wörter

Quellen: React Native Notifications & Permissions: A Complete Guide for Android and iOS

Für einen Benutzer ist eine Benachrichtigung eine Zeile Text wie „Ihre Bestellung wurde versandt“. Für die App, die sie sendet, hängt diese Zeile von Laufzeitberechtigungen, Android-Benachrichtigungskanälen, Gerätetoken, Firebase Cloud Messaging (FCM), Hintergrundverarbeitern sowie von Unterschieden zwischen Android und iOS ab. Ein Fehler in irgendeiner Schicht führt dazu, dass Benachrichtigungen nie ankommen oder die falsche Ansicht geöffnet wird. Diese Anleitung geht Schritt für Schritt durch jede dieser Schichten mit Notifee und React Native Firebase, damit Sie ein Benachrichtigungssystem erstellen können, das in jedem App-Zustand vorhersehbar funktioniert.

Lokale Benachrichtigungen und Push-Benachrichtigungen

Lokale Benachrichtigungen

Eine lokale Benachrichtigung wird direkt auf dem Gerät von der App selbst erstellt, ohne Beteiligung eines Servers. Ein Erinnerungstext für ein Meeting ist ein typisches Beispiel:

Reminder
Your meeting starts in 10 minutes.

Die App kann die Benachrichtigungen offline anzeigen oder planen. Notifee bietet eine React Native API über die nativen Benachrichtigungssysteme beider Plattformen an.

Push-Benachrichtigungen

Eine Push-Benachrichtigung beginnt auf einem Server. Ihr Backend übermittelt die Nachricht an FCM, FCM liefert sie an das Gerät und die React Native-App reagiert darauf:

Your Backend
     │
     ▼
Firebase Cloud Messaging
     │
     ▼
Android / iOS Device
     │
     ▼
React Native Application

FCM unterscheidet zwischen Benachrichtigungsmitteilungen (die das System für Sie anzeigen kann) und Datennachrichten (beliebige Schlüssel-Wert-Pakete für Ihren Code), und es kann entweder ein einzelnes Gerät oder eine Gruppe von Geräten über Topics ansprechen.

Die Genehmigung ist Teil des Zustands Ihrer App

Die Installation einer App bedeutet nicht mehr automatisch, dass Benachrichtigungen angezeigt werden können. Seit Android 13 ist POST_NOTIFICATIONS eine Laufzeitberechtigung, und Apps für Android 13 oder neuer müssen diese vor dem Anzeigen von Inhalten beantragen. iOS erfordert bereits immer eine ausdrückliche Genehmigung des Benutzers. Betrachten Sie die Berechtigung als einen Zustand der App, der sich ändern kann, und nicht als einmalige Einrichtungsschritte.

Ein sinnvoller Ablauf prüft zunächst den Status, erläutert den Vorteil und fragt anschließend nach; jede Antwort ermöglicht es der App, fortzufahren:

Application starts
       │
       ▼
Check notification permission
       │
       ├── Granted ───────► Continue
       │
       └── Not granted
                │
                ▼
        Explain the benefit
                │
                ▼
        Request permission
                │
          ┌─────┴─────┐
          ▼           ▼
       Granted      Denied
          │           │
          ▼           ▼
     Notifications   Continue

Installation der Bibliotheken

Notifee kümmert sich um die Anzeige von Benachrichtigungen:

npm install @notifee/react-native

React Native Firebase stellt das Kern-App-Modul sowie das Messaging-Modul für Push-Benachrichtigungen bereit:

npm install @react-native-firebase/app
npm install @react-native-firebase/messaging

Auf iOS müssen anschließend die nativen CocoaPods-Abhängigkeiten installiert werden:

cd ios
pod install
cd ..

Die native Konfiguration variiert je nach React Native-Version und Projekt-Einrichtung. Zudem benötigt der iOS-Push-Benachrichtigungen die APNs-Anmeldeinformationen in Firebase sowie die Push-Funktion in Xcode. Befolgen Sie die aktuellen Dokumentationen jeder Bibliothek.

Anzeige der ersten nativen Benachrichtigung

Ziel des ersten Beispiels ist eine Benachrichtigung mit diesem Titel und Inhalt:

New Message
You have received a new message.

Beginnen Sie damit, Notifee zusammen mit der für Android-Kanäle verwendeten Wichtigkeits-Enum zu importieren:

import notifee, {
  AndroidImportance,
} from '@notifee/react-native';

Die folgende Funktion erledigt nacheinander drei Dinge: Sie bittet um Berechtigung, erstellt (oder wiederverwendet) einen Android-Kanal mit dem Namen default, und zeigt die Benachrichtigung in diesem Kanal an. Die pressAction-Einstellung mit ID default weist Android an, die App zu öffnen, wenn auf die Benachrichtigung getippt wird; ohne diese Einstellung hat ein Tipp keine Wirkung.

export async function displayNotification() {
  // Request permission
  await notifee.requestPermission();  // Create Android notification channel
  const channelId = await notifee.createChannel({
    id: 'default',
    name: 'Default Notifications',
    importance: AndroidImportance.DEFAULT,
  });  // Display notification
  await notifee.displayNotification({
    title: 'New Message',
    body: 'You have received a new message.',    android: {
      channelId,
      pressAction: {
        id: 'default',
      },
    },
  });
}

Es ist sicher, createChannel wiederholt aufzurufen; ein bereits vorhandenes Channel gibt einfach seine ID zurück. Channels sind ab Android 8.0 (API 26) und neueren Versionen erforderlich. Rufen Sie die Funktion über eine Schaltfläche auf:

<Button
  title="Show Notification"
  onPress={displayNotification}
/>

Anfrage der Android 13-Erlaubnis

Die Android-Erlaubnis hat diesen vollen Namen:

android.permission.POST_NOTIFICATIONS

React Native stellt sie als Konstante in PermissionsAndroid zur Verfügung:

PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS

Die Erlaubnis muss außerdem mit uses-permission in AndroidManifest.xml deklariert werden, andernfalls wird die Anfrage stillschweigend abgelehnt. Beginnen Sie mit den Imports:

import {
  PermissionsAndroid,
  Platform,
} from 'react-native';

Der Helper gibt sofort true zurück auf iOS sowie bei Android-Versionen unter 13 (API-Ebene 33), wo Benachrichtigungen standardmäßig erlaubt sind. Andernfalls wird das Systemdialog angezeigt und mitgeteilt, ob das Ergebnis GRANTED war:

export async function requestAndroidNotificationPermission() {
  if (Platform.OS !== 'android') {
    return true;
  }  if (Platform.Version < 33) {
    return true;
  }  const result = await PermissionsAndroid.request(
    PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS,
  );  return result === PermissionsAndroid.RESULTS.GRANTED;
}

Rufen Sie es von einem Bildschirm aus auf:

const granted =
  await requestAndroidNotificationPermission();

und gehen Sie je nach Ergebnis weiter:

if (granted) {
  console.log('Notifications allowed');
} else {
  console.log('Notifications denied');
}

Android kann drei sinnvolle Ergebnisse zurückgeben:

GRANTED
DENIED
NEVER_ASK_AGAIN

NEVER_ASK_AGAIN erfordert besondere Aufmerksamkeit. Nach wiederholten Ablehnungen stoppt das System die Anzeige des Dialogs, und nur die Systemeinstellungen können die Antwort ändern.

Im richtigen Moment fragen

Die Anzeige des Systemfragments bereits auf dem ersten Bildschirm ist ein häufiger und kostspieliger Fehler. Ein neuer Benutzer, der sofort sieht

"Allow MyApp to send notifications?"

hat keinen Kontext und wird wahrscheinlich ablehnen. Erklären Sie zunächst beispielsweise auf einem Willkommensbildschirm:

Welcome to MyApp

folgt von einer Karte, die auflistet, wofür Benachrichtigungen dienen, zusammen mit einer Schaltfläche:

Enable notifications to receive:
✓ New messages
✓ Order updates
✓ Important alerts[Enable Notifications]

Nur dieser Tap auslöst den OS-Dialog, sodass die Anfrage auf eine Benutzerentscheidung reagiert anstatt zu stören.

iOS-Autorisierung mit Notifee

Auf iOS muss der Benutzer die Zustellung ausdrücklich autorisieren. Notifee gibt das Ergebnis über AuthorizationStatus zurück:

import notifee, {
  AuthorizationStatus,
} from '@notifee/react-native';

Der untenstehende Helper betrachtet zwei Status als Erfolg. AUTHORIZED bedeutet volle Berechtigung. PROVISIONAL ist der leise Modus von iOS, bei dem Benachrichtigungen direkt ins Benachrichtigungszentrum gesendet werden, ohne den Benutzer zu stören, und der Benutzer kann sie später beibehalten oder deaktivieren. Alle anderen Fälle gelten als Ablehnung:

export async function requestIOSNotificationPermission() {
  const settings = await notifee.requestPermission();  if (
    settings.authorizationStatus ===
    AuthorizationStatus.AUTHORIZED
  ) {
    console.log('Notifications authorized');
    return true;
  }  if (
    settings.authorizationStatus ===
    AuthorizationStatus.PROVISIONAL
  ) {
    console.log('Notifications provisionally authorized');
    return true;
  }  console.log('Notifications denied');  return false;
}

Die iOS-Anfrage kann eine Reihe von Optionen enthalten:

Alerts
Sounds
Badges
CarPlay
Announcements
Provisional notifications

Bitten Sie nur um das, was Sie benötigen; eine vorläufige Autorisierung überspringt beispielsweise die Anfrage ganz.

Eine Berechtigungsfunktion für die gesamte App

Anstatt Überprüfungen über verschiedene Bildschirme zu verteilen, verwenden Sie ein Service-Modul:

src/
 ├── services/
 │    └── notificationService.ts
 ├── screens/
 ├── components/
 └── App.tsx

Die Service-Datei:

// notificationService.ts

Verbindet beide Plattformpfade miteinander. Auf Android wird die Versionsprüfung sowie der Laufzeitantrag durchgeführt; auf iOS wird Notifee verwendet und sowohl autorisierter als auch vorläufiger Status akzeptiert:

import {
  Platform,
  PermissionsAndroid,
} from 'react-native';import notifee, {
  AuthorizationStatus,
} from '@notifee/react-native';export async function requestNotificationPermission() {
  if (Platform.OS === 'android') {
    if (Platform.Version < 33) {
      return true;
    }    const result = await PermissionsAndroid.request(
      PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS,
    );    return result === PermissionsAndroid.RESULTS.GRANTED;
  }  const settings = await notifee.requestPermission();  return (
    settings.authorizationStatus ===
      AuthorizationStatus.AUTHORIZED ||
    settings.authorizationStatus ===
      AuthorizationStatus.PROVISIONAL
  );
}

Die Screens rufen anschließend eine Funktion auf:

const enabled = await requestNotificationPermission();

und handeln auf die Antwort ein:

if (enabled) {
  console.log('Ready for notifications');
}

Neueere Versionen von Notifee können auf Android 13 auch über requestPermission() POST_NOTIFICATIONS anfordern; prüfen Sie die aktuellen Notifee-Dokumentationen, falls Sie einen einzigen Anruf für beide Plattformen bevorzugen.

Android-Benachrichtigungskanäle

Android 8.0 führte Kanäle ein, damit Benutzer Kategorien von Benachrichtigungen unabhängig voneinander steuern können. Eine App kann mehrere solcher Kanäle bereitstellen:

MyApp
│
├── Messages
├── Orders
├── Promotions
└── Security

Jeder Kanal hat seine eigene Wichtigkeit sowie Tonart. Nachrichten verdienen in der Regel eine hohe Wichtigkeit, damit sie sichtbar werden:

const messageChannel = await notifee.createChannel({
  id: 'messages',
  name: 'Messages',
  importance: AndroidImportance.HIGH,
});

Bei Aktualisierungen von Bestellungen kann das Standardniveau verwendet werden:

const orderChannel = await notifee.createChannel({
  id: 'orders',
  name: 'Orders',
  importance: AndroidImportance.DEFAULT,
});

Dann gibt eine Benachrichtigung an, zu welchem Kanal sie gehört. Der Aufruf wird in zwei Teilen angezeigt: zunächst der Titel und anschließend der Inhalt:

await notifee.displayNotification({
  title: 'New Message',
  body: 'You received a new message.',

und danach die Android-Einstellungen, die auf die vom createChannel-Funktion zurückgegebene Kanal-ID verweisen:

  android: {
    channelId: messageChannel,
  },
});

Kanäle richten sich an Benutzer, nicht nur an Entwickler. Benutzer können Kanäle in den Systemeinstellungen stummschalten oder blockieren, und nach der Erstellung eines Kanals kann die App seine Wichtigkeit nicht mehr programmatisch ändern; diese Entscheidung liegt beim Benutzer. Wählen Sie sorgfältig oder verwenden Sie später eine neue Kanal-ID.

Eine in der Produktion gut funktionierende Struktur trennt die Kanäle nach ihrem Zweck:

notifications
│
├── messages
├── orders
├── payments
├── security
└── marketing

Ein separater Marketingkanal ermöglicht es Benutzern, Werbungen stummschalten zu können, ohne Sicherheitshinweise zu verlieren.

Wie FCM hineinpasst

Beim Push-Verfahren steht FCM zwischen Ihrem Backend und jeder installierten App:

┌─────────────────┐
             │   Your Backend  │
             └────────┬────────┘
                      │
                      ▼
             ┌─────────────────┐
             │      FCM        │
             └────────┬────────┘
                      │
              ┌───────┴───────┐
              ▼               ▼
        ┌──────────┐     ┌──────────┐
        │ Android  │     │   iOS    │
        └──────────┘     └──────────┘

Erhalt des Geräte-Tokens

Jede Installation der App kann ein FCM-Registrierungstoken erhalten, und Ihr Backend benötigt dieses Token, um ein bestimmtes Gerät anzusprechen. Importieren Sie das Messaging-Modul:

import messaging from '@react-native-firebase/messaging';

und fordern Sie das Token an:

export async function getFCMToken() {
  const token = await messaging().getToken();  console.log('FCM Token:', token);  return token;
}

In einer echten App wird das Token unmittelbar nach seinem Erhalt an Ihren Server gesendet:

const token = await messaging().getToken();
await fetch('https://api.example.com/device-token', {
  method: 'POST',  headers: {
    'Content-Type': 'application/json',
  },  body: JSON.stringify({
    token,
  }),
});

Das Backend speichert anschließend das Token in Verbindung mit dem angemeldeten Benutzer und dem Gerät:

User
  │
  ├── userId
  ├── deviceId
  └── fcmToken

Aktualisierung der Tokens

Tokens ändern sich beispielsweise nach einer Neuinstallation oder beim Löschen von App-Daten. Hören Sie auf Aktualisierungsereignisse:

import messaging from '@react-native-firebase/messaging';

Der Zuhörer leitet jedes neue Token weiter und die Funktion gibt einen Abmelde-Callback zurück:

export function subscribeToTokenRefresh() {
  return messaging().onTokenRefresh(async token => {
    console.log('New FCM token:', token);    await sendTokenToBackend(token);
  });
}

Ein minimaler Hochlade-Hilfsprogramm, erneut in zwei Teilen dargestellt, führt einen JSON POST-Anruf durch:

async function sendTokenToBackend(token: string) {
  await fetch('https://api.example.com/device-token', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },    body: JSON.stringify({
      token,
    }),
  });
}

Firebase empfiehlt, Änderungen der Token zu verarbeiten und den neuen Wert jedes Mal auf Ihren Anwendungsserver zu übertragen, wenn sich dieser ändert. Auf der Serverseite sollten außerdem Token entfernt werden, die von FCM als ungültig gemeldet werden, damit veraltete Geräte nicht angesammelt werden.

Vordergrundnachrichten werden nicht automatisch angezeigt

Der verwirrendste Fall tritt auf, wenn eine Push-Nachricht eintrifft, während die App geöffnet ist:

User
  │
  ▼
React Native App
  │
  ▼
FCM message

Verfolgen Sie Vordergrundnachrichten:

import messaging from '@react-native-firebase/messaging';
const unsubscribe = messaging().onMessage(
  async remoteMessage => {
    console.log('Foreground message:', remoteMessage);
  },
);

Doch das Empfangen einer Nachricht und das Anzeigen einer Systembenachrichtigung sind getrennte Schritte. Während die App im Vordergrund ist, übergibt FCM die Nachricht an Ihren Code und zeigt nichts selbst an. Um eine Benachrichtigung anzuzeigen, geben Sie den Inhalt an Notifee weiter:

import notifee from '@notifee/react-native';

Der untenstehende Handler stellt sicher, dass das Kanal existiert, und zeigt anschließend die Benachrichtigung an. Falls die Nachricht keinen notification-Block enthält (zum Beispiel eine rein datenbasierte Nachricht), wird stattdessen der Standardtext angezeigt:

messaging().onMessage(async remoteMessage => {
  const channelId = await notifee.createChannel({
    id: 'messages',
    name: 'Messages',
  });  await notifee.displayNotification({
    title:
      remoteMessage.notification?.title ??
      'New Message',    body:
      remoteMessage.notification?.body ??
      'You have a new message.',    android: {
      channelId,
      pressAction: {
        id: 'default',
      },
    },
  });
});

Sie kontrollieren die Darstellung, einschließlich des Überspringens derselben, wenn der entsprechende Bildschirm bereits sichtbar ist.

Hintergrund- und Beendigungsbenachrichtigungen

Wenn die App im Hintergrund ist, ändert sich das Verhalten. Auf Android werden FCM-Benachrichtigungen direkt in den Systemtray gesendet, ohne Ihre Vordergrund-Callback-Funktion aufrufen zu lassen. Nachrichten, die sowohl Benachrichtigungen als auch Daten enthalten, unterliegen eigenen Regeln für den Hintergrundbetrieb. Auf iOS sind rein datenbasierte Hintergrundbenachrichtigungen nur mit begrenzter Zuverlässigkeit möglich und können vom System verzögert oder eingeschränkt werden.

React Native Firebase stellt einen speziellen Handler für Hintergrundbenachrichtigungen bereit:

import messaging from '@react-native-firebase/messaging';
messaging().setBackgroundMessageHandler(
  async remoteMessage => {
    console.log(
      'Background message:',
      remoteMessage,
    );
  },
);

Ein häufiger Fehler besteht darin, diesen Handler innerhalb eines React-Komponenten zu registrieren, wie im folgenden Fragment:

function App() {
  messaging().setBackgroundMessageHandler(...);
  return <Navigation />;
}

Ein Hintergrundweckvorgang kann möglicherweise keine Komponente laden, sodass der Handler niemals existieren würde. Registrieren Sie ihn am Eingangspunkt:

// index.js
import messaging from '@react-native-firebase/messaging';messaging().setBackgroundMessageHandler(
  async remoteMessage => {
    console.log(
      'Background message:',
      remoteMessage,
    );
  },
);

und registrieren Sie erst danach die Wurzelkomponente:

AppRegistry.registerComponent(
  appName,
  () => App,
);

Auf Klicks auf Benachrichtigungen reagieren

Ein Klick sollte den Benutzer in der Regel an einen bestimmten Ort führen:

Notification
     │
     ▼
User taps
     │
     ▼
Application opens
     │
     ▼
Order Details

Nehmen wir an, die Nachricht enthält in ihren Daten eine Bestell-ID:

{
  "orderId": "12345"
}

Für Apps, die aus dem Hintergrund wiederaufgenommen werden, wird onNotificationOpenedApp ausgelöst, wenn der Benutzer auf eine Benachrichtigung klickt. Die Callback-Funktion liest die ID aus remoteMessage.data und kann von dort aus navigieren:

messaging().onNotificationOpenedApp(
  remoteMessage => {
    const orderId =
      remoteMessage.data?.orderId;
    if (orderId) {
      // Navigate to order details
      console.log(
        'Open order:',
        orderId,
      );
    }
  },
);

Wenn die App vollständig geschlossen wurde

Falls die App beendet wurde, sieht die Abfolge wie folgt aus:

App completely closed
        │
        ▼
User receives notification
        │
        ▼
User taps notification
        │
        ▼
App launches

In diesem Fall wird onNotificationOpenedApp nicht ausgelöst. Stattdessen sollte man beim Starten einmal nach der Benachrichtigung fragen, die die App gestartet hat:

const remoteMessage =
  await messaging().getInitialNotification();
if (remoteMessage) {
  const orderId =
    remoteMessage.data?.orderId;  console.log(
    'App opened from notification:',
    orderId,
  );
}

Sowohl Methoden beziehen sich nur auf Benachrichtigungen, die von FCM angezeigt werden. Tipps auf Benachrichtigungen, die man selbst mit Notifee angezeigt hat – wie die oben genannten Vordergrundbenachrichtigungen – werden über Notifees eigene Event-APIs (onForegroundEvent, onBackgroundEvent sowie getInitialNotification) übermittelt. Die Behandlung nur einer Art von Benachrichtigungen ist eine häufige Ursache für Fehler vom Typ „Der Tap hat keine Wirkung“.

Für Navigation konzipierte Payloads

Eine Benachrichtigung, die nur

New Order

der App keine Informationen darüber gibt, wo der Benutzer landen soll. Fügen Sie strukturierte Daten neben den sichtbaren Text hinzu:

{
  "notification": {
    "title": "New Order",
    "body": "Order #12345 has been confirmed"
  },
  "data": {
    "type": "order",
    "orderId": "12345"
  }
}

Eine Router-Funktion kann dann den type auf ein Bildschirm anpassen:

function handleNotification(data: any) {
  if (data.type === 'order') {
    navigation.navigate(
      'OrderDetails',
      {
        orderId: data.orderId,
      },
    );
  }
}

Weil Benachrichtigungshandler oft außerhalb von Komponenten ausgeführt werden, ist die Navigation in solchen Hilfsfunktionen in der Regel ein Navigation-Referenz, das am Anfang der App erstellt wird. Für eine ausführliche Behandlung des Routings über Deep Links siehe Routing von Push-Benachrichtigungen über Deep Links in React Native.

Erkennen von Änderungen der Berechtigungen nach der Installation

Berechtigungen sind im Laufe der Zeit nicht konstant. Ein Benutzer kann am Montag Benachrichtigungen zulassen:

Monday:
Notifications = ON

und sie am Mittwoch über die Systemeinstellungen deaktivieren:

Wednesday:
User opens Android SettingsNotifications = OFF

Die App sollte dies bemerken. Notifee liest die aktuellen Einstellungen ohne Aufforderung ein:

import notifee, {
  AuthorizationStatus,
} from '@notifee/react-native';
export async function checkNotificationPermission() {
  const settings =
    await notifee.getNotificationSettings();  if (
    settings.authorizationStatus ===
    AuthorizationStatus.AUTHORIZED
  ) {
    return true;
  }  return false;
}

Beachten Sie, dass diese Überprüfung für vorläufige Autorisierung false zurückgibt; fügen Sie PROVISIONAL hinzu, wenn Ihre Anwendung dies als aktiv betrachtet. Führen Sie die Überprüfung erneut aus, wenn die Anwendung wieder im Vordergrund erscheint.

Wenn der Benutzer nein sagt

Wiederholen Sie die Anfrage nicht. Zeigen Sie stattdessen einen klaren Status an, beispielsweise ein Banner:

Notifications are disabled

mit einer Aktion, die zu den Einstellungen führt:

Enable notifications from Settings
to receive important updates.[Open Settings]

Notifee bietet APIs an, mit denen die Benachrichtigungseinstellungen der Anwendung direkt geöffnet werden können. Die Prinzipien dahinter lassen sich auf einer Karte zusammenfassen:

Don't force
Don't spam
Explain the value
Give the user control

Zentralisierung der Benachrichtigungseinstellungen

In Produktionscode sollten alle mit Benachrichtigungen zusammenhängenden Elemente in wenigen gut definierten Modulen aufbewahrt werden:

src/
│
├── services/
│   ├── notificationService.ts
│   └── firebaseMessaging.ts
│
├── navigation/
│   └── notificationNavigation.ts
│
├── screens/
│
└── App.tsx

Der Hauptdienst:

// notificationService.ts

Es wird ein Initialisierer bereitgestellt, der um Erlaubnis bittet, bei Ablehnung des Benutzers stoppt und ansonsten den Standardkanal erstellt:

import notifee, {
  AndroidImportance,
  AuthorizationStatus,
} from '@notifee/react-native';export async function initializeNotifications() {
  const settings =
    await notifee.requestPermission();  const authorized =
    settings.authorizationStatus ===
      AuthorizationStatus.AUTHORIZED ||
    settings.authorizationStatus ===
      AuthorizationStatus.PROVISIONAL;  if (!authorized) {
    return false;
  }  await notifee.createChannel({
    id: 'default',
    name: 'Default Notifications',
    importance: AndroidImportance.DEFAULT,
  });  return true;
}

Die Aufrufung aus einem Komponenten ist einfach:

useEffect(() => {
  initializeNotifications();
}, []);

Achtung: Auf dem ersten Bildschirm wird dies sofort abgefragt – es tritt das zuvor beschriebene Timing-Problem auf. Rufen Sie es stattdessen über die Opt-in-Schaltfläche auf.

Die Backend-Seite

In einem typischen Datenmodell werden Benutzer und ihre Geräte in separaten Tabellen gespeichert, da ein Benutzer mehrere Handys oder Tablets haben kann:

users
│
├── id
├── name
└── email
user_devices
│
├── id
├── user_id
├── device_id
├── fcm_token
├── platform
├── created_at
└── updated_at

Ein Geräte-Registrierungsdatensatz könnte wie folgt aussehen:

{
  "userId": 101,
  "deviceId": "device-abc",
  "fcmToken": "xxxxx",
  "platform": "android"
}

Wenn ein Geschäftsereignis eintritt, sucht das Backend nach den Tokens des Benutzers und sendet sie über FCM:

Order Created
      │
      ▼
Backend
      │
      ▼
Find user's FCM token
      │
      ▼
Firebase Cloud Messaging
      │
      ▼
User's phone

FCM erwartet, dass Nachrichten aus einem vertrauenswürdigen Umfeld gesendet werden, wie zum Beispiel Ihrem Anwendungsserver oder Cloud Functions – niemals vom Client, da zur Übertragung Server-Zertifikate erforderlich sind.

Skalierbare Payloads

Entwerfen Sie jeden Payload so, dass die App das Ereignis versteht und nicht nur den Text. Eine Zahlungserfassung könnte beispielsweise Folgendes enthalten:

{
  "notification": {
    "title": "Payment Successful",
    "body": "Your payment of ₹999 was successful."
  },
  "data": {
    "type": "payment",
    "paymentId": "PAY12345",
    "screen": "PaymentDetails"
  }
}

Ein einziger switch auf data.type kümmert sich anschließend um jede Art von Benachrichtigung:

switch (data.type) {
  case 'payment':
    navigation.navigate(
      'PaymentDetails',
      {
        paymentId: data.paymentId,
      },
    );
    break;
  case 'order':
    navigation.navigate(
      'OrderDetails',
      {
        orderId: data.orderId,
      },
    );
    break;
}

Jeder neue Benachrichtigungstyp entspricht einem neuen Fall. Die FCM-Datenwerte sind Strings, daher müssen Zahlen explizit interpretiert werden.

Die vollständige Architektur

Wenn alle Komponenten zusammengefügt werden, sieht eine Produktionsumgebung wie folgt aus:

┌──────────────────┐
                    │    Backend API   │
                    └────────┬─────────┘
                             │
                             ▼
                    ┌──────────────────┐
                    │ Firebase / FCM   │
                    └────────┬─────────┘
                             │
                ┌────────────┴────────────┐
                │                         │
                ▼                         ▼
        ┌──────────────┐          ┌──────────────┐
        │    Android   │          │     iOS      │
        └──────┬───────┘          └──────┬───────┘
               │                         │
               ▼                         ▼
        Notification                 APNs / FCM
          Channel                        │
               │                         │
               └────────────┬────────────┘
                            ▼
                    React Native App
                            │
                            ▼
                    Navigation / UI

Auf iOS übermittelt FCM die Nachrichten nicht selbst; es leitet sie über den Apple Push Notification Service (APNs) weiter, weshalb die APNs-Zertifikate konfiguriert werden müssen.

Häufige Fehler

Aufforderung beim ersten Starten

Dieses Muster auf dem ersten Bildschirm ohne jegliche Erklärung ist der häufigste Fehler:

useEffect(() => {
  requestNotificationPermission();
}, []);

Erklären Sie zunächst den Wert und bitten Sie um Genehmigung als Reaktion auf eine Benutzeraktion.

Channels vergessen

Ohne Channel erscheinen Benachrichtigungen nicht auf Android 8.0 und neueren Versionen. Erstellen Sie einen, bevor Sie etwas anzeigen:

await notifee.createChannel({
  id: 'messages',
  name: 'Messages',
});

Erwartung, dass Hintergrundbenachrichtigungen angezeigt werden

Durch Zuhören mit

messaging().onMessage(...)

wird nicht automatisch in jedem Fall eine sichtbare Systembenachrichtigung angezeigt. Für eine konsistente Anzeige im Vordergrund zeigen Sie sie selbst mit Notifee an.

Ignorieren von Token-Änderungen

Ein einmal gespeichertes Token funktioniert nach einer Weile stillschweigend nicht mehr. Verwenden Sie

messaging().onTokenRefresh(...)

und senden Sie jedes neue Token an den Backend-Server.

Registrierung von Hintergrundverarbeitern in Komponenten

Der Hintergrundhandler sollte sich am Eingangspunkt, außerhalb des React-Baums, wie zuvor gezeigt, befinden.

Ablehnung späterer Berechtigungsänderungen

Benutzer können jederzeit ihre Berechtigungen widerrufen oder einen einzelnen Kanal blockieren. Ihre Überprüfungen sollten alle diese Zustände berücksichtigen:

Granted
Denied
Changed externally
Channel blocked
App notifications disabled

Leckage sensibler Daten

Der Benachrichtigungstext ist auf dem Sperrbildschirm sowie für alle Personen in der Nähe des Geräts sichtbar. Anstatt

Your bank account ending in 1234 received ₹50,000.

einen neutralen Inhalt zu senden:

New account activity
Tap to view details.

und die Details erst nach der Authentifizierung des Benutzers innerhalb der App anzuzeigen.

Test-Checkliste

Das Verhalten variiert je nach Betriebssystemversion, App-Zustand und Gerät, daher sollten Tests systematisch durchgeführt werden. Auf Android:

☐ Android 12
☐ Android 13+
☐ Permission granted
☐ Permission denied
☐ Permission disabled from Settings
☐ Channel enabled
☐ Channel disabled
☐ App foreground
☐ App background
☐ App killed
☐ Notification tapped
☐ Multiple notifications

Auf iOS:

☐ Permission granted
☐ Permission denied
☐ Permission changed from Settings
☐ App foreground
☐ App background
☐ App terminated
☐ Notification tapped
☐ Badge
☐ Sound
☐ Deep linking

Und für die FCM-Integration im End-to-End-Szenario:

☐ Token generated
☐ Token stored on backend
☐ Token refresh handled
☐ Foreground message received
☐ Background message received
☐ Notification click handled
☐ Initial notification handled

Überblick über den Laufzeitablauf

Die vollständige Abfolge, von der Anwendungseröffnung über die Zustellung bis zur Navigation, sieht wie folgt aus:

APP START
                       │
                       ▼
              Check permissions
                       │
              ┌────────┴────────┐
              │                 │
           Allowed            Denied
              │                 │
              ▼                 ▼
       Initialize FCM      Show explanation
              │                 │
              ▼                 ▼
       Get FCM token       Request permission
              │
              ▼
       Send token to API
              │
              ▼
          App Running
              │
       ┌──────┴──────┐
       │             │
       ▼             ▼
   Foreground     Background
       │             │
       ▼             ▼
    onMessage      FCM/APNs
       │             │
       ▼             ▼
    Notifee       System UI
       │             │
       └──────┬──────┘
              ▼
        User taps
              │
              ▼
       Read notification data
              │
              ▼
          Navigation

Lokal gegen Push – und die gemeinsame Nutzung beider

Die beiden Benachrichtigungsarten unterscheiden sich in einigen vorhersehbaren Aspekten:

  • Auslöser: Lokale Benachrichtigungen stammen von der Anwendung oder dem Gerät; Push-Benachrichtigungen kommen aus Ihrem Backend.
  • Netzwerk: Lokale Benachrichtigungen funktionieren in der Regel offline; Push erfordert eine Verbindung.
  • FCM: Für lokale Benachrichtigungen nicht notwendig; in der Regel für Push erforderlich.
  • Geplante Erinnerungen: Passen gut zu lokalen Benachrichtigungen; nicht die typische Anwendung von Push.
  • Serverereignisse, Bestellaktualisierungen, Chatnachrichten: Push ist die übliche Wahl; lokale Benachrichtigungen können sie nur eingeschränkt abdecken.
  • Bibliotheken: Notifee für lokale Benachrichtigungen; FCM in Kombination mit einer Anzeigebibliothek für Push.

Sie passen auch gut zusammen. Eine gängige Konfiguration ermöglicht es FCM, die Nachricht zu übermitteln, Ihren Code, sie zu verarbeiten, und Notifee, sie anzuzeigen:

FCM receives message
        ↓
React Native processes message
        ↓
Notifee displays native notification

Was die Benutzer bei jedem Schritt sehen sollten

Für eine manuelle Überprüfung sollte man wissen, was zu erwarten ist. Auf Android 13+ erscheint ein Systemdialog:

Allow MyApp to send you notifications?

In den Systembenachrichtigungs-Einstellungen der App erscheinen die von Ihnen erstellten Kanäle als separate Schalter:

MyApp
 ├── Messages
 ├── Orders
 └── Promotions

Auf iOS erscheint die Systemautorisierungsanfrage, und die übermittelten Benachrichtigungen werden im Notification Center angezeigt. Für Push-Nachrichten kann die Firebase-Konsole eine Testnachricht direkt an einen Geräte-Token senden – das ist der schnellste Weg, um Backend- und Client-Fehler voneinander zu trennen:

Firebase Console
      ↓
Cloud Messaging
      ↓
Test Message
      ↓
FCM Token

Schließlich führt das Tippen auf eine Bestellbenachrichtigung dazu:

Notification
     ↓
Order #12345

dass man auf dem entsprechenden Bildschirm landet:

Order Details Screen

Produktions-Best Practices

Platzieren Sie die Anfrage nach Berechtigungen zu einem geeigneten Zeitpunkt, damit die Nutzer verstehen, warum Benachrichtigungen hilfreich sind – anstatt sie einfach beim Starten der App anzufordern. Halten Sie die Logik zentral in einem Service zusammen, anstatt sie auf verschiedenen Bildschirmen zu duplizieren:

notificationService.ts

Trennen Sie die verschiedenen Benachrichtigungsarten in Kanäle oder Kategorien ein, wie zum Beispiel:

messages
orders
payments
security
marketing

Entwerfen Sie Nutzlasten für die Navigation und fügen Sie bei Bedarf Felder hinzu:

type
id
screen

Zusätzlich zu dem oben Genannten:

  • Verknüpfen Sie Token mit authentifizierten Benutzern und Geräten, speichern Sie sie sicher und kümmern Sie sich um das Erneuern.
  • Halten Sie sensible Informationen aus dem Benachrichtigungstext raus, da dieser außerhalb der App sichtbar ist.
  • Testen Sie auf echten Geräten, da Emulatoren und Simulatoren sich unterschiedlich verhalten können.

Zusammenfassung

Ein funktionsfähiges Benachrichtigungssystem ist weitaus mehr als nur ein einziger Aufruf wie

showNotification();

Es handelt sich um die Kombination all dieser Aspekte:

Permissions
     +
Notification Channels
     +
FCM Tokens
     +
Foreground Handling
     +
Background Handling
     +
Notification Clicks
     +
Deep Linking
     +
Backend Integration

Der Schlüssel ist eine klare Aufgabenteilung. FCM übermittelt die Nachrichten. Das Betriebssystem ist für Berechtigungen sowie die native Darstellung verantwortlich. Notifee gibt React Native die Kontrolle über die lokale Darstellung. Ihr Backend entscheidet, wann was und an wen gesendet wird. Als einheitliches System sieht es so aus:

┌─────────────────┐
              │   Backend API   │
              └────────┬────────┘
                       │
                       ▼
              ┌─────────────────┐
              │      FCM        │
              └────────┬────────┘
                       │
              ┌────────┴────────┐
              ▼                 ▼
        ┌───────────┐     ┌───────────┐
        │  Android  │     │    iOS    │
        │  Channel  │     │   APNs    │
        └─────┬─────┘     └─────┬─────┘
              │                 │
              └────────┬────────┘
                       ▼
                React Native
                       │
                       ▼
                 Notifee
                       │
                       ▼
                Native UI

Mit diesen Grenzen verhalten sich Benachrichtigungen vorhersagbar auf Android und iOS sowie in den Zuständen Vordergrund, Hintergrund und Beendet.

Schnellreferenz

Bitten Sie um die Android-Berechtigung:

await PermissionsAndroid.request(
  PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS,
);

Bitten Sie um die iOS-Berechtigung:

await notifee.requestPermission();

Erstellen Sie einen Android-Kanal:

await notifee.createChannel({
  id: 'messages',
  name: 'Messages',
});

Zeigen Sie eine Benachrichtigung an:

await notifee.displayNotification({
  title: 'New Message',
  body: 'You have a new message.',
  android: {
    channelId: 'messages',
  },
});

Verfolgen Sie Vordergrund-FCM-Nachrichten:

messaging().onMessage(async message => {
  console.log(message);
});

Verfolgen Sie Token-Änderungen:

messaging().onTokenRefresh(token => {
  console.log(token);
});

Bearbeiten Sie Benachrichtigungen, die aus dem Hintergrund geöffnet werden:

messaging().onNotificationOpenedApp(message => {
  console.log(message.data);
});