ibm-bluemix-mobile-services/bms-clientsdk-cordova-plugin-push

Code examples in readme are not clear

Opened this issue · 23 comments

In the Objective-C example the following function contains extraneous lines:

// Handle receiving a remote notification on launch
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {

  -----------
    if (!launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) {
        [[CDVBMSPush sharedInstance] didReceiveRemoteNotificationOnLaunchWithLaunchOptions:launchOptions];
    }
  -----------
}

Removing the lines -----------
will result in a compiler warning: Control reaches end of non-void function.

The instructions say "Add the code below to your application delegate:"

The existing application delegate contains the following definition:

- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
    self.viewController = [[MainViewController alloc] init];
    return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

It is not clear whether the function "didFinishLaunchingWithOptions" is meant to replace the existing code in the delegate or replace it.

In our tests, using the code from the example compiles but fails at runtime, whilst the original code executes but clearly does not contain the same logic.

@GlenMasters That code is for identifying when app opened through clicking a notification.

  1. you can add that code inside the existing didFinishLaunchingWithOptions method. Dont replace it

Eg;

- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{

    if (!launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) {
           [[CDVBMSPush sharedInstance] didReceiveRemoteNotificationOnLaunchWithLaunchOptions:launchOptions];
     }
    self.viewController = [[MainViewController alloc] init];
    return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

@AnanthaKrish Thanks for your response.

We have added the code as you have specified, however we now get a fatal error at runtime.

This is the full content of our AppDelegate.m


/
//  Created by ___FULLUSERNAME___ on ___DATE___.
//  Copyright ___ORGANIZATIONNAME___ ___YEAR___. All rights reserved.
//
#import "AppDelegate.h"
#import "MainViewController.h"
#import "IBM-Swift.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
   if (!launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) {
       [[CDVBMSPush sharedInstance] didReceiveRemoteNotificationOnLaunchWithLaunchOptions:launchOptions];
   }
   self.viewController = [[MainViewController alloc] init];
   return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

// Register device token with Bluemix Push Notification Service
- (void)application:(UIApplication *)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
   
   [[CDVBMSPush sharedInstance] didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}

// Handle error when failed to register device token with APNs
- (void)application:(UIApplication*)application
didFailToRegisterForRemoteNotificationsWithError:(NSError*)error {
   
   [[CDVBMSPush sharedInstance] didFailToRegisterForRemoteNotificationsWithError:error];
}

// Handle receiving a remote notification
-(void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
   
   [[CDVBMSPush sharedInstance] didReceiveRemoteNotificationWithNotification:userInfo];
}

//Handle Notification Click

- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void (^)())completionHandler{
   
   NSMutableDictionary *userInf = [[NSMutableDictionary alloc] init];
   [userInf addEntriesFromDictionary:userInfo];
   [userInf setValue:identifier forKey:@"identifierName"];
   [[CDVBMSPush sharedInstance] didReceiveRemoteNotificationWithNotification:userInf];
   completionHandler();
   
}

@end

The error we are receiving is in attached screen shot:

screen shot 2018-01-05 at 10 46 07

@GlenMasters Try replacing the code like this,

- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
  if (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) {
       [[CDVBMSPush sharedInstance] didReceiveRemoteNotificationOnLaunchWithLaunchOptions:launchOptions];
   }
   self.viewController = [[MainViewController alloc] init];
   return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

@AnanthaKrish

Your suggested code snippet compiles and runs without the fatal error. However it does not expose the identifierName field that we were expecting.
(The same plugin running on android provides us with the identifierName field when the user touches the notification.)

@GlenMasters If you want actionable notifications identifier , please remove the code from didFinishLaunchingWithOptions ,

Remove this;

 if (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) {
       [[CDVBMSPush sharedInstance] didReceiveRemoteNotificationOnLaunchWithLaunchOptions:launchOptions];
   }

and implement this

//HNADLE THE NOTIFICTAION CLICK
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void (^)())completionHandler{
    
    NSMutableDictionary *userInf = [[NSMutableDictionary alloc] init];
    [userInf addEntriesFromDictionary:userInfo];
    [userInf setValue:identifier forKey:@"identifierName"];
    [[CDVBMSPush sharedInstance] didReceiveRemoteNotificationWithNotification:userInf];
    completionHandler();
    
}

@AnanthaKrish
This does not appear to expose the identifier.
The notification is received but it does not contain the identifier that indicates the user clicked on the notification.
The same application works in Android and correctly exposes the identifier as expected.

@GlenMasters Are you clicking on the action button to open the app ? Or just taping on the notification ?

@AnanthaKrish
I am tapping on the notification.

@GlenMasters in iOS if you just tap there is no identifierName will come. If you tap on the action button then teh identifier name will come ...

@AnanthaKrish

What is the action button? I do not follow.

@AnanthaKrish

Ok tried that - does not make any difference. I still just get a standard notification - and no identifier when I click on it.

Here is my code for initialisation - copied from the documentation:


            var options ={"categories":{
                "Category_Name1":[
                  {
                    "IdentifierName":"IdentifierName_1",
                    "actionName":"actionName_1",
                    "IconName":"IconName_1"
                  },
                  {
                    "IdentifierName":"IdentifierName_2",
                    "actionName":"actionName_2",
                    "IconName":"IconName_2"
                  }
                ]},
            };

            BMSPush.initialize(this._appConfigSvc.notificationAppGuid, this._appConfigSvc.notificationClientSecret, options);

and for handling the notifications:

            BMSPush.registerNotificationsCallback(this.onNotificationReceived);

    private onNotificationReceived = (notif) => {

        alert(JSON.stringify(notif));
        console.log(notif);
}

@GlenMasters You have to send the identifier name in the push notification. In Dashboard check the ios section , you'll see Interactive Category:. Add the Category_Name1 there.. Send push notification.
When notification arrives on the device, drag the notifications to see the action names (your case - actionName_1 & actionName_1).
When you click on any of the action button , the respective identifier (IdentifierName_1 or IdentifierName_2) will show in the app with the payload.

@AnanthaKrish

I am not sure that this is going to get me what I want - which is to be able to determine whether my user has received and is responding to the notification.

The notification callback handler is invoked both, when the app is in the background and the user taps on the notification, and when the app is in the foreground (and therefore no notification was received).

I need to be able to determine which scenario has occurred as the behaviour of the app is different in each case. (We navigate to relevant content in response to the notification tap, but alert the user to the notification when the app is in the foreground).

This behaviour seems to work for BMS in Android but not in IOS.

@GlenMasters to identify the notification click (Not through the notification action click ), add the below code in didFinishLaunchingWithOptions,

if (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) {
        NSMutableDictionary *userInfo = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
        [userInfo setObject:@"NotifClick" forKey:@"identifierName"];
        [[CDVBMSPush sharedInstance] didReceiveRemoteNotificationWithNotification:userInfo];

  }

@AnanthaKrish Thanks for that. I am looking into supporting interactive (actionable?) notifications, in addition to regular ones. Below is the IOS-code, from the sample, I am using now. How would a full sample for supporting interactive notifications as well look?!

Cheers
-jo


// Register device token with Bluemix Push Notification Service

  • (void)application:(UIApplication *)application
    didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{

     [[CDVBMSPush sharedInstance] didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
    

}

// Handle error when failed to register device token with APNs

  • (void)application:(UIApplication*)application
    didFailToRegisterForRemoteNotificationsWithError:(NSError*)error {

    [[CDVBMSPush sharedInstance] didFailToRegisterForRemoteNotificationsWithError:error];
    

}

// Handle receiving a remote notification

  • (void)application:(UIApplication *)application
    didReceiveRemoteNotification:(NSDictionary *)userInfo
    fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {

    [[CDVBMSPush sharedInstance] didReceiveRemoteNotificationWithNotification:userInfo];
    }

  • (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
    {

    self.viewController = [[MainViewController alloc] init];

    if (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) {
    [[CDVBMSPush sharedInstance] didReceiveRemoteNotificationOnLaunchWithLaunchOptions:launchOptions];
    }

    return [super application:application didFinishLaunchingWithOptions:launchOptions];
    }

On Android it seems quite easy, just add the options like @GlenMasters did above, and the buttons shows up in the notification. Hitting a button then opens the app, with info om which IdentifierName was selected. I assume not possible to not bring the application to front?

@tverilytt You have to pass the categoryName in iOS field also from Push dashboard (Or REST API).
Am not clear about the question you asked. Clicking on a notification/button will bring the app to foreground.

@AnanthaKrish Thanks. I was thinking if it was possible to take some direct action, when clicking a notification buttion, without having to open the application itself.

I am a bit confused on your code change proposals, so would be great to see a full code sample for handling actionable push notifications for IOS :-)

Cheers
-jo

@tverilytt If you are asking about adding a text field in push notifications, its not possible with the current implementation.

Please send a silent push notification from push service and create a local notification from the device , which have custom Handling options.

@AnanthaKrish Ok, thanks, good suggestion :-)

So no need to add anything particular to IOS code for handling actionable push notifications?
Or add this from the main page:

/HNADLE THE NOTIFICTAION CLICK

  • (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void (^)())completionHandler{

    NSMutableDictionary *userInf = [[NSMutableDictionary alloc] init];
    [userInf addEntriesFromDictionary:userInfo];
    [userInf setValue:identifier forKey:@"identifierName"];
    [[CDVBMSPush sharedInstance] didReceiveRemoteNotificationWithNotification:userInf];
    completionHandler();

}

@tverilytt Yes you have to . Make the changes in the in didReceiveRemoteNotification: fetchCompletionHandler function of iOS. Handle the silent push there instead of sending it to the CDVBMSPush.

Create a local notification there and add the actions you need.

I am struggling to get interactive notifications to work, below is my current AppDelegate, am I missing something?!

EDIT: When app is running in background, it seems to have to be brought to foreground for the notification action to run (the app is not brought to foreground when (licking a notification buttion...).
If the app is not running at all, the notification is not received, when pushing a notification interactive button...

// Register device token with Bluemix Push Notification Service

  • (void)application:(UIApplication *)application
    didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{

     [[CDVBMSPush sharedInstance] didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
    

}

// Handle error when failed to register device token with APNs

  • (void)application:(UIApplication*)application
    didFailToRegisterForRemoteNotificationsWithError:(NSError*)error {

    [[CDVBMSPush sharedInstance] didFailToRegisterForRemoteNotificationsWithError:error];
    

}

// Handle receiving a remote notification

  • (void)application:(UIApplication *)application
    didReceiveRemoteNotification:(NSDictionary *)userInfo
    fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {

    [[CDVBMSPush sharedInstance] didReceiveRemoteNotificationWithNotification:userInfo];
    }

// Handle receiving a remote notification on launch

  • (BOOL)application:(UIApplication*)application
    didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
    {

// jo2mod
// #58
self.viewController = [[MainViewController alloc] init];

// if (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) {
// [[CDVBMSPush sharedInstance] didReceiveRemoteNotificationOnLaunchWithLaunchOptions:launchOptions];
// }
if (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) {
NSMutableDictionary *userInfo = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
[userInfo setObject:@"NotifClick" forKey:@"identifierName"];
[[CDVBMSPush sharedInstance] didReceiveRemoteNotificationWithNotification:userInfo];

}
// jo2mod
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

//HNADLE THE NOTIFICTAION CLICK

  • (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void (^)())completionHandler{

    NSMutableDictionary *userInf = [[NSMutableDictionary alloc] init];
    [userInf addEntriesFromDictionary:userInfo];
    [userInf setValue:identifier forKey:@"identifierName"];
    [[CDVBMSPush sharedInstance] didReceiveRemoteNotificationWithNotification:userInf];
    completionHandler();

}