From 57d45235ecb5fc54c0b80d1147a38e712dd9c431 Mon Sep 17 00:00:00 2001 From: Kyle Corbitt Date: Fri, 10 Jun 2016 05:15:54 -0700 Subject: [PATCH] Correctly passes localNotification to app on wakeup 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 --- .../RCTPushNotificationManager.m | 75 ++++++++++++------- 1 file changed, 48 insertions(+), 27 deletions(-) diff --git a/Libraries/PushNotificationIOS/RCTPushNotificationManager.m b/Libraries/PushNotificationIOS/RCTPushNotificationManager.m index 98f991825..a84f4d959 100644 --- a/Libraries/PushNotificationIOS/RCTPushNotificationManager.m +++ b/Libraries/PushNotificationIOS/RCTPushNotificationManager.m @@ -31,10 +31,6 @@ NSString *const RCTRegisterUserNotificationSettings = @"RegisterUserNotification NSString *const RCTErrorUnableToRequestPermissions = @"E_UNABLE_TO_REQUEST_PERMISSIONS"; -@interface RCTPushNotificationManager () -@property (nonatomic, copy) RCTPromiseResolveBlock requestPermissionsResolveBlock; -@end - @implementation RCTConvert (UILocalNotification) + (UILocalNotification *)UILocalNotification:(id)json @@ -56,12 +52,15 @@ NSString *const RCTErrorUnableToRequestPermissions = @"E_UNABLE_TO_REQUEST_PERMI @end @implementation RCTPushNotificationManager +{ + RCTPromiseResolveBlock _requestPermissionsResolveBlock; +} -static NSDictionary *formatLocalNotification(UILocalNotification *notification) +static NSDictionary *RCTFormatLocalNotification(UILocalNotification *notification) { NSMutableDictionary *formattedLocalNotification = [NSMutableDictionary dictionary]; if (notification.fireDate) { - NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; + NSDateFormatter *formatter = [NSDateFormatter new]; [formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"]; NSString *fireDateString = [formatter stringFromDate:notification.fireDate]; formattedLocalNotification[@"fireDate"] = fireDateString; @@ -77,6 +76,11 @@ static NSDictionary *formatLocalNotification(UILocalNotification *notification) RCT_EXPORT_MODULE() +- (dispatch_queue_t)methodQueue +{ + return dispatch_get_main_queue(); +} + - (void)startObserving { [[NSNotificationCenter defaultCenter] addObserver:self @@ -114,8 +118,18 @@ RCT_EXPORT_MODULE() - (NSDictionary *)constantsToExport { NSDictionary *initialNotification = - [self.bridge.launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] copy]; - return @{@"initialNotification": RCTNullIfNil(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 @@ -150,16 +164,9 @@ RCT_EXPORT_MODULE() + (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]; + userInfo:RCTFormatLocalNotification(notification)]; } - (void)handleLocalNotificationReceived:(NSNotification *)notification @@ -179,7 +186,7 @@ RCT_EXPORT_MODULE() - (void)handleRegisterUserNotificationSettings:(NSNotification *)notification { - if (self.requestPermissionsResolveBlock == nil) { + if (_requestPermissionsResolveBlock == nil) { return; } @@ -190,8 +197,8 @@ RCT_EXPORT_MODULE() @"badge": @((notificationSettings.types & UIUserNotificationTypeBadge) > 0), }; - self.requestPermissionsResolveBlock(notificationTypes); - self.requestPermissionsResolveBlock = nil; + _requestPermissionsResolveBlock(notificationTypes); + _requestPermissionsResolveBlock = nil; } /** @@ -219,12 +226,12 @@ RCT_EXPORT_METHOD(requestPermissions:(NSDictionary *)permissions return; } - if (self.requestPermissionsResolveBlock != nil) { + if (_requestPermissionsResolveBlock != nil) { RCTLogError(@"Cannot call requestPermissions twice before the first has returned."); return; } - self.requestPermissionsResolveBlock = resolve; + _requestPermissionsResolveBlock = resolve; UIUserNotificationType types = UIUserNotificationTypeNone; if (permissions) { @@ -298,11 +305,15 @@ RCT_EXPORT_METHOD(cancelAllLocalNotifications) [RCTSharedApplication() cancelAllLocalNotifications]; } -RCT_EXPORT_METHOD(cancelLocalNotifications:(NSDictionary *)userInfo) +RCT_EXPORT_METHOD(cancelLocalNotifications:(NSDictionary *)userInfo) { for (UILocalNotification *notification in [UIApplication sharedApplication].scheduledLocalNotifications) { __block BOOL matchesAll = YES; - NSDictionary *notificationInfo = notification.userInfo; + NSDictionary *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; @@ -319,16 +330,26 @@ RCT_EXPORT_METHOD(getInitialNotification:(RCTPromiseResolveBlock)resolve reject:(__unused RCTPromiseRejectBlock)reject) { NSDictionary *initialNotification = - [self.bridge.launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] copy]; - resolve(RCTNullIfNil(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 *scheduledLocalNotifications = [UIApplication sharedApplication].scheduledLocalNotifications; - NSMutableArray *formattedScheduledLocalNotifications = [[NSMutableArray alloc] init]; + NSMutableArray *formattedScheduledLocalNotifications = [NSMutableArray new]; for (UILocalNotification *notification in scheduledLocalNotifications) { - [formattedScheduledLocalNotifications addObject:formatLocalNotification(notification)]; + [formattedScheduledLocalNotifications addObject:RCTFormatLocalNotification(notification)]; } callback(@[formattedScheduledLocalNotifications]); }