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 https://github.com/facebook/react-native/pull/8040

Differential Revision: D4081766

Pulled By: bestander

fbshipit-source-id: 2819061bd3a926cb1c9c743af5aabab8c0faec3d
This commit is contained in:
Jonathan Stanton 2016-10-26 21:35:33 -07:00 committed by Facebook Github Bot
parent a16d72842d
commit e000b71845
3 changed files with 101 additions and 4 deletions

View File

@ -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
*/

View File

@ -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

View File

@ -39,6 +39,10 @@ RCT_ENUM_CONVERTER(NSCalendarUnit,
@end
@interface RCTPushNotificationManager ()
@property (nonatomic, strong) NSMutableDictionary *remoteNotificationCallbacks;
@end
@implementation RCTConvert (UILocalNotification)
+ (UILocalNotification *)UILocalNotification:(id)json
@ -58,6 +62,12 @@ RCT_ENUM_CONVERTER(NSCalendarUnit,
return notification;
}
RCT_ENUM_CONVERTER(UIBackgroundFetchResult, (@{
@"UIBackgroundFetchResultNewData": @(UIBackgroundFetchResultNewData),
@"UIBackgroundFetchResultNoData": @(UIBackgroundFetchResultNoData),
@"UIBackgroundFetchResultFailed": @(UIBackgroundFetchResultFailed),
}), UIBackgroundFetchResultNoData, integerValue)
@end
#endif //TARGET_OS_TV
@ -167,9 +177,19 @@ RCT_EXPORT_MODULE()
+ (void)didReceiveRemoteNotification:(NSDictionary *)notification
{
NSDictionary *userInfo = @{@"notification": notification};
[[NSNotificationCenter defaultCenter] postNotificationName:RCTRemoteNotificationReceived
object:self
userInfo:notification];
userInfo:userInfo];
}
+ (void)didReceiveRemoteNotification:(NSDictionary *)notification
fetchCompletionHandler:(RCTRemoteNotificationCallback)completionHandler
{
NSDictionary *userInfo = @{@"notification": notification, @"completionHandler": completionHandler};
[[NSNotificationCenter defaultCenter] postNotificationName:RCTRemoteNotificationReceived
object:self
userInfo:userInfo];
}
+ (void)didReceiveLocalNotification:(UILocalNotification *)notification
@ -186,9 +206,20 @@ RCT_EXPORT_MODULE()
- (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 @@ RCT_EXPORT_MODULE()
_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
*/