# Bare React Native Recipe

Register a bare React Native app's APNs and FCM tokens with Native Notify's push API using react-native-firebase/messaging, and show the Android data messages with Notifee.

A bare React Native app (no Expo modules) usually already has Firebase wired in for Android and an APNs registration for iOS. Both tokens register with the same endpoint. The example uses `@react-native-firebase/messaging` for the tokens, `@notifee/react-native` to display Android pushes, and `react-native-device-info` for a stable device id.

## Register the device

```js title="universalPush.js"
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 (an idempotent upsert — same `deviceId`, same device) and keep `watchUniversalToken(...)` subscribed while the app runs.

- **iOS:** `getAPNSToken()` resolves the raw APNs token as a hex string — or `null` until APNs has delivered it, in which case the next launch registers.
- **Android:** `onTokenRefresh` reports the rotated FCM token, which is registered again under the same device.

## Show Android pushes

Native Notify's Android pushes are FCM *data* messages (`title`, `message`, and `body` = your `pushData` as a JSON string, plus `channelId` / `subtitle` / … when the send set them). Firebase does not display data messages itself — hand them to `showNativeNotifyMessage` from both handlers:

```js title="index.js"
// 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 displays the APNs alert itself. Taps: the push comes straight from APNs, not through FCM, so Firebase's tap handlers may not see it — read `userInfo` in the AppDelegate's `UNUserNotificationCenter` delegate (your `pushData` keys are at its top level; see the [Swift recipe](/docs/push/recipes/swift)) and pass it to JS.

No Firebase on iOS? Use the APNs delegate directly: `application(_:didRegisterForRemoteNotificationsWithDeviceToken:)` hands you the token as `Data`; hex-encode it as the Swift recipe does and pass it to JS through a native module.

## Required project setup

- **Android:** the app's `google-services.json` (the Firebase project whose service account is saved as the app's credentials in Native Notify).
- **iOS:** the Push Notifications capability (an `aps-environment` entitlement that matches the build), and the app's `.p8` key saved in Native Notify with this app's bundle id.
- **Permission:** tokens are issued without it, but nothing is shown until the user allows notifications — `requestPermission()` on iOS, `POST_NOTIFICATIONS` on Android 13+ (both in the code above).

## Send and verify

`"all"` reaches every registered device, with or without a `subscriberId`; to reach one logged-in user's devices, send to `{"type":"subscribers","subscriberIds":[...]}` — see [Send Notifications](/docs/push/sending). Prove one device first with a [test send](/docs/push/verification) to its `deviceId`.

## Next steps

- [Device Registration](/docs/push/registration) and [Push Credentials](/docs/push/credentials).
- Want to skip the native wiring? The `native-notify` SDK works in bare React Native too — it requires Expo modules for the push-token plumbing (`npx install-expo-modules`; see [Installation](/docs/setup/installation)).
