Receiving Newsstand Notifications Only When App Is Running - ios

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.

Related

I want to do a badge increment on the main app icon while receiving a notification in inactive mode of the app

I am working on a chat app in react-native iOS. I want to do a badge increment on the main app icon when a notification is received, when the app is killed or force quit by the user. It works well when the app is in the background mode. But the didReceiveRemoteNotification method is not called when the app is inactive. Any idea on this? I added code inside the didReceiveRemoteNotification method of AppDelegate.
Added the following code set in AppDelegate file
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
[RNNotifications didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
[RNNotifications didFailToRegisterForRemoteNotificationsWithError:error];
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
NSLog(#"APPDELEGATE: didReceiveRemoteNotification:fetchCompletionHandler %#", userInfo);
int badge = (int) application.applicationIconBadgeNumber;
if ( application.applicationState == UIApplicationStateInactive ) {
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:badge+1];
}
else if(application.applicationState == UIApplicationStateBackground) {
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:badge+1];
}
else if(application.applicationState == UIApplicationStateActive) {
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:badge+1];
}
completionHandler(UIBackgroundFetchResultNewData);
}
You need to create a UNNotificationServiceExtension to handle notifications when app is in background/killed.
It's a small application that will be executed on notification reception and has limited time to edit its content before presenting it to the user.
When you receive a notification in it's didReceive(_:withContentHandler:) delegate callback you can modify badge value of UNNotificationContent
In order to catch the incoming notification when the app is in the background or killed, use a UNNotificationServiceExtension in your project. The Info.plist for the UNNotificationServiceExtension (not the normal Info.plist for the main app; that one has normal things for the main app) might look something like
In the - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler of the UNNotificationServiceExtension, you can update the badge the following way:
self.bestAttemptContent = [request.content mutableCopy]; to get the request's content
self.bestAttemptContent.badge = <desired_integer_value> , where <desired_integer_value> would be the integer that you wish to put for the badge count.
self.contentHandler(self.bestAttemptContent); to complete the update of the content.
In many cases, the badge count may need to reflect a value (like number of unread chat messages) for a particular user. For that, you can use a shared user defaults. In fact NSUserDefaults supports the concept of app suite to allow such sharing. See Apple documentation for more details. In particular,
You can use this method when developing an app suite, to share preferences or other data among the apps, or when developing an app extension, to share preferences or other data between the extension and its containing app.
The argument and registration domains are shared between all instances of NSUserDefaults.
In a Constants.h file, have something to track individual counts for each user like
#define NOTIFICATIONS_UNREAD_SHARED [NSString stringWithFormat:#"notificationsUnread-%#",[mySharedDefaults objectForKey:USERNAME]]
and in your app, you would save the individual counts for each user, to the app suite's shared user defaults, with something like
NSUserDefaults *mySharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:#"group.com.yourCompany.yourAppname"];
if ([[NSUserDefaults standardUserDefaults] objectForKey:USERNAME]) {
mySharedDefaults setObject:[[NSUserDefaults standardUserDefaults] objectForKey:USERNAME] forKey:USERNAME];
[mySharedDefaults setObject:[NSNumber numberWithInteger:arrExistingRead.count] forKey:NOTIFICATIONS_READ_SHARED];
[mySharedDefaults setObject:[NSNumber numberWithInteger:([AppConstant sharedconstant].countObj.arrAllMessages.count - arrExistingRead.count)] forKey:NOTIFICATIONS_UNREAD_SHARED];
[mySharedDefaults synchronize];
}
Then in your UNNotificationServiceExtension, you would do something like
NSUserDefaults *mySharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:#"group.com.yourCompany.yourAppname"];
if ([mySharedDefaults objectForKey:NOTIFICATIONS_UNREAD_SHARED]) {
if ([mySharedDefaults objectForKey:USERNAME]) {
self.bestAttemptContent.badge = [NSNumber numberWithInt:[[mySharedDefaults objectForKey:NOTIFICATIONS_UNREAD_SHARED] intValue]+1];
} else { // username somehow not set; reset badge to 0
self.bestAttemptContent.badge = #0;
}
} else { // notifications unread count not found for this user; reset badge to 0
self.bestAttemptContent.badge = #0;
}
Troubleshooting
In case the extension doesn't appear to be receiving the push notifications, some things to verify:
Look at the build targets. Besides the main app, there should be one for the extension too.
In the settings for the main app, it should associate with the UNNotificationServiceExtension :
You need to set the badge field in the payload in the push notification.
https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/generating_a_remote_notification
You will have to do the calculation server side.

stringByEvaluatingJavaScriptFromString in didReceiveRemoteNotification but after webView is loaded

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];
}
}

Cancel Banner Notification in iOS8 Not Working

My title might not accurately depict my question, so I apologize. I have looked for a solution to creating this level of functionality but I am unable to find it.
I am creating a VoIP application for iOS 8. When a user receives a call I am displaying a notification with a 12 second ringtone. While this notification is in progress if the call disconnects I want the Incoming Call notification to disappear and display a Missed Call notification immediately. This level of functionality is possible because Viber does it.
Currently, I am sending a silent push notification when a Incoming Call is available. This is my payload...
aps = {
"content-available" = 1;
};
category = INCOMING;
from = "+15555554220";
Upon receiving the silent push I am creating a Local Notification like this ...
if ([userInfo[#"category"] isEqualToString:#"INCOMING"]) {
NSLog(#"application: didReceiveRemoteNotification: fetchCompletionHandler: Incoming Call Notification Received");
NSLog(#"application: didReceiveRemoteNotification: fetchCompletionHandler: Sending Local Notification For Incoming Call");
// Get Caller Contact Info
NSDictionary *contact = [self findContactInfoForNumber:userInfo[#"from"]];
NSString *message = [NSString stringWithFormat:#"Incoming Call: %#",userInfo[#"from"]];
if (contact != nil) {
message = [NSString stringWithFormat:#"Incoming Call: %# %#",contact[#"firstName"],contact[#"lastName"]];
}
UILocalNotification *notification = [[UILocalNotification alloc] init];
NSMutableDictionary *infoDict = [NSMutableDictionary dictionaryWithObject:#"Incoming Call" forKey:#"type"];
notification.userInfo = infoDict;
notification.category = #"INCOMING_CALL_CATEGORY";
notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:1];
notification.alertBody = message;
notification.timeZone = [NSTimeZone defaultTimeZone];
notification.soundName = #"ring.m4a";
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
Then, once the call is disconnected I am sending another silent push notification for a missed call...
aps = {
"content-available" = 1;
};
category = MISSED;
Once received I am canceling all local notifications like this...
if ([userInfo[#"category"] isEqualToString:#"MISSED"]) {
NSLog(#"application: didReceiveRemoteNotification: fetchCompletionHandler: Missed Call Notification Received");
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
[[UIApplication sharedApplication] cancelAllLocalNotifications];
}
The issue I am encountering is that on the lock screen it behaves exactly the way I want it to. I receive the incoming call notification and when the caller hangs up that notification immediately disappears from notification center and a missed call notification is there in the place of it. However when the phone is on the home screen. A banner is shown and then it plays the whole ringtone then displays the missed call. Does anyone know the reason why this is happening? Does anyone have any solutions to achieve this level of functionality? Like I said before the app Vider is a prime example of what I want my app to do.
Thanks you in advance.

iOS Intercept push notification

I'm making an applikation which is using push notifications, I am currently using pushbots for the push notifications. I was wondering if there is any way to intercept the notifications that is received by the application and check the notification before the notification is shown on the device. And if the data in the notification is not correct, dont show a notification at all? Is this possible with pushbot or do I need to do it all by my self?
Yes, you can achieve this behaviour playing around with Local Notifications.
You can configure your payload without an alert and "content-available": "1" so your application can receive notifications without showing them to the user.
// Payload
{
aps: {
"content-available": 1
},
text: 'my alert message' // your custom info
}
And in your app code, register notifications as
// Register notifications
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
UIRemoteNotificationTypeNewsstandContentAvailability];
Then, the key is to trigger Local Notifications in the application:didReceiveRemoteNotification:fetchCompletionHandler: method based on some condition
- (void)application:(UIApplication *)application didReceiveRemoteNotification: (NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
NSLog(#"push data package: %#", userInfo);
// Retrieve your data
NSString *text = [userInfo objectForKey:#"text"];
BOOL mustShow = YES;
// Only show notification if app is background and your custom condition
if ((state == UIApplicationStateInactive || state == UIApplicationStateBackground)
&& mustShow) {
// Raise the local notification a second after received
UILocalNotification* localNotification = [[UILocalNotification alloc] init];
localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:1];
localNotification.alertBody = text;
localNotification.timeZone = [NSTimeZone defaultTimeZone];
localNotification.applicationIconBadgeNumber = [[UIApplication sharedApplication] applicationIconBadgeNumber] + 1;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
}
This way you can "intercept" the notifications before "displaying" them to the user.
You can't prevent the notification from being shown once it reaches the device (assuming it contains an alert field in the aps dictionary - if it doesn't, no notification will be shown anyway).
You should determine in your server which notifications should be sent to which device tokens. You can associate device tokens with users in your DB, if your functionality requires it.
Even if what you request was possible, it would be very inefficient to send notifications to all the devices that installed your app, and then only display the notification in a small subset of them.

Add my Push notification in Notification area

I got my push notification successfully in both background and foreground but my problem is when my application is in active state/foreground and push notification is arrived then i show this pushnotification message on alertview using didReceiveRemoteNotification method,
My alert view have two buttons
1) later
2) Ok.
If I press 1) "later" button then I want add this pushnotification message in notification area so after some time user can see and tap on that particular push notification and go with that and that record of push notification will remove from notification area.
This is not possible. There is no API to access notification are of iOS.
Alternative
What near alternative you can try is Local Notification. When user select later set Local Notification for that thing. You can add this Local Notification when user leave your app so that you don't get notification while user is continue with your application.
Better Approach
The most general approach for this problem is Notification screen in app. Your application has one screen which has list of received notification so that user can check that in you app. I suggest you to go with this. Because this is most common and clear idea.
You need to implement core data for Notifications but it can only happen when your Application is is Active state.
1-create a new identity everytime a new notification arrive.
2-save it.
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
NSDictionary *Notification = userInfo;
NSString *title = [(NSDictionary*)[(NSDictionary*)[Notification valueForKey:#"aps"] valueForKey:#"alert"] valueForKey:#"title"];
NSString *body = [(NSDictionary*)[(NSDictionary*)[Notification valueForKey:#"aps"] valueForKey:#"alert"] valueForKey:#"body"];
XXNotification *objNotification = [XXNotification create];
objNotification.title = title;
objNotification.detail = body;
[XXNotification save:nil];
NSArray *arrNotification =[XXNotification allUnRead:nil];
[UtilityFunctions setApplicationBadgeNumber:[arrNotification count]];//Utility functions is my class for common functions.
if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateActive)
{
[UtilityFunctions showAlertView:title message:body delegate:self cancelButtonTitle:#"Ok" otherButtonTitle:#"Cancel" withTag:99 withAccessibilityHint:[NSString stringWithFormat:#"%#:|:%#", title,body]];
}
else if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateInactive || [[UIApplication sharedApplication] applicationState] == UIApplicationStateBackground)
{
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
localNotification.userInfo = userInfo;
localNotification.soundName = UILocalNotificationDefaultSoundName;
localNotification.alertBody = body;
localNotification.fireDate = [NSDate date];
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
if (![IsLocationSaved isEqualToString:#"NO"])
{
[[NSNotificationCenter defaultCenter]postNotificationName:kNotificationForShowingNotification object: nil userInfo:nil];
}
}
}
On showing the UIAlertView, on its click event either delete that notification in DB or make a bool in it as isRead and make it YES. then save it,
On Notifications list Query the notification from DB or only those whose isRead = NO.
That is the way I did it in My Application.

Resources