mirror of
https://github.com/status-im/react-native.git
synced 2025-01-16 20:44:10 +00:00
57d45235ec
Summary: Currently if an RN app is started in response to a remote notification, that notification's data is available on startup via `PushNotificationIOS.popInitialNotification()`. However, if the app is started in response to a "local" notification, that information is never passed in. This PR modifies the `popInitialNotification` behavior so it will return the notification used to launch the app, no matter if it was local or remote. I've tested this change in my app and ensured that when the app is woken up with a `localNotification` it's passed in to `PushNotificationIOS.popInitialNotification`. I've also tested that the `localNotification` event continues working as before. Closes https://github.com/facebook/react-native/pull/7765 Differential Revision: D3417267 Pulled By: nicklockwood fbshipit-source-id: 0b5b432e9a75dda7d3c50289a3bf0f1c1ffcf061
358 lines
14 KiB
Objective-C
358 lines
14 KiB
Objective-C
/**
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*/
|
|
|
|
#import "RCTPushNotificationManager.h"
|
|
|
|
#import "RCTBridge.h"
|
|
#import "RCTConvert.h"
|
|
#import "RCTEventDispatcher.h"
|
|
#import "RCTUtils.h"
|
|
|
|
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0
|
|
|
|
#define UIUserNotificationTypeAlert UIRemoteNotificationTypeAlert
|
|
#define UIUserNotificationTypeBadge UIRemoteNotificationTypeBadge
|
|
#define UIUserNotificationTypeSound UIRemoteNotificationTypeSound
|
|
#define UIUserNotificationTypeNone UIRemoteNotificationTypeNone
|
|
#define UIUserNotificationType UIRemoteNotificationType
|
|
|
|
#endif
|
|
|
|
NSString *const RCTLocalNotificationReceived = @"LocalNotificationReceived";
|
|
NSString *const RCTRemoteNotificationReceived = @"RemoteNotificationReceived";
|
|
NSString *const RCTRemoteNotificationsRegistered = @"RemoteNotificationsRegistered";
|
|
NSString *const RCTRegisterUserNotificationSettings = @"RegisterUserNotificationSettings";
|
|
|
|
NSString *const RCTErrorUnableToRequestPermissions = @"E_UNABLE_TO_REQUEST_PERMISSIONS";
|
|
|
|
@implementation RCTConvert (UILocalNotification)
|
|
|
|
+ (UILocalNotification *)UILocalNotification:(id)json
|
|
{
|
|
NSDictionary<NSString *, id> *details = [self NSDictionary:json];
|
|
UILocalNotification *notification = [UILocalNotification new];
|
|
notification.fireDate = [RCTConvert NSDate:details[@"fireDate"]] ?: [NSDate date];
|
|
notification.alertBody = [RCTConvert NSString:details[@"alertBody"]];
|
|
notification.alertAction = [RCTConvert NSString:details[@"alertAction"]];
|
|
notification.soundName = [RCTConvert NSString:details[@"soundName"]] ?: UILocalNotificationDefaultSoundName;
|
|
notification.userInfo = [RCTConvert NSDictionary:details[@"userInfo"]];
|
|
notification.category = [RCTConvert NSString:details[@"category"]];
|
|
if (details[@"applicationIconBadgeNumber"]) {
|
|
notification.applicationIconBadgeNumber = [RCTConvert NSInteger:details[@"applicationIconBadgeNumber"]];
|
|
}
|
|
return notification;
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation RCTPushNotificationManager
|
|
{
|
|
RCTPromiseResolveBlock _requestPermissionsResolveBlock;
|
|
}
|
|
|
|
static NSDictionary *RCTFormatLocalNotification(UILocalNotification *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));
|
|
return formattedLocalNotification;
|
|
}
|
|
|
|
RCT_EXPORT_MODULE()
|
|
|
|
- (dispatch_queue_t)methodQueue
|
|
{
|
|
return dispatch_get_main_queue();
|
|
}
|
|
|
|
- (void)startObserving
|
|
{
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(handleLocalNotificationReceived:)
|
|
name:RCTLocalNotificationReceived
|
|
object:nil];
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(handleRemoteNotificationReceived:)
|
|
name:RCTRemoteNotificationReceived
|
|
object:nil];
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(handleRemoteNotificationsRegistered:)
|
|
name:RCTRemoteNotificationsRegistered
|
|
object:nil];
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(handleRegisterUserNotificationSettings:)
|
|
name:RCTRegisterUserNotificationSettings
|
|
object:nil];
|
|
}
|
|
|
|
- (void)stopObserving
|
|
{
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
}
|
|
|
|
- (NSArray<NSString *> *)supportedEvents
|
|
{
|
|
return @[@"localNotificationReceived",
|
|
@"remoteNotificationReceived",
|
|
@"remoteNotificationsRegistered"];
|
|
}
|
|
|
|
// TODO: Once all JS call sites for popInitialNotification have
|
|
// been removed we can get rid of this
|
|
- (NSDictionary<NSString *, id> *)constantsToExport
|
|
{
|
|
NSDictionary<NSString *, id> *initialNotification =
|
|
self.bridge.launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
|
|
|
|
UILocalNotification *initialLocalNotification =
|
|
self.bridge.launchOptions[UIApplicationLaunchOptionsLocalNotificationKey];
|
|
|
|
if (initialNotification) {
|
|
return @{@"initialNotification": [initialNotification copy]};
|
|
} else if (initialLocalNotification) {
|
|
return @{@"initialNotification": RCTFormatLocalNotification(initialLocalNotification)};
|
|
} else {
|
|
return @{@"initialNotification": (id)kCFNull};
|
|
}
|
|
}
|
|
|
|
+ (void)didRegisterUserNotificationSettings:(__unused UIUserNotificationSettings *)notificationSettings
|
|
{
|
|
if ([UIApplication instancesRespondToSelector:@selector(registerForRemoteNotifications)]) {
|
|
[[UIApplication sharedApplication] registerForRemoteNotifications];
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTRegisterUserNotificationSettings
|
|
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:RCTRemoteNotificationsRegistered
|
|
object:self
|
|
userInfo:@{@"deviceToken" : [hexString copy]}];
|
|
}
|
|
|
|
+ (void)didReceiveRemoteNotification:(NSDictionary *)notification
|
|
{
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTRemoteNotificationReceived
|
|
object:self
|
|
userInfo:notification];
|
|
}
|
|
|
|
+ (void)didReceiveLocalNotification:(UILocalNotification *)notification
|
|
{
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTLocalNotificationReceived
|
|
object:self
|
|
userInfo:RCTFormatLocalNotification(notification)];
|
|
}
|
|
|
|
- (void)handleLocalNotificationReceived:(NSNotification *)notification
|
|
{
|
|
[self sendEventWithName:@"localNotificationReceived" body:notification.userInfo];
|
|
}
|
|
|
|
- (void)handleRemoteNotificationReceived:(NSNotification *)notification
|
|
{
|
|
[self sendEventWithName:@"remoteNotificationReceived" body:notification.userInfo];
|
|
}
|
|
|
|
- (void)handleRemoteNotificationsRegistered:(NSNotification *)notification
|
|
{
|
|
[self sendEventWithName:@"remoteNotificationsRegistered" body:notification.userInfo];
|
|
}
|
|
|
|
- (void)handleRegisterUserNotificationSettings:(NSNotification *)notification
|
|
{
|
|
if (_requestPermissionsResolveBlock == nil) {
|
|
return;
|
|
}
|
|
|
|
UIUserNotificationSettings *notificationSettings = notification.userInfo[@"notificationSettings"];
|
|
NSDictionary *notificationTypes = @{
|
|
@"alert": @((notificationSettings.types & UIUserNotificationTypeAlert) > 0),
|
|
@"sound": @((notificationSettings.types & UIUserNotificationTypeSound) > 0),
|
|
@"badge": @((notificationSettings.types & UIUserNotificationTypeBadge) > 0),
|
|
};
|
|
|
|
_requestPermissionsResolveBlock(notificationTypes);
|
|
_requestPermissionsResolveBlock = nil;
|
|
}
|
|
|
|
/**
|
|
* 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(RCTErrorUnableToRequestPermissions, 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;
|
|
}
|
|
|
|
_requestPermissionsResolveBlock = resolve;
|
|
|
|
UIUserNotificationType types = UIUserNotificationTypeNone;
|
|
if (permissions) {
|
|
if ([RCTConvert BOOL:permissions[@"alert"]]) {
|
|
types |= UIUserNotificationTypeAlert;
|
|
}
|
|
if ([RCTConvert BOOL:permissions[@"badge"]]) {
|
|
types |= UIUserNotificationTypeBadge;
|
|
}
|
|
if ([RCTConvert BOOL:permissions[@"sound"]]) {
|
|
types |= UIUserNotificationTypeSound;
|
|
}
|
|
} else {
|
|
types = UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound;
|
|
}
|
|
|
|
UIApplication *app = RCTSharedApplication();
|
|
if ([app respondsToSelector:@selector(registerUserNotificationSettings:)]) {
|
|
UIUserNotificationSettings *notificationSettings =
|
|
[UIUserNotificationSettings settingsForTypes:(NSUInteger)types categories:nil];
|
|
[app registerUserNotificationSettings:notificationSettings];
|
|
} else {
|
|
[app registerForRemoteNotificationTypes:(NSUInteger)types];
|
|
}
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(abandonPermissions)
|
|
{
|
|
[RCTSharedApplication() unregisterForRemoteNotifications];
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(checkPermissions:(RCTResponseSenderBlock)callback)
|
|
{
|
|
if (RCTRunningInAppExtension()) {
|
|
callback(@[@{@"alert": @NO, @"badge": @NO, @"sound": @NO}]);
|
|
return;
|
|
}
|
|
|
|
NSUInteger types = 0;
|
|
if ([UIApplication instancesRespondToSelector:@selector(currentUserNotificationSettings)]) {
|
|
types = [RCTSharedApplication() currentUserNotificationSettings].types;
|
|
} else {
|
|
|
|
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0
|
|
|
|
types = [RCTSharedApplication() enabledRemoteNotificationTypes];
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
callback(@[@{
|
|
@"alert": @((types & UIUserNotificationTypeAlert) > 0),
|
|
@"badge": @((types & UIUserNotificationTypeBadge) > 0),
|
|
@"sound": @((types & UIUserNotificationTypeSound) > 0),
|
|
}]);
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(presentLocalNotification:(UILocalNotification *)notification)
|
|
{
|
|
[RCTSharedApplication() presentLocalNotificationNow:notification];
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(scheduleLocalNotification:(UILocalNotification *)notification)
|
|
{
|
|
[RCTSharedApplication() scheduleLocalNotification:notification];
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(cancelAllLocalNotifications)
|
|
{
|
|
[RCTSharedApplication() cancelAllLocalNotifications];
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(cancelLocalNotifications:(NSDictionary<NSString *, id> *)userInfo)
|
|
{
|
|
for (UILocalNotification *notification in [UIApplication sharedApplication].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) {
|
|
[[UIApplication sharedApplication] cancelLocalNotification:notification];
|
|
}
|
|
}
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(getInitialNotification:(RCTPromiseResolveBlock)resolve
|
|
reject:(__unused RCTPromiseRejectBlock)reject)
|
|
{
|
|
NSDictionary<NSString *, id> *initialNotification =
|
|
self.bridge.launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
|
|
|
|
UILocalNotification *initialLocalNotification =
|
|
self.bridge.launchOptions[UIApplicationLaunchOptionsLocalNotificationKey];
|
|
|
|
if (initialNotification) {
|
|
resolve([initialNotification copy]);
|
|
} else if (initialLocalNotification) {
|
|
resolve(RCTFormatLocalNotification(initialLocalNotification));
|
|
} else {
|
|
resolve((id)kCFNull);
|
|
}
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(getScheduledLocalNotifications:(RCTResponseSenderBlock)callback)
|
|
{
|
|
NSArray<UILocalNotification *> *scheduledLocalNotifications = [UIApplication sharedApplication].scheduledLocalNotifications;
|
|
NSMutableArray<NSDictionary *> *formattedScheduledLocalNotifications = [NSMutableArray new];
|
|
for (UILocalNotification *notification in scheduledLocalNotifications) {
|
|
[formattedScheduledLocalNotifications addObject:RCTFormatLocalNotification(notification)];
|
|
}
|
|
callback(@[formattedScheduledLocalNotifications]);
|
|
}
|
|
|
|
@end
|