Created
July 12, 2016 18:02
-
-
Save pedro/766baf701185e81e4a9708693e52dfb4 to your computer and use it in GitHub Desktop.
Push Notifications error patch for React Native in iOS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Push notification manager from react-native 0.28, plus patch to handle errors correctly: | |
// https://github.com/facebook/react-native/pull/4277 | |
/** | |
* 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 "RCTEventEmitter.h" | |
@interface RCTPushNotificationManager : RCTEventEmitter | |
+ (void)didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings; | |
+ (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken; | |
+ (void)didReceiveRemoteNotification:(NSDictionary *)notification; | |
+ (void)didReceiveLocalNotification:(UILocalNotification *)notification; | |
+ (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Push notification manager from react-native 0.28, plus patch to handle errors correctly: | |
// https://github.com/facebook/react-native/pull/4277 | |
/** | |
* 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"; | |
@interface RCTPushNotificationManager () | |
@property (nonatomic, copy) RCTPromiseResolveBlock requestPermissionsResolveBlock; | |
@end | |
@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 | |
RCT_EXPORT_MODULE() | |
- (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] copy]; | |
return @{@"initialNotification": RCTNullIfNil(initialNotification)}; | |
} | |
+ (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 | |
{ | |
NSMutableDictionary *details = [NSMutableDictionary new]; | |
if (notification.alertBody) { | |
details[@"alertBody"] = notification.alertBody; | |
} | |
if (notification.userInfo) { | |
details[@"userInfo"] = RCTJSONClean(notification.userInfo); | |
} | |
[[NSNotificationCenter defaultCenter] postNotificationName:RCTLocalNotificationReceived | |
object:self | |
userInfo:details]; | |
} | |
+ (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error | |
{ | |
NSDictionary *errorInfo = @{ | |
@"error" : @YES, | |
@"info": RCTJSErrorFromNSError(error) | |
}; | |
[[NSNotificationCenter defaultCenter] postNotificationName:RCTRemoteNotificationsRegistered | |
object:self | |
userInfo:errorInfo]; | |
} | |
- (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 (self.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), | |
}; | |
self.requestPermissionsResolveBlock(notificationTypes); | |
self.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 (self.requestPermissionsResolveBlock != nil) { | |
RCTLogError(@"Cannot call requestPermissions twice before the first has returned."); | |
return; | |
} | |
self.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 *)userInfo) | |
{ | |
for (UILocalNotification *notification in [UIApplication sharedApplication].scheduledLocalNotifications) { | |
__block BOOL matchesAll = YES; | |
NSDictionary *notificationInfo = notification.userInfo; | |
[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] copy]; | |
resolve(RCTNullIfNil(initialNotification)); | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment