For system upper than iOS 10 version, within UNUserNotificationCenterDelegate, there are two methods to clarify whether the APNS message method was called from user clicked or system received the message:
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler
While for system under iOS 10 version, there was an issue for the application delegate method - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo, there are 3 ways to call this method:
1:Click a notification message when App was running in background, will call this method.
2:Click App icon wether App was running in background or was killed, before App really launched(about 1~3 seconds), during these seconds if received an APNS message, this method will be called too.
3: App was running, pull down the notification bar to make application become inactive, at this state if received an APNS message, this method will be called too.
Now here is the question, how to judge these 3 kind of clicks? Generally if user did't click message to enter app(2 and 3's click), app shouldn't execute this message(like open a new page base on the message info). But how to know it is from 2 and 3?
From below answers, found that kind-2 has been resolved with the applicationState, while kind-3 still has left no answer, anyone knows how to fix it?
Check the app state in the application didReceiveRemoteNotification
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
if ( application.applicationState == UIApplicationStateInactive || application.applicationState == UIApplicationStateBackground )
{
//app was on background
}
}
You can check that using UIApplication state as the following:
///- if app is running in foreground
if (application.applicationState == UIApplicationStateActive) {
[self pushNotificationReceivedWhileActiveWithInfo:userInfo];
///- if app was running in background
} else if (application.applicationState == UIApplicationStateBackground) {
if (self.pushReceivedWhileClosed != YES) {
[self pushNotificationReceivedWhileInBackgroundWithInfo:userInfo];
}
}
Also you’ll need to handle the notification when the app was fully closed by getting it from didFinishLaunchingWithOptions when opened from a notification by saving the notification data and execute it when the user reaches the home screen let’s say:
///- when receiving push notification while [app closed]
if (launchOptions != nil) {
NSDictionary *dictionary = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (dictionary != nil) {
NSLog(#"[AppDelegate]##########################AppDelegate######################### Received notification while app CLOSED");
///- save the dictionary data and execute it when the app loads for example.
self.pushReceivedWhileClosed = YES;
}
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSMutableDictionary *userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (userInfo != nil)
{
comingFromNotification = YES;
}else{
comingFromNotification = NO;
}
}
Related
I am having an iOS application where I am receiving Push Notifications. I have the following code in AppDelegate
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
if ( application.applicationState == UIApplicationStateActive )
{
// app was already in the foreground
}
else
{
NSLog(#"Received push notification");
}
}
I am running the app in Xcode with my iPhone. When the app is in background I am able see the NSLog when the notification comes.
When I close the app i.e removing it from opened apps in iOS the debugger session in Xcode stops and I can't see the NSlog after that even after receiving notification on my iPhone for the app when the app is closed (Push Notifications can come to app even if it is closed)
How can I print logs when the app is being opened from closed state?
It sounds like you're describing a few different things here.
First, you won't be able to see any NSLog notification messages if/after the app is fully terminated, until the app is launched by some mechanism.
If you want to print logs when the app is being opened from a terminated state, you can do something like the following:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// ...
NSLog(#"%s", __PRETTY_FUNCTION__); // print the function name
// Process remote notifications
NSDictionary *remoteNotification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (remoteNotification) {
[self processNotification:remoteNotification];
}
}
- (void)processNotification:(NSDictionary *)payload {
NSString *someString = [payload objectForKey:#"someStringKey"];
// do something!
}
My problem seems to be duplicate of this one,but it's not. While application is killed and not running in the background, if I receive push notification and clicked the notification banner, it works fine. "userInfo" isn't empty and application handles the notification. BUT if i dismiss the notification banner and open the app via clicking the application icon, this "userInfo" returns nil.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions {
NSDictionary* userInfo = [launchOptions valueForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if(userInfo != nil){
//Handling notification
}
}
and also
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
if([application applicationState] == UIApplicationStateActive) {
NSLog(#"...1");
}else if([application applicationState] == UIApplicationStateInactive){
NSLog(#"...2");
}else if([application applicationState] == UIApplicationStateBackground){
NSLog(#"...2");
}
completionHandler(UIBackgroundFetchResultNoData);
}
Is there any way to handle these notifications or should I handle them by my own ?
No your app is only informed about the notification that is used to open/launch your app.
There is no way to detect of there are any notification in the notification center for your app. You need to build this yourself in your apps server.
I have searched for hours but somewhere is saying that,
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandlerNotification
is called, regardless of application is in active state or inactive state. Somewhere it says when application is not active it push notification calls your didFinishLaunchingWithOptions and there you can detect notification like this :
NSDictionary *userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]`
In my case, both are working if user tap on notification and everything is fine, but the problem occurs when user does not tap on notification and direct open app from its icon, then neither didFinishLaunchingWithOptions detect it has notification nor didReceiveRemoteNotification get called. I need to save messages in database, received from push notification, but without tapping on notification how to call method?
to get notification data in didFinishLaunchingWithOptions do this code
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if(launchOptions != nil)
{
NSDictionary *remoteNotif = [launchOptions objectForKey: UIApplicationLaunchOptionsRemoteNotificationKey];
if(remoteNotif != nil){
[UIApplication sharedApplication].applicationIconBadgeNumber = [[[remoteNotif objectForKey:#"aps"] objectForKey: #"badge"] integerValue];
//do something here to process notification
}
}
}
My app doesn't receive push notifications when it's not running.
I am trying to handle remote notification sent as JSON and update data in my app using data from given JSON.
All is going well when app is active or in background.
But when app is not running, app is processing notifications only when I open my app by tapping on notification, but not when I open app by tapping on icon.
Here is the code from appDelegate class:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[Parse setApplicationId:appId
clientKey:clKey];
if (application.applicationState != UIApplicationStateBackground) {
BOOL preBackgroundPush = ![application respondsToSelector:#selector(backgroundRefreshStatus)];
BOOL oldPushHandlerOnly = ![self respondsToSelector:#selector(application:didReceiveRemoteNotification:fetchCompletionHandler:)];
BOOL noPushPayload = ![launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (preBackgroundPush || oldPushHandlerOnly || noPushPayload) {
[PFAnalytics trackAppOpenedWithLaunchOptions:launchOptions];
}
}
[application registerForRemoteNotificationTypes:UIRemoteNotificationTypeBadge|
UIRemoteNotificationTypeAlert|
UIRemoteNotificationTypeSound|
UIRemoteNotificationTypeNewsstandContentAvailability];
NSDictionary *notificationPayload = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
[self processPushNotification:notificationPayload foreground:YES];
return YES;
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
TFLog(#"didRegisterForRemoteNotificationsWithDeviceToken");
// Store the deviceToken in the current installation and save it to Parse.
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation setDeviceTokenFromData:deviceToken];
[currentInstallation saveInBackground];
TFLog(#"deviceToken: %#, currentInstallation.badge: %ld", currentInstallation.deviceToken, (long)currentInstallation.badge);
TFLog(#"deviceToken: %#, deviceType: %#", currentInstallation.deviceToken, currentInstallation.deviceType);
TFLog(#"installationId: %#", currentInstallation.installationId);
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
TFLog(#"didFailToRegisterForRemoteNotificationsWithError %#", error);
if (error.code == 3010) {
TFLog(#"Push notifications are not supported in the iOS Simulator.");
} else {
// show some alert or otherwise handle the failure to register.
TFLog(#"application:didFailToRegisterForRemoteNotificationsWithError: %#", error);
}
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
TFLog(#"%#", userInfo);
[PFPush handlePush:userInfo];
[self processPushNotification:userInfo foreground:YES];
[PFAnalytics trackAppOpenedWithRemoteNotificationPayload:userInfo];
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
TFLog(#"didReceiveRemoteNotification2");
[self processPushNotification:userInfo foreground:YES];
[PFAnalytics trackAppOpenedWithRemoteNotificationPayload:userInfo];
}
As result, app is receiving remote notification in all states, excepting when it is not running.
What have I missed?
You have missed the bit in the Local and Push Notification Programming Guide where it says -
If the action button is tapped (on a device running iOS), the system
launches the application and the application calls its delegate’s
application:didFinishLaunchingWithOptions: method (if implemented); it
passes in the notification payload (for remote notifications) or the
local-notification object (for local notifications).
If the application icon is tapped on a device running iOS, the
application calls the same method, but furnishes no information about
the notification
Also, this note from Apple -
Important: Delivery of notifications is a “best effort”, not
guaranteed. It is not intended to deliver data to your app, only to
notify the user that there is new data available.
If your application is launched from the app icon rather than the notification you need to check for updated content independent of any push notification that may have been received. This enables an application to behave differently when it opens from a notification and when it is opened from the app icon.
For example, the Facebook app opens directly to the item in the notification when launched from the notification alert but not when launched from the app icon - which is the "correct" behaviour from a user point of view. If I interact with the notification then I am interested in its content. If I launch the app from the icon then I just want to use the app - I can then access the notifications in the app if I want.
There's no way you can get the information about your push notification JSON payload when you launch the app by explicitly tapping the app icon.
That's as per Apple's design. When you open the application from any push notification action, then you can retrieve the push notification in application: didFinishLaunchingWithOptions: delegate method. Typically you look up for the UIApplicationLaunchOptionsRemoteNotificationKey key in your launchOptions dictionary.
But, when you open the app by explicitly tapping the application icon, though still the application: didFinishLaunchingWithOptions: delegate will be called, the UIApplicationLaunchOptionsRemoteNotificationKey key will return nil.
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!!