Register a bare React Native app's APNs and FCM tokens with Native Notify, and show Android pushes with Notifee.
1. Set up the project
npm install @react-native-firebase/app @react-native-firebase/messaging @notifee/react-native react-native-device-info
cd ios && pod install
Android needs the app's google-services.json, from the Firebase project whose service account you save in step 4. iOS needs the Push Notifications capability, with an aps-environment entitlement that matches the build.
2. Register the device and show pushes
import { PermissionsAndroid, Platform } from "react-native";
import messaging from "@react-native-firebase/messaging";
import notifee, { AndroidImportance } from "@notifee/react-native";
import DeviceInfo from "react-native-device-info";
const REGISTER_URL = "https://app.nativenotify.com/api/universal/device/register";
async function postRegistration({ appId, appToken, subscriberId }, token) {
const res = await fetch(REGISTER_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
appId,
appToken,
deviceId: await DeviceInfo.getUniqueId(), // IDFV on iOS, ANDROID_ID on Android
platform: Platform.OS,
subscriberId, // your logged-in user's id; omit for an anonymous device
tokens: Platform.OS === "ios" ? { apnsToken: token } : { fcmToken: token },
}),
});
if (res.status !== 201) {
// 400/403 answer { error: { code, message } }; a wrong appId/appToken answers 401 as plain text.
throw new Error(`Native Notify register failed (${res.status}): ${await res.text()}`);
}
return res.json(); // { ok, device, tokens }
}
export async function registerUniversalDevice(args) {
// Without permission nothing is shown (tokens are issued either way).
if (Platform.OS === "android" && Number(Platform.Version) >= 33) {
await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS);
} else if (Platform.OS === "ios") {
await messaging().requestPermission();
}
// iOS: the raw APNs token (null until APNs has delivered it). Android: the FCM token.
const token = Platform.OS === "ios" ? await messaging().getAPNSToken() : await messaging().getToken();
if (!token) return null; // not ready yet — the next launch registers
return postRegistration(args, token);
}
// FCM rotates Android tokens — register the new one.
export function watchUniversalToken(args) {
return messaging().onTokenRefresh((fcmToken) => {
if (Platform.OS !== "android") return;
postRegistration(args, fcmToken).catch((err) => console.warn("Native Notify:", err));
});
}
// Android pushes are FCM *data* messages: show them yourself (foreground and background).
export async function showNativeNotifyMessage(remoteMessage) {
const data = remoteMessage.data || {};
if (!data.title) return;
const channelId = await notifee.createChannel({
id: data.channelId || "default",
name: "Notifications",
importance: AndroidImportance.HIGH,
});
await notifee.displayNotification({
title: data.title,
body: data.message,
data: { pushData: data.body || "{}" }, // your pushData JSON (+ nn_notification_id / nn_source)
android: { channelId, pressAction: { id: "default" } },
});
}
Call registerUniversalDevice({ appId, appToken, subscriberId }) on every launch, and keep watchUniversalToken(...) subscribed while the app runs.
3. Wire up the handlers
Android pushes are FCM data messages, so Firebase doesn't display them. Hand them to showNativeNotifyMessage from both handlers:
// index.js — before AppRegistry.registerComponent (the background handler must be set early)
import { Platform } from "react-native";
import messaging from "@react-native-firebase/messaging";
import { showNativeNotifyMessage } from "./universalPush";
if (Platform.OS === "android") {
messaging().onMessage(showNativeNotifyMessage);
messaging().setBackgroundMessageHandler(showNativeNotifyMessage);
}
On iOS the system shows the alert itself. A tap comes straight from APNs, so Firebase's tap handlers may not see it: read userInfo in the AppDelegate's UNUserNotificationCenter delegate and pass it to JS, as in the Swift recipe. Without Firebase on iOS, hex-encode the token from application(_:didRegisterForRemoteNotificationsWithDeviceToken:) the same way and pass it to JS through a native module.
4. Save credentials
Save the Firebase service-account JSON (Android) and the .p8 key with this app's bundle id (iOS) under App Settings → Push credentials. See Push credentials.
5. Send a push
Check one device first with a test send, then send to an audience.
Next steps
- Device registration — fields, deregister and errors.
- Expo setup — the
native-notifySDK also works in bare React Native, with Expo modules (npx install-expo-modules).