Since Android 13, notifications need the runtime permission POST_NOTIFICATIONS, and Android shows the prompt only once the app has a channel. Ask at a moment the user understands: after two refusals, only Settings helps.
export async function registerForPush() {
// Android 13+: create a channel first, or the permission prompt never appears
await Notifications.setNotificationChannelAsync('new-arrivals', {
name: 'New arrivals', importance: Notifications.AndroidImportance.HIGH,
});
let { status } = await Notifications.getPermissionsAsync();
if (status !== 'granted') ({ status } = await Notifications.requestPermissionsAsync());
console.log('notification permission:', status, '| real device:', Device.isDevice);
if (status !== 'granted') return null;
try {
const projectId = Constants.expoConfig?.extra?.eas?.projectId; // set by `eas init`
const { data } = await Notifications.getExpoPushTokenAsync({ projectId });
return data; // ExponentPushToken[...]
// ...catch: log 'no push token:' and return nullThe first launch showed the prompt (New Arrival Alerts's figure); after Allow, logcat read:
'notification permission:', 'granted', '| real device:', false 'no push token:', 'Unable to get Firebase Messaging instance. Did you configure `googleServicesFile` path in app config? ... Default FirebaseApp is not initialized in this process com.anonymous.booknest. ...'
An Expo 6,418 push token wraps the platform's device token, and on Android that comes from Firebase 1 , which needs a Firebase project and its google-services.json (next section). This emulator image has Google Play 1 services, so with those files it could receive pushes; Device.isDevice lets you skip the attempt elsewhere.