Permalink
Browse files

Remote notification completion handler

Summary:
**Motivation**
To allow handling of remote notifications using the preferred delegate [application:didReceiveRemoteNotification:fetchCompletionHandler](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/#//apple_ref/occ/intfm/UIApplicationDelegate/application:didReceiveRemoteNotification:fetchCompletionHandler:).

React native right now is setup to use [application:didReceiveRemoteNotification](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/#//apple_ref/occ/intfm/UIApplicationDelegate/application:didReceiveRemoteNotification:) which doesn't give the engineer the option to call back to the app via a completion handler and inform the app that it is done with handling the push notification.

The primary reason to use the `fetchCompletionHandler` would be to wake up the app to fetch updates so the next time the user opens the app it will be up to date.

A new method will be exposed: `PushNotificationIOS#
Closes #8040

Differential Revision: D4081766

Pulled By: bestander

fbshipit-source-id: 2819061bd3a926cb1c9c743af5aabab8c0faec3d
  • Loading branch information...
JAStanton authored and Facebook Github Bot committed Oct 27, 2016
1 parent a16d728 commit e000b71845a852a1ed763a6c22a5e3b8911c8c26
@@ -24,6 +24,12 @@ const NOTIF_REGISTER_EVENT = 'remoteNotificationsRegistered';
const NOTIF_REGISTRATION_ERROR_EVENT = 'remoteNotificationRegistrationError';
const DEVICE_LOCAL_NOTIF_EVENT = 'localNotificationReceived';
+export type FetchResult = {
+ NewData: string,
+ NoData: string,
+ ResultFailed: string,
+};
+
/**
* An event emitted by PushNotificationIOS.
*/
@@ -84,6 +90,17 @@ export type PushNotificationEventName = $Enum<{
* {
* [RCTPushNotificationManager didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
* }
+ * // Required for the notification event. You must call the completion handler after handling the remote notification.
+ * - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
+ * fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
+ * {
+ * [RCTPushNotificationManager didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
+ * }
+ * // Optionally implement this method over the previous to receive remote notifications. However
+ * // implement the application:didReceiveRemoteNotification:fetchCompletionHandler: method instead of this one whenever possible.
+ * // If your delegate implements both methods, the app object calls the `application:didReceiveRemoteNotification:fetchCompletionHandler:` method
+ * // Either this method or `application:didReceiveRemoteNotification:fetchCompletionHandler:` is required in order to receive remote notifications.
+ * //
* // Required for the registrationError event.
* - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
* {
@@ -106,6 +123,15 @@ class PushNotificationIOS {
_alert: string | Object;
_sound: string;
_badgeCount: number;
+ _notificationId: string;
+ _isRemote: boolean;
+ _remoteNotificationCompleteCalllbackCalled: boolean;
+
+ static FetchResult: FetchResult = {
+ NewData: 'UIBackgroundFetchResultNewData',
+ NoData: 'UIBackgroundFetchResultNoData',
+ ResultFailed: 'UIBackgroundFetchResultFailed',
+ };
/**
* Schedules the localNotification for immediate presentation.
@@ -341,6 +367,11 @@ class PushNotificationIOS {
*/
constructor(nativeNotif: Object) {
this._data = {};
+ this._remoteNotificationCompleteCalllbackCalled = false;
+ this._isRemote = nativeNotif.remote;
+ if (this._isRemote) {
+ this._notificationId = nativeNotif.notificationId;
+ }
if (nativeNotif.remote) {
// Extract data from Apple's `aps` dict as defined:
@@ -364,6 +395,28 @@ class PushNotificationIOS {
}
}
+ /**
+ * This method is available for remote notifications that have been received via:
+ * `application:didReceiveRemoteNotification:fetchCompletionHandler:`
+ * https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/#//apple_ref/occ/intfm/UIApplicationDelegate/application:didReceiveRemoteNotification:fetchCompletionHandler:
+ *
+ * Call this to execute when the remote notification handling is complete. When
+ * calling this block, pass in the fetch result value that best describes
+ * the results of your operation. You *must* call this handler and should do so
+ * as soon as possible. For a list of possible values, see `PushNotificationIOS.FetchResult`.
+ *
+ * If you do not call this method your background remote notifications could
+ * be throttled, to read more about it see the above documentation link.
+ */
+ finish(fetchResult: FetchResult) {
+ if (!this._isRemote || !this._notificationId || this._remoteNotificationCompleteCalllbackCalled) {
+ return;
+ }
+ this._remoteNotificationCompleteCalllbackCalled = true;
+
+ RCTPushNotificationManager.onFinishRemoteNotification(this._notificationId, fetchResult);
+ }
+
/**
* An alias for `getAlert` to get the notification's main message string
*/
@@ -11,10 +11,13 @@
@interface RCTPushNotificationManager : RCTEventEmitter
+typedef void (^RCTRemoteNotificationCallback)(UIBackgroundFetchResult result);
+
#if !TARGET_OS_TV
+ (void)didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings;
+ (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken;
+ (void)didReceiveRemoteNotification:(NSDictionary *)notification;
++ (void)didReceiveRemoteNotification:(NSDictionary *)notification fetchCompletionHandler:(RCTRemoteNotificationCallback)completionHandler;
+ (void)didReceiveLocalNotification:(UILocalNotification *)notification;
+ (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error;
#endif
@@ -39,6 +39,10 @@ @implementation RCTConvert (NSCalendarUnit)
@end
+@interface RCTPushNotificationManager ()
+@property (nonatomic, strong) NSMutableDictionary *remoteNotificationCallbacks;
+@end
+
@implementation RCTConvert (UILocalNotification)
+ (UILocalNotification *)UILocalNotification:(id)json
@@ -58,6 +62,12 @@ + (UILocalNotification *)UILocalNotification:(id)json
return notification;
}
+RCT_ENUM_CONVERTER(UIBackgroundFetchResult, (@{
+ @"UIBackgroundFetchResultNewData": @(UIBackgroundFetchResultNewData),
+ @"UIBackgroundFetchResultNoData": @(UIBackgroundFetchResultNoData),
+ @"UIBackgroundFetchResultFailed": @(UIBackgroundFetchResultFailed),
+}), UIBackgroundFetchResultNoData, integerValue)
+
@end
#endif //TARGET_OS_TV
@@ -167,9 +177,19 @@ + (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
+ (void)didReceiveRemoteNotification:(NSDictionary *)notification
{
+ NSDictionary *userInfo = @{@"notification": notification};
+ [[NSNotificationCenter defaultCenter] postNotificationName:RCTRemoteNotificationReceived
+ object:self
+ userInfo:userInfo];
+}
+
++ (void)didReceiveRemoteNotification:(NSDictionary *)notification
+ fetchCompletionHandler:(RCTRemoteNotificationCallback)completionHandler
+{
+ NSDictionary *userInfo = @{@"notification": notification, @"completionHandler": completionHandler};
[[NSNotificationCenter defaultCenter] postNotificationName:RCTRemoteNotificationReceived
object:self
- userInfo:notification];
+ userInfo:userInfo];
}
+ (void)didReceiveLocalNotification:(UILocalNotification *)notification
@@ -186,9 +206,20 @@ - (void)handleLocalNotificationReceived:(NSNotification *)notification
- (void)handleRemoteNotificationReceived:(NSNotification *)notification
{
- NSMutableDictionary *userInfo = [notification.userInfo mutableCopy];
- userInfo[@"remote"] = @YES;
- [self sendEventWithName:@"remoteNotificationReceived" body:userInfo];
+ NSMutableDictionary *remoteNotification = [NSMutableDictionary dictionaryWithDictionary:notification.userInfo[@"notification"]];
+ RCTRemoteNotificationCallback 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:notification.userInfo];
}
- (void)handleRemoteNotificationsRegistered:(NSNotification *)notification
@@ -224,6 +255,16 @@ - (void)handleRegisterUserNotificationSettings:(NSNotification *)notification
_requestPermissionsResolveBlock = nil;
}
+RCT_EXPORT_METHOD(onFinishRemoteNotification:(NSString*)notificationId fetchResult:(UIBackgroundFetchResult)result) {
+ RCTRemoteNotificationCallback 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
*/

0 comments on commit e000b71

Please sign in to comment.