diff --git a/packages/react-native/Libraries/PushNotificationIOS/RCTPushNotificationManager.mm b/packages/react-native/Libraries/PushNotificationIOS/RCTPushNotificationManager.mm
index 8b3be6bccc33..d5c6bc7270a0 100644
--- a/packages/react-native/Libraries/PushNotificationIOS/RCTPushNotificationManager.mm
+++ b/packages/react-native/Libraries/PushNotificationIOS/RCTPushNotificationManager.mm
@@ -322,7 +322,18 @@ - (void)handleRemoteNotificationRegistrationError:(NSNotification *)notification
*/
RCT_EXPORT_METHOD(setApplicationIconBadgeNumber : (double)number)
{
- RCTSharedApplication().applicationIconBadgeNumber = number;
+ NSInteger badgeCount = (NSInteger)lround(number);
+ if (@available(iOS 16.0, *)) {
+ [UNUserNotificationCenter.currentNotificationCenter
+ setBadgeCount:badgeCount
+ withCompletionHandler:^(NSError *_Nullable error) {
+ if (error != nil) {
+ RCTLogWarn(@"Failed to set application icon badge number: %@", error);
+ }
+ }];
+ } else {
+ RCTSharedApplication().applicationIconBadgeNumber = badgeCount;
+ }
}
/**
diff --git a/packages/rn-tester/js/examples/PushNotificationIOS/PushNotificationIOSExample.js b/packages/rn-tester/js/examples/PushNotificationIOS/PushNotificationIOSExample.js
new file mode 100644
index 000000000000..60d0565ea099
--- /dev/null
+++ b/packages/rn-tester/js/examples/PushNotificationIOS/PushNotificationIOSExample.js
@@ -0,0 +1,98 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @flow strict-local
+ * @format
+ */
+
+'use strict';
+
+import type {RNTesterModuleExample} from '../../types/RNTesterTypes';
+
+import RNTesterText from '../../components/RNTesterText';
+import * as React from 'react';
+import {useCallback, useState} from 'react';
+import {Button, StyleSheet, View} from 'react-native';
+import PushNotificationIOS from 'react-native/Libraries/PushNotificationIOS/PushNotificationIOS';
+
+// Fires setApplicationIconBadgeNumber many times back-to-back.
+const RAPID_FIRE_COUNT = 50;
+
+function BadgeExample(): React.Node {
+ const [reported, setReported] = useState(null);
+
+ const updateBadge = useCallback((n: number) => {
+ PushNotificationIOS.setApplicationIconBadgeNumber(n);
+ }, []);
+
+ const readBadge = useCallback(() => {
+ PushNotificationIOS.getApplicationIconBadgeNumber((n: number) => {
+ setReported(n);
+ });
+ }, []);
+
+ const rapidFire = useCallback(() => {
+ for (let i = 1; i <= RAPID_FIRE_COUNT; i++) {
+ PushNotificationIOS.setApplicationIconBadgeNumber(i);
+ }
+ }, []);
+
+ return (
+
+
+ {reported == null
+ ? 'Tap "Read current badge" to query the badge number.'
+ : `Last read badge number: ${reported}`}
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ wrapper: {
+ padding: 10,
+ },
+ text: {
+ marginBottom: 10,
+ },
+ button: {
+ marginVertical: 4,
+ },
+});
+
+exports.framework = 'React';
+exports.title = 'PushNotificationIOS';
+exports.category = 'iOS';
+exports.documentationURL = 'https://reactnative.dev/docs/pushnotificationios';
+exports.description = 'Application icon badge number via UserNotifications.';
+
+exports.examples = [
+ {
+ title: 'Application icon badge number',
+ description:
+ 'Set/clear/read the app icon badge. "Rapid-fire" stresses the setter ' +
+ 'to confirm the main thread never blocks on the UserNotifications XPC ' +
+ 'round-trip.',
+ render(): React.Node {
+ return ;
+ },
+ },
+] as Array;
diff --git a/packages/rn-tester/js/utils/RNTesterList.ios.js b/packages/rn-tester/js/utils/RNTesterList.ios.js
index e3260a576dc3..334560b91e85 100644
--- a/packages/rn-tester/js/utils/RNTesterList.ios.js
+++ b/packages/rn-tester/js/utils/RNTesterList.ios.js
@@ -277,6 +277,11 @@ const APIs: Array = (
key: 'PointerEventsExample',
module: require('../examples/PointerEvents/PointerEventsExample'),
},
+ {
+ key: 'PushNotificationIOSExample',
+ module: require('../examples/PushNotificationIOS/PushNotificationIOSExample'),
+ category: 'iOS',
+ },
{
key: 'RCTRootViewIOSExample',
module: require('../examples/RCTRootView/RCTRootViewIOSExample'),