Skip to content

Commit

Permalink
Migrate notification scheduling to UserNotifications (#41039)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: #41039

## Changelog:

[iOS][Breaking] - repeatInterval is deprecated in PushNotificationIOS. Use fireDate and the new fireIntervalSeconds.

Reviewed By: philIip

Differential Revision: D50277316

fbshipit-source-id: ddcc2d2fc9d89d2bacac296848109e98c95c0107
  • Loading branch information
Ingrid Wang authored and facebook-github-bot committed Oct 20, 2023
1 parent d4fe550 commit a129993
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,34 @@ type Permissions = {|

type Notification = {|
+alertTitle?: ?string,
// Actual type: string | number
+fireDate?: ?number,
+alertBody?: ?string,
+userInfo?: ?Object,
/**
* Identifier for the notification category. See the [Apple documentation](https://developer.apple.com/documentation/usernotifications/declaring_your_actionable_notification_types)
* for more details.
*/
+category?: ?string,
// Actual type: 'year' | 'month' | 'week' | 'day' | 'hour' | 'minute'
+repeatInterval?: ?string,
/**
* Actual type: string | number
*
* Schedule notifications using EITHER `fireDate` or `fireIntervalSeconds`.
* If both are specified, `fireDate` takes precedence.
* If you use `presentLocalNotification`, both will be ignored
* and the notification will be shown immediately.
*/
+fireDate?: ?number,
/**
* Seconds from now to display the notification.
*
* Schedule notifications using EITHER `fireDate` or `fireIntervalSeconds`.
* If both are specified, `fireDate` takes precedence.
* If you use `presentLocalNotification`, both will be ignored
* and the notification will be shown immediately.
*/
+fireIntervalSeconds?: ?number,
/** Badge count to display on the app icon. */
+applicationIconBadgeNumber?: ?number,
/** Whether to silence the notification sound. */
+isSilent?: ?boolean,
/**
* Custom notification sound to play. Write-only: soundName will be null when
Expand All @@ -37,6 +57,8 @@ type Notification = {|
+soundName?: ?string,
/** DEPRECATED. This was used for iOS's legacy UILocalNotification. */
+alertAction?: ?string,
/** DEPRECATED. Use `fireDate` or `fireIntervalSeconds` instead. */
+repeatInterval?: ?string,
|};

export interface Spec extends TurboModule {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,48 +25,59 @@
static NSString *const kErrorUnableToRequestPermissions = @"E_UNABLE_TO_REQUEST_PERMISSIONS";

#if !TARGET_OS_UIKITFORMAC
@implementation RCTConvert (NSCalendarUnit)

RCT_ENUM_CONVERTER(
NSCalendarUnit,
(@{
@"year" : @(NSCalendarUnitYear),
@"month" : @(NSCalendarUnitMonth),
@"week" : @(NSCalendarUnitWeekOfYear),
@"day" : @(NSCalendarUnitDay),
@"hour" : @(NSCalendarUnitHour),
@"minute" : @(NSCalendarUnitMinute)
}),
0,
integerValue)

@end

@interface RCTPushNotificationManager () <NativePushNotificationManagerIOSSpec>
@property (nonatomic, strong) NSMutableDictionary *remoteNotificationCallbacks;
@end

@implementation RCTConvert (UILocalNotification)
@implementation RCTConvert (UNNotificationContent)

+ (UILocalNotification *)UILocalNotification:(id)json
+ (UNNotificationContent *)UNNotificationContent:(id)json
{
NSDictionary<NSString *, id> *details = [self NSDictionary:json];
BOOL isSilent = [RCTConvert BOOL:details[@"isSilent"]];
UILocalNotification *notification = [UILocalNotification new];
notification.alertTitle = [RCTConvert NSString:details[@"alertTitle"]];
notification.fireDate = [RCTConvert NSDate:details[@"fireDate"]] ?: [NSDate date];
notification.alertBody = [RCTConvert NSString:details[@"alertBody"]];
notification.alertAction = [RCTConvert NSString:details[@"alertAction"]];
notification.userInfo = [RCTConvert NSDictionary:details[@"userInfo"]];
notification.category = [RCTConvert NSString:details[@"category"]];
notification.repeatInterval = [RCTConvert NSCalendarUnit:details[@"repeatInterval"]];
UNMutableNotificationContent *content = [UNMutableNotificationContent new];
content.title = [RCTConvert NSString:details[@"alertTitle"]];
content.body = [RCTConvert NSString:details[@"alertBody"]];
content.userInfo = [RCTConvert NSDictionary:details[@"userInfo"]];
content.categoryIdentifier = [RCTConvert NSString:details[@"category"]];
if (details[@"applicationIconBadgeNumber"]) {
notification.applicationIconBadgeNumber = [RCTConvert NSInteger:details[@"applicationIconBadgeNumber"]];
content.badge = [RCTConvert NSNumber:details[@"applicationIconBadgeNumber"]];
}
if (!isSilent) {
notification.soundName = [RCTConvert NSString:details[@"soundName"]] ?: UILocalNotificationDefaultSoundName;
NSString *soundName = [RCTConvert NSString:details[@"soundName"]];
content.sound =
soundName ? [UNNotificationSound soundNamed:details[@"soundName"]] : [UNNotificationSound defaultSound];
}

return content;
}

+ (NSDictionary<NSString *, id> *)NSDictionaryForNotification:
(JS::NativePushNotificationManagerIOS::Notification &)notification
{
// Note: alertAction is not set, as it is no longer relevant with UNNotification
NSMutableDictionary *notificationDict = [NSMutableDictionary new];
notificationDict[@"alertTitle"] = notification.alertTitle();
notificationDict[@"alertBody"] = notification.alertBody();
notificationDict[@"userInfo"] = notification.userInfo();
notificationDict[@"category"] = notification.category();
if (notification.fireIntervalSeconds()) {
notificationDict[@"fireIntervalSeconds"] = @(*notification.fireIntervalSeconds());
}
return notification;
if (notification.fireDate()) {
notificationDict[@"fireDate"] = @(*notification.fireDate());
}
if (notification.applicationIconBadgeNumber()) {
notificationDict[@"applicationIconBadgeNumber"] = @(*notification.applicationIconBadgeNumber());
}
if (notification.isSilent()) {
notificationDict[@"isSilent"] = @(*notification.isSilent());
if ([notificationDict[@"isSilent"] isEqualToNumber:@(NO)]) {
notificationDict[@"soundName"] = notification.soundName();
}
}
return notificationDict;
}

@end
Expand Down Expand Up @@ -420,50 +431,44 @@ - (void)handleRemoteNotificationRegistrationError:(NSNotification *)notification

RCT_EXPORT_METHOD(presentLocalNotification : (JS::NativePushNotificationManagerIOS::Notification &)notification)
{
NSMutableDictionary *notificationDict = [NSMutableDictionary new];
notificationDict[@"alertTitle"] = notification.alertTitle();
notificationDict[@"alertBody"] = notification.alertBody();
notificationDict[@"alertAction"] = notification.alertAction();
notificationDict[@"userInfo"] = notification.userInfo();
notificationDict[@"category"] = notification.category();
notificationDict[@"repeatInterval"] = notification.repeatInterval();
if (notification.fireDate()) {
notificationDict[@"fireDate"] = @(*notification.fireDate());
}
if (notification.applicationIconBadgeNumber()) {
notificationDict[@"applicationIconBadgeNumber"] = @(*notification.applicationIconBadgeNumber());
}
if (notification.isSilent()) {
notificationDict[@"isSilent"] = @(*notification.isSilent());
if ([notificationDict[@"isSilent"] isEqualToNumber:@(NO)]) {
notificationDict[@"soundName"] = notification.soundName();
}
}
[RCTSharedApplication() presentLocalNotificationNow:[RCTConvert UILocalNotification:notificationDict]];
NSDictionary<NSString *, id> *notificationDict = [RCTConvert NSDictionaryForNotification:notification];
UNNotificationContent *content = [RCTConvert UNNotificationContent:notificationDict];
UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:0.1
repeats:NO];
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:[[NSUUID UUID] UUIDString]
content:content
trigger:trigger];

UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center addNotificationRequest:request withCompletionHandler:nil];
}

RCT_EXPORT_METHOD(scheduleLocalNotification : (JS::NativePushNotificationManagerIOS::Notification &)notification)
{
NSMutableDictionary *notificationDict = [NSMutableDictionary new];
notificationDict[@"alertTitle"] = notification.alertTitle();
notificationDict[@"alertBody"] = notification.alertBody();
notificationDict[@"alertAction"] = notification.alertAction();
notificationDict[@"userInfo"] = notification.userInfo();
notificationDict[@"category"] = notification.category();
notificationDict[@"repeatInterval"] = notification.repeatInterval();
if (notification.fireDate()) {
notificationDict[@"fireDate"] = @(*notification.fireDate());
}
if (notification.applicationIconBadgeNumber()) {
notificationDict[@"applicationIconBadgeNumber"] = @(*notification.applicationIconBadgeNumber());
}
if (notification.isSilent()) {
notificationDict[@"isSilent"] = @(*notification.isSilent());
if ([notificationDict[@"isSilent"] isEqualToNumber:@(NO)]) {
notificationDict[@"soundName"] = notification.soundName();
}
NSDictionary<NSString *, id> *notificationDict = [RCTConvert NSDictionaryForNotification:notification];
UNNotificationContent *content = [RCTConvert UNNotificationContent:notificationDict];

UNNotificationTrigger *trigger = nil;
if (notificationDict[@"fireDate"]) {
NSDate *fireDate = [RCTConvert NSDate:notificationDict[@"fireDate"]] ?: [NSDate date];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components =
[calendar components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour |
NSCalendarUnitMinute | NSCalendarUnitSecond)
fromDate:fireDate];
trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:components repeats:NO];
} else if (notificationDict[@"fireIntervalSeconds"]) {
trigger = [UNTimeIntervalNotificationTrigger
triggerWithTimeInterval:[notificationDict[@"fireIntervalSeconds"] doubleValue]
repeats:NO];
}
[RCTSharedApplication() scheduleLocalNotification:[RCTConvert UILocalNotification:notificationDict]];

UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:[[NSUUID UUID] UUIDString]
content:content
trigger:trigger];

UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center addNotificationRequest:request withCompletionHandler:nil];
}

RCT_EXPORT_METHOD(cancelAllLocalNotifications)
Expand Down

0 comments on commit a129993

Please sign in to comment.