I am facing a problem in iOS push notification in a Cordova hybrid app, need guidance/help in identifying whether I am doing it the right way?
Here is the flow ->
User recieved push notification while app is in background -> Tap on the notification from the notification center -> App is opened (invoked) -> Get the custom payload values -> perform a JS Callback using stringByEvaluatingJavaScriptFromString -> The JS function will do the necessary actions the app has to do.
Here is my code where I am doing it
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
[[Pushbots sharedInstance] receivedPush:userInfo]; //Using Pushbots notification platform
NSString* Value1 = [userInfo objectForKey:#"val1"];
NSString* Value2 = [userInfo objectForKey:#"val2"];
NSString * jsCallBack = [NSString stringWithFormat:#"testfromdelegate('%#','%#')", Value1, Value2];
NSLog(#"%#", jsCallBack);
[self.viewController.webView stringByEvaluatingJavaScriptFromString:jsCallBack];
}
Everything works till my NSLog(#"%#", jsCallBack);, and it fails at the jsCallBack. I understood that by the time my jsCallBack is being called, the webView is not yet ready, as the app is invoked from background.
What do I do to make this work? How to check whether my webView is ready, and then perform my jsCallBack?
How do I do this when the app is not running at all? For e.g., a user received a Whatsapp message and the app is not in background/foreground. But when the user taps on the message from the notification center, the whatsapp app opens with the user chat screen.
PS: All this code perfectly works when the app is in foreground. That means, the webView is already there and so jsCallBack has no problem.
If the app is in open or in background, then you can execute the javascript directly:
//This will be called if the app was open, in the foreground (active) or in the background and you reopen it from a push message
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
[[Pushbots sharedInstance] receivedPush:userInfo]; //Using Pushbots notification platform
NSString* Value1 = [userInfo objectForKey:#"val1"];
NSString* Value2 = [userInfo objectForKey:#"val2"];
NSString * jsCallBack = [NSString stringWithFormat:#"testfromdelegate('%#','%#')", Value1, Value2];
NSLog(#"%#", jsCallBack);
// If the app is in the foreground just execute the javascript
if ( application.applicationState == UIApplicationStateActive ) {
[self.viewController.webView stringByEvaluatingJavaScriptFromString: jsCallBack];
} else {
// If the app was in background, then force to execute the js code on the main thread
[self.viewController.webView performSelectorOnMainThread:#selector(stringByEvaluatingJavaScriptFromString:) withObject:jsCallBack waitUntilDone:NO]
}
}
If the app was completelly closed, then you can add an observer that will listen for the CDVPageDidLoadNotification (cordova finish loading), so you store the userInfo in a new variable myUserInfo and wait until the page is loaded to use it:
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
//Don't remove the existing code, just paste this before the return YES
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(cordovaDidLoad) name:CDVPageDidLoadNotification object:self.viewController.webView];
//This will be called if the app was closed completelly and you open it from a push
if (launchOptions) { //launchOptions is not nil
NSDictionary *userInfo = [launchOptions valueForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
self.myUserInfo = userInfo;
}
return YES;
}
Then, when cordova is loaded this method will be called:
- (void)cordovaDidLoad {
//Check that myUserInfo isn't null, that will mean that the apps was completelly closed and it was opened from the push notification
if (self.myUserInfo) {
NSString* Value1 = [self.myUserInfo objectForKey:#"val1"];
NSString* Value2 = [self.myUserInfo objectForKey:#"val2"];
NSString * jsCallBack = [NSString stringWithFormat:#"testfromdelegate('%#','%#')", Value1, Value2];
NSLog(#"%#", jsCallBack);
[self.viewController.webView stringByEvaluatingJavaScriptFromString: jsCallBack];
}
}
Related
I'm able to send push notifications to my IOS device. But when I click on that notification it just opens the app.when my app is in background or in foreground it works perfectly, I want the app to open and navigate to a specific view controller depending on the push notification received.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
// Print message ID.
NSLog(#"Message ID: %#", userInfo[#"gcm.message_id"]);
[[FIRMessaging messaging] appDidReceiveMessage:userInfo];
NSDictionary *apsDict = [userInfo objectForKey:#"aps"];
NSString *alertForegroundMessage = [NSString stringWithFormat:#"%#", [apsDict objectForKey:#"alert"]];
// Pring full message.
NSLog(#"%#", userInfo);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 20 * NSEC_PER_SEC),
dispatch_get_main_queue(), ^{
// Check result of your operation and call completion block with the result
completionHandler(UIBackgroundFetchResultNewData);
NSMutableArray *arrnotification;
arrnotification = [userInfo valueForKey:#"aps"];
});
if (application.applicationState == UIApplicationStateActive)
{
NSDictionary *userInfo = [[NSDictionary alloc] init];
[[NSNotificationCenter defaultCenter] postNotificationName:#"downloadDataFromServer" object:self userInfo:userInfo];
NSLog(#"User Info : %#",userInfo);
completionHandler(UIBackgroundFetchResultNewData);
}
else
{
NSLog(#"userInfo->%#", [userInfo objectForKey:#"aps"]);
NSDictionary *sentObject = [NSDictionary dictionaryWithObjectsAndKeys:noteDict,#"data", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"downloadDataFromServer" object:sentObject];
completionHandler(UIBackgroundFetchResultNewData);
}
completionHandler(UIBackgroundFetchResultNewData);
}
When the app gets launched after the user opens the app from the notification. The best way to handle navigation based on the notification is to check the launchOptions dictionary in the application(_:didFinishLaunchingWithOptions:) method. This should contain the remoteNotification- key, which holds the push notification data.
As mentioned in the discussion section of application(_:didReceiveRemoteNotification:fetchCompletionHandler:) -method, documentation, the system does not automatically launch your app.
application(_:didFinishLaunchingWithOptions:)
in my iOS App i have an UIWebView so how i can open a specified URL in the UIWebView, with a push notification?
If somebody open the App with the notification, i want to show a specified Website in
the UIWebView.
Can I bind the URL (in background) with the push notification?
Thank you.
According to Apple...
If the app is running and receives a remote notification, the app
calls this method to process the notification. Your implementation of
this method should use the notification to take an appropriate course
of action. ... If the app is not running when a push notification
arrives, the method launches the app and provides the appropriate
information in the launch options dictionary. The app does not call
this method to handle that push notification. Instead, your
implementation of the application:willFinishLaunchingWithOptions: or
application:didFinishLaunchingWithOptions: method needs to get the
push notification payload data and respond appropriately.
So, there are three possible scenarios:
1) App is in foreground: you will have full control, just implement didReceiveNotification and do whatever you want.
2) App is running, but in background: the action won't be triggered until the user actually open your app using the received notification.
3) The app is not running: in this case you should implement didFinishLaunchingWithOptions in order to get the additional info and perform the task.
So the code should look like this for didFinishLaunchingWithOptions
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSDictionary *userInfo = [launchOptions valueForKey:#"UIApplicationLaunchOptionsRemoteNotificationKey"];
NSDictionary *apsInfo = [userInfo objectForKey:#"aps"];
if(apsInfo) {
// Get the URL or any other data
}
}
And this is an approximation for didReceiveNotification
/**
* Remote Notification Received while application was open.
*/
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
#if !TARGET_IPHONE_SIMULATOR
UIApplicationState state = [application applicationState];
if (state == UIApplicationStateActive)
{
NSString *message = nil;
id aps = [userInfo objectForKey:#"aps"];
if ([aps isKindOfClass:[NSDictionary class]]) {
message = [aps objectForKey:#"alert"];
}
if (message) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Notificación"
message:message
delegate:self
cancelButtonTitle:#"Aceptar"
otherButtonTitles:nil, nil];
[alertView show];
}
}
// Aditional data
NSString *url = [userInfo objectForKey:#"url"];
NSLog(#"Received Push URL: %#", url);
if(url!=nil)
{
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]];
}
NSLog(#"remote notification: %#",[userInfo description]);
NSDictionary *apsInfo = [userInfo objectForKey:#"aps"];
NSString *alert = [apsInfo objectForKey:#"alert"];
NSLog(#"Received Push Alert: %#", alert);
NSString *sound = [apsInfo objectForKey:#"sound"];
NSLog(#"Received Push Sound: %#", sound);
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
NSString *badge = [apsInfo objectForKey:#"badge"];
NSLog(#"Received Push Badge: %#", badge);
application.applicationIconBadgeNumber = [[apsInfo objectForKey:#"badge"] integerValue];
#endif
}
I have an app where I have push notification.
What I have is I go to respective category when push is clicked (from the push notification list).
All is working perfectly, except when the app is killed.
If the app is minimized and push comes, if I click on push, it goes to respective category.
However if I forcefully kill the app and push comes and click on the push, it just open the app and no transition occurs.
Is this natural behavior in iPhone or I am doing something wrong?
In didReceiveRemoteNotification I go to specific category based on the data I received.
didReceiveRemoteNotification not call app will not run, that time push notification open app newly in device, you manually check if any notifications are bending in application:didFinishLaunchingWithOptions: try this for check bending notifications in app.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...............
...............
UILocalNotification *localNotif =[launchOptions objectForKey: UIApplicationLaunchOptionsRemoteNotificationKey];
if (localNotif) {
NSLog(#"load notifiation *******");
isLoadNotification=YES;
}
return YES;
}
Below is what I used.
-(BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
if (launchOptions != nil) {
NSMutableDictionary *dic = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey"];
NSMutableDictionary *dicItem = [dic objectForKey:#"aps"];
NSString *itemNotification = [dicItem objectForKey:#"alert"];
// do all transition here....
}else if (launchOptions == nil){
NSLog(#"launch ,,, nil");
}
...//code something
}
i'm not receiving newsstand notifications when the app is not running, here is what I have done.
The app has the proper plist keys 'UINewsstandApp = YES' and 'UIBackgroundModes = newsstand-content'.
In the app delegate I register for all notification types, and i receive my token from APNS, from the server side ( i am using a gem called Grocer ) i set up the dev certificate and send a regular push and it works.
if i send a newsstand push I receive it if the app is running on 'didReceiveRemoteNotification', but when the app is not running i get nothing in the notification center, which is mainly because 'Grocer' has the following payload
{"aps": {"content-available":1}} and can't add any other keys ( alert, badge, etc )
so I thought that I should not get anything in the notification center, I look for the 'UIApplicationLaunchOptionsRemoteNotificationKey' in the launch options, and then write a file to make sure that the app ran in the background, the file is never written as such
NSDictionary *remoteNotif = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if(remoteNotif)
{
NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [cachePath stringByAppendingPathExtension:#"pushreceived.txt"];
[#"testing" writeToFile:filePath atomically:YES encoding:NSASCIIStringEncoding error:nil];
}
I have NKDontThrottleNewsstandContentNotifications set to true in my user defaults, and then I synchronize to make sure.
when the app is running, no matter how many times i send the push, I always get a callback on " didReceiveRemoteNotification ", with the proper "content-available"
if the app is closed or in the background, nothing happens.
Update:
I managed to change the gem that sends the notification payload, here is the dictionary it sends
{"aps"=>
{
"alert"=>"Hello iPhone!!",
"content-available"=>1,
"badge"=>1,
"sound"=>"default"
}
}
and here the userinfo dictionary I receive on my app ( while running )
{
aps = {
alert = "Hello iPhone!!";
badge = 1;
"content-available" = 1;
sound = default;
};
}
please note the quotation marks around content-available, does this mean that APNS parsed it as a custom key ?
To receive notifications when your application is running in the background, you need to add a applicationDidEnterBackgroud method to your main class. From the Apple documentation:
Applications might also find local notifications useful when they run in the background and some message, data, or other item arrives that might be of interest to the user. In this case, they should present the notification immediately using the UIApplication method presentLocalNotificationNow: (iOS gives an application a limited time to run in the background). Listing 2-2 illustrates how you might do this.
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(#"Application entered background state.");
// bgTask is instance variable
NSAssert(self->bgTask == UIInvalidBackgroundTask, nil);
bgTask = [application beginBackgroundTaskWithExpirationHandler: ^{
dispatch_async(dispatch_get_main_queue(), ^{
[application endBackgroundTask:self->bgTask];
self->bgTask = UIInvalidBackgroundTask;
});
}];
dispatch_async(dispatch_get_main_queue(), ^{
while ([application backgroundTimeRemaining] > 1.0) {
// Replace this code with your notification handling process
NSString *friend = [self checkForIncomingChat];
if (friend) {
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
if (localNotif) {
localNotif.alertBody = [NSString stringWithFormat:
NSLocalizedString(#"%# has a message for you.", nil), friend];
localNotif.alertAction = NSLocalizedString(#"Read Message", nil);
localNotif.soundName = #"alarmsound.caf";
localNotif.applicationIconBadgeNumber = 1;
[application presentLocalNotificationNow:localNotif];
[localNotif release];
friend = nil;
break;
}
}
}
[application endBackgroundTask:self->bgTask];
self->bgTask = UIInvalidBackgroundTask;
});
}
Also note:
Because the only notification type supported for non-running applications is icon-badging, simply pass NSRemoteNotificationTypeBadge as the parameter of registerForRemoteNotificationTypes:.
Try these as a sanity check:
In Settings -> Newsstand, you have automatic content download as on for your app.
Make sure that you used [[UIApplication sharedApplication] registerForRemoteNotificationTypes:UIRemoteNotificationTypeNewsstandContentAvailability] somehwere in your app.
Make sure that you have required background mode as 'newsstand-content' in your plist.
Run the app in the device, not in the simulator.
If you have done those correctly you should get a notification in didReceiveRemoteNotification even when your app is not running.
I've created an iphone app with push notification feature. The server works well and I could receive the notification when app is running in foreground. The function
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
is called then notification arrives.
However, when I press the home key, brought the app into background or kill it in the task bar. I cannot receive any notification, neither in the notification area or any popup.
Anyone knows the reason? Thanks
You can use...
-(BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
if (launchOptions != nil) {
NSMutableDictionary *dic = [launchOptions objectForKey:#"UIApplicationLaunchOptionsRemoteNotificationKey"];
NSMutableDictionary *dicItem = [dic objectForKey:#"aps"];
NSString *itemNotification = [dicItem objectForKey:#"alert"];
}else if (launchOptions == nil){
NSLog(#"launch ,,, nil");
}
...//code something
}
itemNotification is an item in Notification barge. Have fun!!