If you aren’t using push notifications, you’re leaving retention on the table. They are one of the most effective ways to bring users back into your app.
But if you are building a cross-platform application with React Native, you have probably hit a wall already, because iOS and Android are fundamentally different beasts when it comes to messaging.
- iOS relies exclusively on APNs (Apple Push Notification service).
- Android natively speaks FCM (Firebase Cloud Messaging).
The moment you try to build a unified app, these two systems clash. They use different token formats, require different permission flows, and expect different server-side payloads. If you try to build separate back-end logic for each, you are doubling your work and creating a maintenance nightmare.
You don’t want your server to decide “Is this user on an iPhone or a Samsung?” every time you send an alert. Below, we are going to solve this fragmentation and walk through the complete architecture.
What You Need to Know About Native iOS Infrastructure
Before you bring push notifications into a cross-platform environment, you need a basic understanding of how iOS handles them at the native level (since iPhones only accept messages that pass through Apple’s own system).
How iOS Delivers Push Alerts
iOS doesn’t let your app talk directly to the device for push delivery. Everything goes through APNs. When your app starts up and the user grants permission, the iPhone asks APNs for something called a Device Token. This token is simply the unique identifier Apple generates for your app on that specific device (an address APNs uses when forwarding notifications).
A few things matter here:
- Each installation of your app gets its own token.
- Tokens can change when the user reinstalls the app, resets the phone, or restores from backup.
- If you’re not tracking token updates correctly, delivery becomes unreliable.
Mandatory iOS Setup Before React Native Can Do Its Job
Even if most of your code lives in React Native, iOS still expects a proper native setup inside Xcode. The essential pieces are:
1. The APNs Authentication Key (.p8)
This file is generated in the Apple Developer account. It’s what your back-end uses to prove to Apple that it’s allowed to send notifications for your app. Without it, APNs will simply reject every request.
Developers need to handle it carefully because:
- The key is sensitive and shouldn’t be shared.
- The key ID and Team ID must be correctly configured on the server.
- If the key is rotated, the back-end needs to be updated immediately.
2. Enabling Push Capabilities in Xcode
In the Xcode project, the “Push Notifications” capability has to be turned on. If it isn’t, the app can run perfectly fine but will never receive a token from APNs.
3. Understanding the Device Token
This is the fundamental difference between iOS development and Android. Apple’s token comes from APNs. Android’s token comes from FCM. They’re not interchangeable, they’re not the same format, and your back end must treat them differently (unless you’re using Firebase as the unifying layer, but we’ll talk about that later).
At this stage, the key takeaway is simple: On iOS, everything begins with APNs. No cross-platform library changes that. React Native only works once Apple’s native requirements are satisfied.
Using Firebase Cloud Messaging to Tie Everything Together
When the native iOS credentials are ready, you might be tempted to connect your server directly to APNs.
Don't do it.
Connecting directly to APNs means you now have two separate notification pipelines to maintain: one for Apple (APNs) and one for Android. You would need to format payloads differently, handle different response codes, and manage two distinct sets of tokens.
Instead, we use Firebase Cloud Messaging (FCM) as a broker. For a React Native app, FCM is the industry standard.
When your React Native app initializes on an iPhone, it first contacts Apple to get the APNs Device Token we mentioned earlier. If you were using raw native code, you would stop there.
But with Firebase in the mix, the React Native SDK takes that APNs token and silently sends it to Google’s servers. In exchange, Google returns an FCM Registration Token.
This FCM Token is the only thing your back-end needs to save.
By storing only the FCM token, your database schema becomes clean and platform-agnostic. You don’t need columns for ios_token or android_token. You just have a push_token field. When you want to message a user, you send the payload to that token, and Firebase handles the delivery to the iPhone.
Bringing Push Notifications Into React Native (Without the Headaches)
If you have your Apple keys and your Firebase project, the next step is making the app listen for notifications.
Libraries Worth Using (and Why)
React Native lacks a built-in, robust push engine. For most professional projects, react-native-firebase is the industry standard.
Specifically, the Messaging module (@react-native-firebase/messaging) is the go-to choice. It mimics the native Firebase SDKs perfectly, handling the complex handshake between your JavaScript code, the native iOS layer, and FCM.
Handling Permissions and Fetching the User’s Token
On iOS, silence is the default. You must explicitly request permission to disturb the user. The implementation flow is strict:
- Request Permission: Call messaging().requestPermission() at a strategic moment (not immediately at launch).
- Get the Token: Once authorized, call messaging().getToken(). This retrieves the FCM Registration Token.
- Sync: Send this token immediately to your back-end via API for storage.
- Listen: Implement onTokenRefresh to automatically catch and sync token updates if the device restores or rotates keys.
Behavior Differences: Foreground, Background, and Terminated
One common trap for developers is expecting notifications to look the same in all states. They don’t.
- Foreground (App Open): By default, iOS will not show a native banner. You must use an onMessage listener to catch the payload and display a custom in-app alert (like a toast or modal).
- Background/Terminated: The system takes over. The standard notification tray banner appears. You need listeners like onNotificationOpenedApp to detect if a user tapped that banner so you can handle deep linking.
Thinking of building an app with React Native?
We've got you coveredYour Back-End’s Role in the Push Notification Pipeline
Once the app collects the FCM token, the entire responsibility shifts to your back end. This is the layer that decides who gets notified, when they get notified, and what the payload looks like. If the back-end logic isn’t solid, delivery will be unreliable.
Storing and Managing Tokens
Each device your user logs in from needs its own FCM token. Your server should expect multiple tokens per user and store them in a table that’s simple and flexible:
- user_id
- fcm_token
- platform (optional, since FCM is abstracted)
- updated_at
Two important rules apply here:
- Store tokens immediately when the app sends them.
- Replace or delete tokens if FCM reports they are invalid.
Incorrect or stale tokens are the primary cause of failed deliveries in production apps.
Building and Sending Notification Payloads
When your server sends a notification, it constructs a payload and sends it to FCM’s /send endpoint. That payload typically includes two parts:
- notification (title, body — what the user sees)
- data (additional fields your app uses for routing and logic)
For example, if you need to deep link into a specific screen, those parameters belong in the data section. FCM keeps both sections intact and forwards them properly to iOS or Android.
Pro tip: Keep delivery predictable.
Your server should log every push request and track FCM’s response. This is how you detect invalid tokens, rate limits, or malformed payloads before they become user-visible problems. This translates to fewer missed notifications and less guesswork when troubleshooting.
Advanced Features That Make Your Push System Useful
Deep Linking and In-App Routing
A push notification that just opens the home screen is a wasted opportunity. If you send a user a "New Comment" alert, tapping it should take them directly to that comment.
To achieve this in React Native, you rely on the Data Payload we mentioned earlier.
- The Payload: Send a custom key, like type: "chat_screen" and id: "405".
- The Listeners: You need to handle two distinct scenarios in your React Native code:
- onNotificationOpenedApp: Triggers when the user taps a notification while the app is in the background.
- getInitialNotification: Triggers when the user taps a notification that launches the app from a cold start (quit state).
- The Router: Pass these parameters to your navigation library (like React Navigation) to virtually "push" the correct screen onto the stack immediately after the app loads.
Rich Notifications on iOS
This is one area where React Native cannot help you directly. To display a picture or a video inside the notification bubble (Rich Media), you must touch native iOS code.
You need to add a Notification Service Extension to your Xcode project. This is a small, separate mini-app bundled with your main app.
- How it works: When a notification arrives with the flag "mutable-content": 1, iOS wakes up this extension strictly for 30 seconds.
- The Job: The extension downloads the image URL from your payload, attaches it to the notification, and hands it back to the system to display.
Note: If the download takes too long (slow network), iOS kills the process and displays the plain text version as a fallback.
Silent and Data-Only Pushes
Sometimes you want to update the app without bothering the user, like syncing a database or updating a news feed so it's ready when they open the app.
For this, you send a Silent Push.
- The Payload: You must include "content-available": 1 and omit the visible alert body.
- The Warning: Apple treats these as "low priority." If the user is in Low Power Mode, or if they force-quit your app (swiped it away), the system will likely ignore the silent push to save battery. Never rely on silent pushes for critical data delivery; use them only for "nice-to-have" background refreshes.
Wrapping It Up
By now, the full flow should make sense. Both platforms need native setup: iOS for APNs, Android for FCM. The difference is that with React Native + Firebase, the Android side is mostly handled through Gradle + google-services.json, while the iOS side also requires Apple Developer and Xcode configuration.
React Native handles permissions, tokens, and listeners and your back end keeps the tokens fresh and sends structured payloads. Most of the problems teams run into come from skipping one of these layers or assuming React Native hides the platform differences. It doesn’t. However, once the architecture is in place, push notifications become predictable, testable, and easy to extend.
If you’re working on a cross-platform product and want this part of the stack handled cleanly from the start, Tapptitude can help shape the architecture, fill in the native pieces, and make sure your push pipeline behaves the way it should in production.
FAQs
What’s the single hardest part of this integration?
The certificate management. Most "broken" push systems usually trace back to an expired Apple certificate or a mismatch between the Production and Development environments. That’s why we recommend using the .p8 Auth Key over the old .p12 certificates. It removes the expiration headache entirely.
Should I use APNs directly or Firebase Cloud Messaging (FCM)?
Use FCM. Connecting directly to APNs forces you to build a custom engine just for iOS, separate from your Android logic. FCM acts as a reliable broker, letting your back-end send one message to one API, while Google handles the complex handshake with Apple’s servers in the background.
Do I need native iOS code to implement push notifications in React Native?
React Native gets you 90% of the way there, but push notifications are a system-level feature. You absolutely have to open Xcode to flip the "Push Notifications" switch and enable Background Modes. There is no JavaScript workaround for that permission layer.
What is a Device Token?
On iOS, the Device Token is the unique identifier APNs assign to your app on a specific device. It’s the “address” Apple uses to deliver notifications. Firebase converts this APNs token into a single FCM token so your back end only has to store one value.
Ready to build your own iOS App?
Get in touchTapptitude
Tapptitude is a mobile app development company specialized in providing high-quality mobile app development services, and a top-rated app company on Clutch.