Notifiche push su mobile: quello che impari solo sbagliando
6 aprile 2026

La documentazione di Expo per le notifiche push è buona. È chiara, ha esempi funzionanti, e si riesce a fare una demo in un pomeriggio.
Poi metti in produzione e scopri che la demo era il caso facile.
Come funziona il flusso base
Expo gestisce le notifiche push tramite il proprio servizio di inoltro: la tua app chiede il permesso all'utente, ottiene un Expo Push Token, e tu lo salvi sul backend. Quando vuoi mandare una notifica, fai una richiesta all'API di Expo, che si occupa di inoltrarla ad APNs (Apple) o FCM (Google) a seconda della piattaforma.
// lato client: ottenere il token
const { status } = await Notifications.requestPermissionsAsync();
if (status !== "granted") return;
const token = await Notifications.getExpoPushTokenAsync({
projectId: Constants.expoConfig?.extra?.eas?.projectId,
});
// salvare sul backend
await api.post("/push-token", { token: token.data });
// lato server: mandare una notifica
await fetch("https://exp.host/--/api/v2/push/send", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
to: token,
title: "Nuova foto in galleria",
body: "Qualcuno ha caricato una foto. Vai a vederla!",
}),
});
Semplice. Funziona. E poi ti arrivano i primi problemi reali.
Il primo problema: i permessi non arrivano
Su iOS, il sistema dei permessi è rigido. Se l'utente nega la prima richiesta — o non risponde, o pressa "Non consentire" per errore — non puoi più chiedere di nuovo dall'app. L'unica via è le Impostazioni di sistema.
Questo significa che la UX del momento in cui chiedi i permessi è critica. Se mostri la dialog di sistema troppo presto — prima che l'utente capisca cosa fa l'app e perché le notifiche sarebbero utili — perdi una percentuale significativa di utenti per sempre.
Per ReD Sposi ho aspettato il completamento dell'RSVP per chiedere i permessi. In quel momento l'utente ha già investito qualcosa nell'app, capisce cosa fa, e ha un motivo concreto per voler ricevere aggiornamenti. La conversion rate sul permesso è stata molto più alta.
Su Android il sistema è più permissivo — le notifiche sono abilitate di default fino ad Android 13, e anche dopo è più facile riattivarle. Ma proprio per questo gli utenti Android sono meno abituati a scegliere consapevolmente, e vale la pena di contestualizzare comunque la richiesta.
Il secondo problema: i token scadono e cambiano
Un Expo Push Token non è permanente. Cambia se l'utente reinstalla l'app, se cambia dispositivo, e in alcuni casi dopo aggiornamenti significativi dell'app o del sistema operativo.
Se non gestisci questa casistica, accumuli token vecchi nel database e le notifiche smettono di arrivare silenziosamente — nessun errore esplicito, nessuna segnalazione, semplicemente non succede nulla.
La soluzione è aggiornare il token ogni volta che l'app si avvia in foreground:
useEffect(() => {
const syncToken = async () => {
const token = await Notifications.getExpoPushTokenAsync({ ... });
// upsert sul backend: aggiorna se esiste, crea se non esiste
await api.post("/push-token", { token: token.data });
};
syncToken();
}, []);
Sul backend, quando una notifica fallisce con DeviceNotRegistered, rimuovi il token dal database. Expo restituisce questo errore nel body della risposta per i token non più validi.
const result = await sendPushNotification(token, payload);
if (result.details?.error === "DeviceNotRegistered") {
await PushToken.deleteOne({ token });
}
Il terzo problema: la differenza iOS/Android sui canali
Su Android 8+, le notifiche appartengono a canali — categorie configurabili dall'utente nelle impostazioni di sistema. Se non definisci un canale, Expo ne crea uno di default, ma perdi la possibilità di dare all'utente controllo granulare.
Per ReD Sposi ho definito due canali: uno per le notifiche sociali (nuove foto, messaggi in chat) e uno per le notifiche importanti (conferma RSVP, aggiornamenti del programma). L'utente può silenziare il primo senza perdere il secondo.
await Notifications.setNotificationChannelAsync("social", {
name: "Aggiornamenti social",
importance: Notifications.AndroidImportance.DEFAULT,
sound: true,
});
await Notifications.setNotificationChannelAsync("important", {
name: "Aggiornamenti importanti",
importance: Notifications.AndroidImportance.HIGH,
sound: true,
});
Su iOS i canali non esistono — il sistema di controllo è diverso (le categorie sono più limitate). Quindi questa logica vale solo Android, ed è facile dimenticarsene se sviluppi principalmente su simulatore iOS.
Quello che non testa il simulatore
Il simulatore iOS non supporta le notifiche push. Punto. Puoi testare la gestione delle notifiche in foreground con Notifications.scheduleNotificationAsync, ma le push reali richiedono un dispositivo fisico.
Questo crea un gap di testing che si sente: durante lo sviluppo, il flusso di notifiche push è sempre un po' opaco perché non puoi vederlo funzionare comodamente. La prima volta che lo vedi funzionare davvero è spesso in produzione, con utenti reali.
Ho risolto in parte con un endpoint di test nel pannello admin che manda una notifica push a me stesso. Non è elegante, ma è stato il modo più veloce per verificare che il flusso funzionasse end-to-end senza aspettare che succedesse qualcosa di reale nell'app.
Vale la fatica?
Sì. Per ReD Sposi le notifiche push sono la differenza tra un'app che gli invitati usano attivamente e un'app che aprono una volta e dimenticano.
Quando viene caricata una nuova foto, una notifica porta gli invitati in galleria. Quando la chat è attiva, una notifica mantiene la conversazione viva. Quando aggiorno il programma della giornata, tutti lo sanno entro pochi secondi.
La documentazione ti porta al settanta percento. Il restante trenta — token management, permessi iOS, canali Android, testing su dispositivo fisico — lo impari in produzione. È frustrante, ma non è evitabile.