I've implemented the push notification like below and it works when app is opened but when the app is killed in ios and the user press the push notification the app opens and then crashes. For android, it's not asking for permission.
Asking for permission doesn't trigger on android somehow.
function useNotificationPermission() {
const [expoPushToken, setExpoPushToken] = useState('')
const [notification, setNotification] = useState<Notifications.Notification | null>(null)
const notificationListener = useRef<Notifications.Subscription | null>(null)
const responseListener = useRef<Notifications.Subscription | null>(null)
useEffect(() => {
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: false,
shouldSetBadge: false,
}),
})
async function registerForPushNotificationsAsync() {
let token
if (Platform.OS === 'android') {
Notifications.setNotificationChannelAsync('default', {
name: 'default',
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: '#FF231F7C',
})
}
if (Device.isDevice) {
const { status: existingStatus } = await Notifications.getPermissionsAsync()
let finalStatus = existingStatus
console.log({ existingStatus })
if (existingStatus !== 'granted') {
const { status } = await Notifications.requestPermissionsAsync()
console.log({ requestStatus: status })
finalStatus = status
}
if (finalStatus !== 'granted') {
// alert('Failed to get permission for push notification!')
return
}
token = await Notifications.getExpoPushTokenAsync({
projectId: Constants?.expoConfig?.extra?.eas?.projectId,
})
} else {
// alert('Must use physical device for Push Notifications')
}
return token?.data
}
registerForPushNotificationsAsync().then((token) => setExpoPushToken(token))
notificationListener.current = Notifications?.addNotificationReceivedListener(
(notification) => {
setNotification(notification)
}
)
responseListener.current = Notifications?.addNotificationResponseReceivedListener(
(response) => {
console.log(response)
}
)
return () => {
if (notificationListener.current) {
Notifications.removeNotificationSubscription(notificationListener.current)
}
if (responseListener.current) {
Notifications.removeNotificationSubscription(responseListener.current)
}
}
}, [])
return { expoPushToken, notification }
}
Handling the notification in the app
function useNotificationObserver() {
useEffect(() => {
let isMounted = true
function redirect(notification: Notifications.Notification) {
const url = notification?.request?.content?.data?.url || '/'
if (url) {
router.push(url)
}
}
Notifications.getLastNotificationResponseAsync().then((response) => {
if (!isMounted || !response?.notification) {
return
}
redirect(response?.notification)
})
const subscription = Notifications.addNotificationResponseReceivedListener((response) => {
redirect(response?.notification)
})
return () => {
isMounted = false
subscription.remove()
}
}, [])
}
Root of the app
export default function Layout (){
useNotificationObserver()
useNotificationPermission()
return (<App/>)
}
When sending the notification in the payload I've added content-available: 1 as well.