Lesson 20 of 26
Push Notifications, Conceptually
The real pipeline behind a push notification: device tokens, APNs/FCM delivery, notification vs. data payloads, and where Expo simplifies it.
A push notification feels instant from the user's side — a banner appears, sometimes seconds after something happened on a server the user isn't even looking at. Underneath, that's a real, multi-step pipeline crossing your app, the device's OS, and at least one push service outside your control entirely. Understanding that pipeline matters because most push notification bugs are actually a misunderstanding of which step failed, not a bug in any one piece of code.
The pipeline
- Step 1
App registers for notifications
The app asks the OS to register it for push, which first requires notification permission — the same runtime permission model from the earlier lesson.
- Step 2
Device receives a push token
The OS hands back a token: an identifier for this specific app installation on this specific device.
- Step 3
Token is sent to your backend
Your app sends that token to your own server, which stores it against the relevant user/account.
- Step 4
Server sends a message via APNs/FCM
Your server (or a service in front of it) sends the notification content to Apple's push service (iOS) or Google's FCM (Android), targeting the stored token.
- Step 5
The push service delivers it to the device
APNs or FCM is responsible for actually getting the message to the device, waking it if necessary, and handing it to the OS.
- Step 6
The OS displays it, or hands it to your app
A notification-style payload can be shown by the OS directly; a data-only payload is handed to your app's own code to decide what to do with.
Registration and the token
Before your server can send a single push message, the app has to register for notifications and receive back a device push token — an opaque identifier tied to this specific app installation on this specific device. This step depends on the permissions lesson: registering for notifications requires the user to have granted notification permission, since a push token is useless (and, on iOS in particular, unobtainable) without it.
// Conceptual shape of registration — the exact API depends on the library
async function registerForPushNotifications() {
const permission = await requestNotificationPermission();
if (permission !== "granted") return null;
const token = await getDevicePushToken();
await sendTokenToBackend(token); // your own server needs this to target the device later
return token;
}That token then needs to reach your backend somehow — usually a simple API call made right after registration succeeds. Without this step, your server has no way to address this particular device at all; the token is the entire addressing mechanism.
APNs and FCM: the platform push services
Your backend never contacts a device directly. Instead, it sends the message to whichever platform push service owns that token: APNs (Apple Push Notification service) for iOS devices, or FCM (Firebase Cloud Messaging) for Android devices. Those services are operated by Apple and Google respectively, and they're responsible for the genuinely hard part — maintaining a persistent, efficient connection to every device running that platform, and actually delivering the message even if the device is asleep or the app isn't running.
Notification payloads vs. data payloads
A real and useful distinction in how a push message is structured:
- A notification-style payload carries a title/body the OS knows how to display on its own — a banner can appear even if your app isn't running at all, with no app code involved in the decision to show it.
- A data-only payload carries arbitrary data with no OS-displayable content, and is instead handed to your app's own code to decide what to do with — show a custom notification, update some local state, silently sync something in the background. This gives you more control, at the cost of real platform constraints on when (and whether) your app's code actually gets to run to process it, especially if the app was fully terminated rather than just backgrounded.
Where Expo simplifies this
Handling APNs and FCM as two entirely separate integrations — different token formats, different request formats, different credentials — is real, recurring work. Expo provides a unified push notification service that sits in front of both: your backend sends one consistent kind of request to Expo's service, using an Expo-issued push token, and Expo is responsible for translating and routing that to APNs or FCM as appropriate. This is a real, current, and commonly used option for teams already building on Expo — it doesn't remove the underlying pipeline above, it just collapses the "talk to two different platform services" step into one.
What to remember
- The real pipeline: register (requires permission) → receive a device token → send it to your server → your server messages APNs/FCM → the platform service delivers it → the OS displays it or your app handles it.
- Registering for push depends on notification permission already having been granted — this lesson builds directly on the permissions lesson.
- APNs (iOS) and FCM (Android) are the actual delivery services; your backend never talks to a device directly.
- A notification-style payload can be shown by the OS with no app code involved; a data-only payload is handed to your app's own code, with real constraints on when it can run.
- Expo's push service is a real, current option for handling both platforms through one unified API instead of integrating APNs and FCM separately.
Check yourself
4 questions · pass 3/4 to unlock Avoiding Unnecessary Re-Renders
1.What does an app receive after registering for push notifications, before anything can be sent to it?
2.Which system actually delivers a push message to an iOS or Android device?
3.What's the real distinction between a 'notification' payload and a 'data' payload in push messaging?
4.What does Expo's push notification service provide that saves you from handling APNs and FCM separately?
4 left to answer