Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

'UIUserNotificationSettings' is deprecated: first deprecated in iOS 10.0 #14

Closed
robertying opened this issue May 29, 2019 · 6 comments
Closed

Comments

@robertying
Copy link

Feature Request

Make PushNotification compatible with iOS 10 new UserNotifications Framework

Why it is needed

Xcode says 'UIUserNotificationSettings' is deprecated: first deprecated in iOS 10.0 - Use UserNotifications Framework's UNNotificationSettings

iOS 13 is almost there, so it is quite natural to ask this update ;)

Possible implementation

  • Need to change all delegate methods to accept UserNotifications stuff, such as UNNotificationSettings
  • Update documentations for changes in AppDelegate.m
@apppro123
Copy link

Yeah that would be VERY helpful.

And can you (i could help) adding more support for repeat (f.e. daysOfWeek, ...).

Thank you!

@apppro123
Copy link

Does somebody know what RCTSharedApplication() is and for what im using it for?

@apppro123
Copy link

RNPushNotificationIOS.m file

`/**

  • Copyright (c) Facebook, Inc. and its affiliates.
  • This source code is licensed under the MIT license found in the
  • LICENSE file in the root directory of this source tree.
    */

#import "RNCPushNotificationIOS.h"

#import <UserNotifications/UserNotifications.h>

#import <React/RCTBridge.h>
#import <React/RCTConvert.h>
#import <React/RCTEventDispatcher.h>
#import <React/RCTUtils.h>

NSString *const RCTRemoteNotificationReceived = @"RemoteNotificationReceived";

static NSString *const kLocalNotificationReceived = @"LocalNotificationReceived";
static NSString *const kRemoteNotificationsRegistered = @"RemoteNotificationsRegistered";
static NSString *const kRegisterUserNotificationSettings = @"RegisterUserNotificationSettings";
static NSString *const kRemoteNotificationRegistrationFailed = @"RemoteNotificationRegistrationFailed";

static NSString *const kErrorUnableToRequestPermissions = @"E_UNABLE_TO_REQUEST_PERMISSIONS";

#if !TARGET_OS_TV
@implementation RCTConvert (NSCalendarUnit)

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

@EnD

@interface RNCPushNotificationIOS ()
@Property (nonatomic, strong) NSMutableDictionary *remoteNotificationCallbacks;
@EnD

@implementation RCTConvert (UNNotificationRequest)

  • (UNNotificationRequest *)N:(id)json
    {
    NSDictionary<NSString *, id> *details = [self NSDictionary:json];
    BOOL isSilent = [RCTConvert BOOL:details[@"isSilent"]];
    //content
    UNMutableNotificationContent *content = [UNMutableNotificationContent new];
    content.title = [RCTConvert NSString:details[@"alertTitle"]];
    content.body = [RCTConvert NSString:details[@"alertBody"]];
    content.userInfo = [RCTConvert NSDictionary:details[@"userInfo"]];
    //action/category with NSNotificationCategory/-Action at launch time and
    //then you can assign it to a notification by setting content.categoryIndentifier = ...
    if (details[@"applicationIconBadgeNumber"]) {
    content.badge = [RCTConvert NSNumber:details[@"applicationIconBadgeNumber"]];
    }
    // if (!isSilent) {
    // content.sound = [RCTConvert NSString:details[@"soundName"]] ?: [UNNotificationSound defaultSound];
    // }
    //trigger
    NSDate *startDate = [RCTConvert NSDate:details[@"start"]] ?: [NSDate date];
    NSDate *endDate = [RCTConvert NSDate:details[@"end"]] ?: [NSDate date];
    NSDateComponents *triggerDate = [[NSCalendar currentCalendar]
    components:NSCalendarUnitYear +
    NSCalendarUnitMonth + NSCalendarUnitDay +
    NSCalendarUnitHour + NSCalendarUnitMinute +
    NSCalendarUnitSecond fromDate:startDate toDate:endDate options:0];

    UNCalendarNotificationTrigger *trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:triggerDate repeats:NO];
    UNNotificationRequest *notification = [UNNotificationRequest requestWithIdentifier:[RCTConvert NSString:details[@"id"]] content: content trigger: trigger];
    return notification;
    }

RCT_ENUM_CONVERTER(UIBackgroundFetchResult, (@{
@"UIBackgroundFetchResultNewData": @(UIBackgroundFetchResultNewData),
@"UIBackgroundFetchResultNoData": @(UIBackgroundFetchResultNoData),
@"UIBackgroundFetchResultFailed": @(UIBackgroundFetchResultFailed),
}), UIBackgroundFetchResultNoData, integerValue)

@EnD
#endif //TARGET_OS_TV

@implementation RNCPushNotificationIOS
{
RCTPromiseResolveBlock _requestPermissionsResolveBlock;
}

#if !TARGET_OS_TV
//
//static NSDictionary *RCTFormatLocalNotification(UNNotificationRequest *notification)
//{
// NSMutableDictionary *formattedLocalNotification = [NSMutableDictionary dictionary];
// if (notification.fireDate) {
// NSDateFormatter *formatter = [NSDateFormatter new];
// [formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"];
// NSString *fireDateString = [formatter stringFromDate:notification.fireDate];
// formattedLocalNotification[@"fireDate"] = fireDateString;
// }
// formattedLocalNotification[@"alertAction"] = RCTNullIfNil(notification.alertAction);
// formattedLocalNotification[@"alertBody"] = RCTNullIfNil(notification.alertBody);
// formattedLocalNotification[@"applicationIconBadgeNumber"] = @(notification.applicationIconBadgeNumber);
// formattedLocalNotification[@"category"] = RCTNullIfNil(notification.category);
// formattedLocalNotification[@"soundName"] = RCTNullIfNil(notification.soundName);
// formattedLocalNotification[@"userInfo"] = RCTNullIfNil(RCTJSONClean(notification.userInfo));
// formattedLocalNotification[@"remote"] = @no;
// return formattedLocalNotification;
//}

static NSDictionary *RCTFormatUNNotification(UNNotification *notification)
{
NSMutableDictionary *formattedNotification = [NSMutableDictionary dictionary];
UNNotificationContent *content = notification.request.content;

formattedNotification[@"identifier"] = notification.request.identifier;

if (notification.date) {
NSDateFormatter *formatter = [NSDateFormatter new];
[formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"];
NSString *dateString = [formatter stringFromDate:notification.date];
formattedNotification[@"date"] = dateString;
}

formattedNotification[@"title"] = RCTNullIfNil(content.title);
formattedNotification[@"body"] = RCTNullIfNil(content.body);
formattedNotification[@"category"] = RCTNullIfNil(content.categoryIdentifier);
formattedNotification[@"thread-id"] = RCTNullIfNil(content.threadIdentifier);
formattedNotification[@"userInfo"] = RCTNullIfNil(RCTJSONClean(content.userInfo));

return formattedNotification;
}

#endif //TARGET_OS_TV

RCT_EXPORT_MODULE()

  • (dispatch_queue_t)methodQueue
    {
    return dispatch_get_main_queue();
    }

#if !TARGET_OS_TV

  • (void)startObserving
    {
    [[NSNotificationCenter defaultCenter] addObserver:self
    selector:@selector(handleLocalNotificationReceived:)
    name:kLocalNotificationReceived
    object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self
    selector:@selector(handleRemoteNotificationReceived:)
    name:RCTRemoteNotificationReceived
    object:nil];
    // [[NSNotificationCenter defaultCenter] addObserver:self
    // selector:@selector(handleRegisterUserNotificationSettings:)
    // name:kRegisterUserNotificationSettings
    // object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self
    selector:@selector(handleRemoteNotificationsRegistered:)
    name:kRemoteNotificationsRegistered
    object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self
    selector:@selector(handleRemoteNotificationRegistrationError:)
    name:kRemoteNotificationRegistrationFailed
    object:nil];
    }

  • (void)stopObserving
    {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    }

  • (NSArray<NSString *> *)supportedEvents
    {
    return @[@"localNotificationReceived",
    @"remoteNotificationReceived",
    @"remoteNotificationsRegistered",
    @"remoteNotificationRegistrationError"];
    }

  • (void)didRegisterUserNotificationSettings:(__unused UNNotificationSettings *)notificationSettings
    {
    if ([UIApplication instancesRespondToSelector:@selector(registerForRemoteNotifications)]) {
    [RCTSharedApplication() registerForRemoteNotifications];
    [[NSNotificationCenter defaultCenter] postNotificationName:kRegisterUserNotificationSettings
    object:self
    userInfo:@{@"notificationSettings": notificationSettings}];
    }
    }

  • (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
    {
    NSMutableString *hexString = [NSMutableString string];
    NSUInteger deviceTokenLength = deviceToken.length;
    const unsigned char *bytes = deviceToken.bytes;
    for (NSUInteger i = 0; i < deviceTokenLength; i++) {
    [hexString appendFormat:@"%02x", bytes[i]];
    }
    [[NSNotificationCenter defaultCenter] postNotificationName:kRemoteNotificationsRegistered
    object:self
    userInfo:@{@"deviceToken" : [hexString copy]}];
    }

  • (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
    {
    [[NSNotificationCenter defaultCenter] postNotificationName:kRemoteNotificationRegistrationFailed
    object:self
    userInfo:@{@"error": error}];
    }

  • (void)didReceiveRemoteNotification:(NSDictionary *)notification
    {
    NSDictionary *userInfo = @{@"notification": notification};
    [[NSNotificationCenter defaultCenter] postNotificationName:RCTRemoteNotificationReceived
    object:self
    userInfo:userInfo];
    }

  • (void)didReceiveRemoteNotification:(NSDictionary *)notification
    fetchCompletionHandler:(RNCRemoteNotificationCallback)completionHandler
    {
    NSDictionary *userInfo = @{@"notification": notification, @"completionHandler": completionHandler};
    [[NSNotificationCenter defaultCenter] postNotificationName:RCTRemoteNotificationReceived
    object:self
    userInfo:userInfo];
    }

//+ (void)didReceiveLocalNotification:(UNNotificationRequest *)notification
//{
// [[NSNotificationCenter defaultCenter] postNotificationName:kLocalNotificationReceived
// object:self
// userInfo:RCTFormatLocalNotification(notification)];
//}

  • (void)handleLocalNotificationReceived:(NSNotification *)notification
    {
    [self sendEventWithName:@"localNotificationReceived" body:notification.userInfo];
    }

  • (void)handleRemoteNotificationReceived:(NSNotification *)notification
    {
    NSMutableDictionary *remoteNotification = [NSMutableDictionary dictionaryWithDictionary:notification.userInfo[@"notification"]];
    RNCRemoteNotificationCallback completionHandler = notification.userInfo[@"completionHandler"];
    NSString *notificationId = [[NSUUID UUID] UUIDString];
    remoteNotification[@"notificationId"] = notificationId;
    remoteNotification[@"remote"] = @yES;
    if (completionHandler) {
    if (!self.remoteNotificationCallbacks) {
    // Lazy initialization
    self.remoteNotificationCallbacks = [NSMutableDictionary dictionary];
    }
    self.remoteNotificationCallbacks[notificationId] = completionHandler;
    }

    [self sendEventWithName:@"remoteNotificationReceived" body:remoteNotification];
    }

  • (void)handleRemoteNotificationsRegistered:(NSNotification *)notification
    {
    [self sendEventWithName:@"remoteNotificationsRegistered" body:notification.userInfo];
    }

  • (void)handleRemoteNotificationRegistrationError:(NSNotification *)notification
    {
    NSError *error = notification.userInfo[@"error"];
    NSDictionary *errorDetails = @{
    @"message": error.localizedDescription,
    @"code": @(error.code),
    @"details": error.userInfo,
    };
    [self sendEventWithName:@"remoteNotificationRegistrationError" body:errorDetails];
    }

//- (void)handleRegisterUserNotificationSettings:(NSNotification *)notification
//{
// if (_requestPermissionsResolveBlock == nil) {
// return;
// }
//
// UNNotificationSettings *notificationSettings = notification.userInfo[@"notificationSettings"];
// NSDictionary *notificationTypes = @{
// @"alert": @((notificationSettings.types & UNAuthorizationOptionAlert) > 0),
// @"sound": @((notificationSettings.types & UNAuthorizationOptionSound) > 0),
// @"badge": @((notificationSettings.types & UNAuthorizationOptionBadge) > 0),
// };
//
// _requestPermissionsResolveBlock(notificationTypes);
// // Clean up listener added in requestPermissions
// [self removeListeners:1];
// _requestPermissionsResolveBlock = nil;
//}

RCT_EXPORT_METHOD(onFinishRemoteNotification:(NSString *)notificationId fetchResult:(UIBackgroundFetchResult)result) {
RNCRemoteNotificationCallback completionHandler = self.remoteNotificationCallbacks[notificationId];
if (!completionHandler) {
RCTLogError(@"There is no completion handler with notification id: %@", notificationId);
return;
}
completionHandler(result);
[self.remoteNotificationCallbacks removeObjectForKey:notificationId];
}

/**

  • Update the application icon badge number on the home screen
    */
    RCT_EXPORT_METHOD(setApplicationIconBadgeNumber:(NSInteger)number)
    {
    RCTSharedApplication().applicationIconBadgeNumber = number;
    }

/**

  • Get the current application icon badge number on the home screen
    */
    RCT_EXPORT_METHOD(getApplicationIconBadgeNumber:(RCTResponseSenderBlock)callback)
    {
    callback(@[@(RCTSharedApplication().applicationIconBadgeNumber)]);
    }

RCT_EXPORT_METHOD(requestPermissions:(NSDictionary *)permissions
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
if (RCTRunningInAppExtension()) {
reject(kErrorUnableToRequestPermissions, nil, RCTErrorWithMessage(@"Requesting push notifications is currently unavailable in an app extension"));
return;
}

if (_requestPermissionsResolveBlock != nil) {
RCTLogError(@"Cannot call requestPermissions twice before the first has returned.");
return;
}

// Add a listener to make sure that startObserving has been called
[self addListener:@"remoteNotificationsRegistered"];
_requestPermissionsResolveBlock = resolve;
//
// UNAuthorizationOptions types = UNAuthorizationOptionNone;
// if (permissions) {
// if ([RCTConvert BOOL:permissions[@"alert"]]) {
// types |= UNAuthorizationOptionAlert;
// }
// if ([RCTConvert BOOL:permissions[@"badge"]]) {
// types |= UNAuthorizationOptionBadge;
// }
// if ([RCTConvert BOOL:permissions[@"sound"]]) {
// types |= UNAuthorizationOptionSound;
// }
// } else {
// types = UNAuthorizationOptionAlert | UNAuthorizationOptionBadge | UNAuthorizationOptionSound;
// }
//
// UNNotificationSettings *notificationSettings =
// [UNNotificationSettings settingsForTypes:types categories:nil];
// [RCTSharedApplication() registerUserNotificationSettings:notificationSettings];

UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];

UNAuthorizationOptions options = UNAuthorizationOptionAlert + UNAuthorizationOptionSound + UNAuthorizationOptionBadge;
[center requestAuthorizationWithOptions:options
completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (!granted) {
NSLog(@"Oops, no access");
}
}];
}

RCT_EXPORT_METHOD(abandonPermissions)
{
[RCTSharedApplication() unregisterForRemoteNotifications];
}

RCT_EXPORT_METHOD(checkPermissions:(RCTResponseSenderBlock)callback)
{
if (RCTRunningInAppExtension()) {
callback(@[@{@"alert": @no, @"badge": @no, @"sound": @no}]);
return;
}
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
callback(@[@{
@"alert": @([settings alertSetting] == UNNotificationSettingEnabled),
@"badge": @([settings badgeSetting] == UNNotificationSettingEnabled),
@"sound": @([settings soundSetting] == UNNotificationSettingEnabled),
}]);
}];
// NSUInteger types = [RCTSharedApplication() currentUserNotificationSettings].types;
// callback(@[@{
// @"alert": @((types & UNAuthorizationOptionAlert) > 0),
// @"badge": @((types & UNAuthorizationOptionBadge) > 0),
// @"sound": @((types & UNAuthorizationOptionSound) > 0),
// }]);
}

//RCT_EXPORT_METHOD(presentLocalNotification:(UNNotificationRequest *)notification)
//{
// [RCTSharedApplication() presentLocalNotificationNow:notification];
//}

RCT_EXPORT_METHOD(scheduleLocalNotification:(UNNotificationRequest *)notification)
{
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center addNotificationRequest:notification withCompletionHandler:^(NSError * _Nullable error){
if (error != nil) {
NSLog(@"Unable to Add Notification Request");
}
}];
}

RCT_EXPORT_METHOD(cancelAllLocalNotifications)
{
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center removeAllPendingNotificationRequests];
[center removeAllDeliveredNotifications];
}

RCT_EXPORT_METHOD(cancelLocalNotifications:(NSArray<NSString *> *)identifiers)
{
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center removePendingNotificationRequestsWithIdentifiers: identifiers];
// for (UILocalNotification *notification in RCTSharedApplication().scheduledLocalNotifications) {
// __block BOOL matchesAll = YES;
// NSDictionary<NSString *, id> *notificationInfo = notification.userInfo;
// // Note: we do this with a loop instead of just isEqualToDictionary:
// // because we only require that all specified userInfo values match the
// // notificationInfo values - notificationInfo may contain additional values
// // which we don't care about.
// [userInfo enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
// if (![notificationInfo[key] isEqual:obj]) {
// matchesAll = NO;
// *stop = YES;
// }
// }];
// if (matchesAll) {
// [RCTSharedApplication() cancelLocalNotification:notification];
// }
// }
}

//RCT_EXPORT_METHOD(getInitialNotification:(RCTPromiseResolveBlock)resolve
// reject:(__unused RCTPromiseRejectBlock)reject)
//{
// NSMutableDictionary<NSString *, id> *initialNotification =
// [self.bridge.launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] mutableCopy];
//
// UILocalNotification *initialLocalNotification =
// self.bridge.launchOptions[UIApplicationLaunchOptionsLocalNotificationKey];
//
// if (initialNotification) {
// initialNotification[@"remote"] = @yES;
// resolve(initialNotification);
// } else if (initialLocalNotification) {
// resolve(RCTFormatLocalNotification(initialLocalNotification));
// } else {
// resolve((id)kCFNull);
// }
//}

//RCT_EXPORT_METHOD(getScheduledLocalNotifications:(RCTResponseSenderBlock)callback)
//{
// NSArray<UNNotificationRequest *> *scheduledLocalNotifications = RCTSharedApplication().scheduledLocalNotifications;
// NSMutableArray<NSDictionary *> *formattedScheduledLocalNotifications = [NSMutableArray new];
// for (UNNotificationRequest *notification in scheduledLocalNotifications) {
// [formattedScheduledLocalNotifications addObject:RCTFormatLocalNotification(notification)];
// }
// callback(@[formattedScheduledLocalNotifications]);
//}

RCT_EXPORT_METHOD(removeAllDeliveredNotifications)
{
if ([UNUserNotificationCenter class]) {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center removeAllDeliveredNotifications];
}
}

RCT_EXPORT_METHOD(removeDeliveredNotifications:(NSArray<NSString *> *)identifiers)
{
if ([UNUserNotificationCenter class]) {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center removeDeliveredNotificationsWithIdentifiers:identifiers];
}
}

RCT_EXPORT_METHOD(getDeliveredNotifications:(RCTResponseSenderBlock)callback)
{
if ([UNUserNotificationCenter class]) {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center getDeliveredNotificationsWithCompletionHandler:^(NSArray<UNNotification *> *_Nonnull notifications) {
NSMutableArray<NSDictionary *> *formattedNotifications = [NSMutableArray new];

  for (UNNotification *notification in notifications) {
    [formattedNotifications addObject:RCTFormatUNNotification(notification)];
  }
  callback(@[formattedNotifications]);
}];

}
}

#else //TARGET_OS_TV

  • (NSArray<NSString *> *)supportedEvents
    {
    return @[];
    }

#endif //TARGET_OS_TV

@EnD
`

First changes i have made. Not tested at all (just that app still starts). And i have just commented some methods. Any help is welcome!

@jjhampton
Copy link

Is this also related to methods such as didReceiveRemoteNotification being used by react-native-push-notification-ios ?

https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623117-application?language=objc

I see that method used within RNCPushNotificationIOS.m, but is apparently labeled deprecated in the link above?

@chrisbobbe
Copy link

chrisbobbe commented Sep 15, 2020

#66 went a long way toward doing this, I think!

I'd suggest a few followups to that:

It's no longer necessary for people to override the deprecated didRegisterUserNotificationSettings in AppDelegate.m; that can be removed in the example app. It's no longer necessary because the interesting code that that override invoked has been removed, with the move to the newer API.

With the use of APIs that are new in iOS 10 (e.g., here; see Apple's doc on that), it should be made explicit that pre-iOS 10 is no longer supported. (I'm not sure if pre-iOS 10 APIs were used before #66.) A good place to do that, in addition to the docs, is in the platform designation in the Podspec (see CocoaPods doc).

@Naturalclar
Copy link
Collaborator

Added support for UNNotification in version 1.7.0 🎉
Please open another issue for a specific feature request within UNNotification capabilities :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants