expo: [ios][notifications] On iOS only, notifications to killed app are not delivered
Summary
Notifications to a killed app on iOS are not delivered. They are delivered to a running app on iOS. On Android, the same code build receives notifications in both a killed or running app.
Managed or bare workflow? If you have ios/
or android/
directories in your project, the answer is bare!
bare
What platform(s) does this occur on?
iOS
SDK Version (managed workflow only)
No response
Environment
% expo diagnostics
Expo CLI 4.3.2 environment info:
System:
OS: macOS 10.15.7
Shell: 5.7.1 - /bin/zsh
Binaries:
Node: 14.15.1 - /usr/local/bin/node
Yarn: 1.22.10 - /usr/local/bin/yarn
npm: 6.14.8 - /usr/local/bin/npm
Watchman: 4.9.0 - /usr/local/bin/watchman
Managers:
CocoaPods: 1.10.1 - /Users/me/.rbenv/shims/pod
SDKs:
iOS SDK:
Platforms: iOS 14.4, DriverKit 20.2, macOS 11.1, tvOS 14.3, watchOS 7.2
IDEs:
Xcode: 12.4/12D4e - /usr/bin/xcodebuild
npmPackages:
expo: ^40.0.1 => 40.0.1
react: 16.13.1 => 16.13.1
react-dom: 16.13.1 => 16.13.1
react-native: ~0.63.4 => 0.63.4
react-native-web: ~0.13.12 => 0.13.18
npmGlobalPackages:
expo-cli: 4.3.2
Expo Workflow: bare
Dependencies from package.json
:
"dependencies": {
"@expo/react-native-action-sheet": "^3.8.0",
"@expo/vector-icons": "^12.0.2",
"@ptomasroos/react-native-multi-slider": "^2.2.2",
"@react-native-community/art": "https://github.com/Brewskey/art",
"@react-native-community/image-editor": "^2.3.0",
"@react-native-community/masked-view": "0.1.10",
"@react-native-community/netinfo": "5.9.7",
"@react-native-community/picker": "1.6.6",
"@react-native-community/slider": "3.0.3",
"@react-native-community/viewpager": "4.2.0",
"@react-navigation/bottom-tabs": "^5.11.2",
"@react-navigation/drawer": "^5.11.4",
"@react-navigation/native": "^5.8.10",
"@react-navigation/stack": "^5.12.8",
"axios": "^0.21.0",
"emoji-mart-native": "^0.6.2-beta",
"expo": "^40.0.1",
"expo-app-loader-provider": "~8.0.0",
"expo-app-loading": "^1.0.0",
"expo-asset": "~8.2.1",
"expo-av": "^8.7.0",
"expo-camera": "~9.1.0",
"expo-constants": "~9.3.3",
"expo-device": "~2.4.0",
"expo-file-system": "~9.3.0",
"expo-image-manipulator": "~8.4.0",
"expo-intent-launcher": "~8.4.0",
"expo-linear-gradient": "~8.4.0",
"expo-linking": "~2.0.0",
"expo-localization": "~9.1.0",
"expo-notifications": "~0.8.2",
"expo-permissions": "~10.0.0",
"expo-splash-screen": "~0.8.1",
"expo-status-bar": "~1.0.3",
"expo-video-player": "^1.6.0",
"hoist-non-react-statics": "^3.3.2",
"i18n-js": "^3.7.1",
"moment": "^2.28.0",
"react": "16.13.1",
"react-native": "https://github.com/expo/react-native/archive/sdk-40.0.1.tar.gz",
"react-native-amazing-cropper": "https://github.com/myplaceonline/react-native-amazing-cropper.git",
"react-native-calendar-picker": "^7.0.3",
"react-native-calendars": "^1.437.0",
"react-native-elements": "^3.0.0-alpha.1",
"react-native-emoji-board": "^1.2.1",
"react-native-emoji-input": "https://github.com/myplaceonline/react-native-emoji-input#customize",
"react-native-emoji-selector": "^0.1.9",
"react-native-gesture-handler": "~1.8.0",
"react-native-gifted-chat": "^0.16.3",
"react-native-google-places-autocomplete": "^2.1.1",
"react-native-image-rotate": "^2.1.0",
"react-native-keyboard-spacer": "^0.4.1",
"react-native-linear-gradient": "^2.5.6",
"react-native-paper": "^4.4.1",
"react-native-progress": "https://github.com/Brewskey/react-native-progress",
"react-native-progress-circle": "^2.1.0",
"react-native-progress-wheel": "^1.0.5",
"react-native-radio-button": "^2.0.1",
"react-native-ratings": "^7.3.0",
"react-native-reanimated": "~1.13.0",
"react-native-safe-area-context": "3.1.9",
"react-native-screens": "~2.15.2",
"react-native-semi-circle-progress": "https://github.com/pajicf/react-native-semi-circle-progress",
"react-native-sideswipe": "^1.5.0",
"react-native-simple-radio-button": "^2.7.4",
"react-native-snap-carousel": "^3.9.1",
"react-native-svg": "12.1.0",
"react-native-swipe-list-view": "^3.2.5",
"react-native-vector-icons": "^7.1.0",
"react-native-webview": "11.0.0",
"react-native-wizard": "^2.1.0",
"react-number-format": "^4.4.1",
"react-redux": "^7.2.2",
"react-tween-state": "^0.1.5",
"redux": "^4.0.5",
"tslib": "^2.0.1"
},
Reproducible demo or steps to reproduce from a blank project
Below is the relevant code which is at the top of App.js
so that it runs as soon as possible. Neither the debug lines in Notifications.setNotificationHandler
nor Notifications.addNotificationResponseReceivedListener
are printed when a notification is clicked on a killed app.
import React from 'react';
import { Text, View } from 'react-native';
import Constants from 'expo-constants';
// Initialize core debug code
let accumulatedDebug = "";
let deviceInfo = null;
const MAX_DEBUG = 1000000;
const MAX_CONSOLE_LOG_SIZE = 9500;
function appendToLog(str) {
accumulatedDebug = accumulatedDebug + "\n" + str;
// Limit how much memory debug is storing
if (accumulatedDebug.length > MAX_DEBUG) {
accumulatedDebug = "[... truncated ...] " + accumulatedDebug.substring(MAX_DEBUG / 2);
}
}
function getLogPrefix() {
let result = "[" + new Date().toISOString() + "] ";
if (deviceInfo != null && deviceInfo.installationId) {
result += "{" + deviceInfo.installationId + "} ";
}
return result;
}
function info(message, skipConsole) {
const str = getLogPrefix() + message;
if (!skipConsole) {
console.log(str);
}
appendToLog(str);
}
function debug(message) {
const str = getLogPrefix() + message;
if (str.length > MAX_CONSOLE_LOG_SIZE) {
console.debug(str.substr(0, MAX_CONSOLE_LOG_SIZE) + " [...] TRUNCATED");
} else {
console.debug(str);
}
appendToLog(str);
}
function toJSONPretty(obj) {
try {
return JSON.stringify(obj, null, 2);
} catch (e) {
info("Error in toJSONPretty for " + (typeof obj) + " : " + e);
}
}
info("Application started. Version: " + Constants.manifest.version);
// Register for notifications as early as possible
// https://docs.expo.io/versions/latest/sdk/notifications/
// https://docs.expo.io/push-notifications/sending-notifications/
import * as Notifications from 'expo-notifications';
const NOTIFICATION_RECEIVED_WHEN_APP_IN_FOREGROUND = "notificationWithAppInForeground";
const NOTIFICATION_CLICKED_AND_APP_IN_FOREGROUND_OR_BACKGROUND_OR_CLOSED = "notificationClicked";
function globalReceivedNotificationForeground(notification) {
globalProcessNotification(notification, NOTIFICATION_RECEIVED_WHEN_APP_IN_FOREGROUND);
}
function globalClickedNotification(response) {
globalProcessNotification(response.notification, NOTIFICATION_CLICKED_AND_APP_IN_FOREGROUND_OR_BACKGROUND_OR_CLOSED);
}
let isAppScreensWithDrawerComponentReady = false;
let pendingNotification = null;
function globalProcessNotification(notification, source) {
// Most notifications are actually handled in AppScreensWithDrawerComponent
info("globalProcessNotification from " + source + ", notification: " + toJSONPretty(notification));
try {
const data = notification.request.content.data;
info("globalProcessNotification isAppScreensWithDrawerComponentReady: " + isAppScreensWithDrawerComponentReady);
if (pendingNotification == null && !isAppScreensWithDrawerComponentReady && source == NOTIFICATION_CLICKED_AND_APP_IN_FOREGROUND_OR_BACKGROUND_OR_CLOSED) {
info("globalProcessNotification queued pendingNotification");
pendingNotification = notification;
}
} catch (e) {
handleException(e, "globalProcessNotification");
}
}
function registerGlobalNotificationHandler() {
try {
// https://docs.expo.io/push-notifications/receiving-notifications/
// behavior for when notifications are received while your app is foregrounded
Notifications.setNotificationHandler({
handleNotification: (notification) => {
info("Notifications: handleNotification notification: " + toJSONPretty(notification));
return {
shouldShowAlert: true,
shouldPlaySound: false,
shouldSetBadge: false,
};
},
handleSuccess: (notificationId) => {
info("Notifications: handleSuccess notificationId: " + notificationId);
},
handleError: (error) => {
info("Notifications: handleError error: " + error);
},
});
// There are two different subscriptions so that you can easily address cases where a
// notification comes in while your app is open and foregrounded, and cases where a notification
// comes in while your app is backgrounded or closed, and the user taps on the notification.
// fired whenever a notification is received while the app is foregrounded
Notifications.addNotificationReceivedListener(globalReceivedNotificationForeground);
// fired whenever a user taps on or interacts with a notification (works when app is foregrounded, backgrounded, or killed).
Notifications.addNotificationResponseReceivedListener(globalClickedNotification);
} catch (e) {
info("Error in registerGlobalNotificationHandler: " + e);
}
}
registerGlobalNotificationHandler();
export default function App() {
[...]
I’m a bit confused by all the issues related to this (#12228, #11933, #11343, #9866, #9478, #7787, and #6943). It seems like that since I’m using the bare workflow, I can just run expo install expo-notifications@0.11.0
to get the latest fix for this; is that right? And do I need to use useLastNotificationResponse
(or getLastNotificationResponseAsync
) or should Notifications.addNotificationResponseReceivedListener
work?
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Comments: 15 (8 by maintainers)
I’m working with SDK 41 also, and my addNotificationResponseReceivedListener() is not called when the app is killed.
My code is :
in App.js
The listener is only work if app work in background.
@oron11 @kevgrig I’m working with Expo SDK 41 and expo-notifications
~0.11.6
and I’m still having the same issue: no matter where I placeaddNotificationResponseReceivedListener
, if the app is killed this listener is not working. What expo-notifications version do you have? And where did you ended up placing the listener?