UILocalNotification doesn't trigger didReceiveLocalNotification when tapping from Notification Center - ios

I'm using Objective-c to create a UILocalNotification in my iPhone app. I'm targeting iOS 8 and using XCode 6.
My problem relates to handling UILocalNotification when app is not running and it's opened by tapping on a notification. When the user taps the notification and opens the app I use didReceiveLocalNotification in AppDelegate.m to show a particular View Controller and send the VC some data (a date).
This works fine when tapping the notification from the lockscreen. When tapping the notification in the Notification Center didReceiveLocalNotification is never called. I used a UIAlertView to test this on my device. didFinishLaunchingWithOptions is called, but when I include the code to show the particular View Controller in that method it never works (though another UIAlertView showed me it was running that part of the code, just never completing the showViewController method).
Here's my didFinishLaunchingWithOptions code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([UIApplication sharedApplication].scheduledLocalNotifications.count >= 1) {
// Handle local notification received if app wasn't running in background
UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if ([[notification.userInfo objectForKey:#"notification"] isEqual:#"mood rating"]) {
// Create reportVC
NSLog(#"launched app and about to show reportvc");
ReportViewController *reportVC = (ReportViewController *)[self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:#"reportVC"];
// Date stuff goes here - code removed
// show the reportVC
[self.window.rootViewController showViewController:reportVC sender:self];
}
} else {
[self createLocalNotification];
}
return YES;
}
And here is my didReceiveLocalNotification code:
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
// Create report view
ReportViewController *reportVC = (ReportViewController *)[self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:#"reportVC"];
// Same date stuff goes here as in didFinishLaunchingWithOptions
// Show report vc
[self.window.rootViewController showViewController:reportVC sender:self];
}
The date stuff that I've take out is just checking if it's after 9pm or not, and either creating today's date or yesterday's, and then setting the result as a property of the reportVC. Let me know if that's relevant and I'll add it back in.
So here's some stuff I've tried to fix this:
I've tried using presentViewController:animated:completion: instead of showViewController:sender: but I want to use showViewController so I can have the navbar show up, and that didn't fix the problem anyway.
I've tried adding this line to my didFinishLaunchingWithOptions method:
[self application:application didReceiveLocalNotification:notification];
which did kind of fix the problem—when tapping from Notification Center it opened the right view, but it ruined lockscreen notifications. With this line added, when tapping a notification from the lockscreen I got the reportVC presented twice: the first one was a black screen apart from the navbar, and the one on top of that was correct.
I tried replacing my current showViewController line of code with this instead:
UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;
[topController showViewController:reportVC sender:self];
but that didn't fix the problem either.
I tried taking the code I've doubled up on out of my didFinishLaunchingWithOptions method, because I realised didReceiveLocalNotification seems to be run anyway once didFinishLaunchingWithOptions is done. That does seem to be the case, but it doesn't fix my problem with Notification Center notifications.
And in case this is screwing it up, here's how I'm testing this at the moment:
I add these two lines to the didFinishLaunchingWithOptions method:
[[UIApplication sharedApplication] cancelAllLocalNotifications];
[self createLocalNotification];
I change the scheduled time for the notification to be about 3 minutes into the future. It's normally set to go off at 9pm every night using date components like this:
dateComponents.hour = 21;
dateComponents.minute = 0;
And I change the notification's repeatInterval to be NSCalendarUnitMinute instead of NSCalendarUnitDay which is what it's set to for release builds.
Then I run the app on my device using XCode, and stop it once it's run and scheduled the notification. I run it again without these two lines:
[[UIApplication sharedApplication] cancelAllLocalNotifications];
[self createLocalNotification];
And then stop the app from XCode, open multitasking on my device, swipe the app up to close it, and wait for a notification to arrive. After tapping each test notification I multitask and close the app again so I can test from a totally closed app each time.

You may want to try this (present your view controller asynchronously with the dispatch_async()):
if ([[notification.userInfo objectForKey:#"notification"] isEqual:#"mood rating"]) {
dispatch_async(dispatch_get_main_queue(), ^{
// Create reportVC
NSLog(#"launched app and about to show reportvc");
ReportViewController *reportVC = (ReportViewController *)[self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:#"reportVC"];
// Date stuff goes here - code removed
// show the reportVC
[self.window.rootViewController showViewController:reportVC sender:self];
});
}
or this (call [self.window makeKeyAndVisible] before presenting your view controller):
if ([[notification.userInfo objectForKey:#"notification"] isEqual:#"mood rating"]) {
// Create reportVC
NSLog(#"launched app and about to show reportvc");
ReportViewController *reportVC = (ReportViewController *)[self.window.rootViewController.storyboard instantiateViewControllerWithIdentifier:#"reportVC"];
// Date stuff goes here - code removed
// show the reportVC
[self.window makeKeyAndVisible];
[self.window.rootViewController showViewController:reportVC sender:self];
}

Related

UILocalNotification when I take off application

I have created a UILocalNotification with
localNotification.fireDate = [CurrentDate dateByAddingTimeInterval:+60
So, After 60s, it will notification for me.
And my AppDelegate, I have call method.
(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
// Request to reload table view data
[[NSNotificationCenter defaultCenter] postNotificationName:#"reloadData" object:self];
NSLog(#"OK...");
// Set icon badge number to zero
application.applicationIconBadgeNumber = 0;
}
When I click on notification, it is called didReceiveLocalNotification method. It is OK.
But when I click in App's Icon in screen, it is not called didReceiveLocalNotification method.
I have research and followed this code in didFinishLaunchingWithOptions
// Handle launching from a notification
UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
// If not nil, the application was based on an incoming notifiation
if (notification){
NSLog(#"test...");
}
But, it is not working.
It is get notification = nil.
So, it is not NSLog(#"test...");
Please help me!
when app icon is clicked its not local notification click, where as your code in didFinishLaunchingWithOptions works in the case when app is not running in background and user or you click on local notification. Whereas whenever app icon is clicked not always didFinishLaunchingWithOptionsis called, it is called only when app starts i.e not present in background but, applicationDidBecomeActive is called whenever app starts from anywhere in any mode. UIApplicationDelegate [https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol]

How to redirect to a specific page on click of app icon?

We are working on iPhone app and we have implemented push notification functionality. When the app is inactive, any notification received shows an alert and the badge icon counter gets incremented. When the user clicks either on the app's icon or on the alert, the app becomes active and everything works fine.
What we want is this: On new notification, whenever a user clicks on the app icon, it should open a specific page (e.g. the Notifications page). Currently, when the app becomes active, it shows the last previously open page.
How can I redirect the app (on foreground) to a specific page?
assume that this is your PayLoad
{"aps":{"alert":"Hai User","type":"center","badge":1,"sound":"default"}}
the didReceiveRemoteNotification method handle the payload
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
// this is used for clear the count when user press the notification.
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
[[UIApplication sharedApplication] cancelAllLocalNotifications];
//When notification is pressed on background it comes here
//Get strings based on information on your json payload for example
NSMutableString *notificationType =[[userInfo objectForKey:#"aps"]objectForKey:#"type"];
if([notificationType isEqualToString:#"center"]){
//redirect/push a viewcontroler here , it automatically navigate to your VC.
profileViewController *profile = [[profileViewController alloc] initWithNibName:#"profile ViewController" bundle:nil];
[self.window.rootViewController presentViewController: profile animated:YES completion:NULL];
}
}
notification types from Apple is here
Inside your
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
[self openNewViewController]; // read the userInfo from the payload and based on that open new controller
}
method you can directly push/modal/addsubview your desired page.
And inside "didFinishLaunchingWithOptions:" check for remote notification payload
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) {
NSDictionary *dict = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
//Call your page here
}
}

Determine if user did not clicked on the notification, when app is in the background

Is there any way to find out after notification is been sent, how many users clicked on the notification and how many people didnt click on the noficiation event (badge) when the app is in the background?
I am more interested to find out how many people didnt click, as people who clicked can be tracked as app will go in the foreground and request can be made vs if app is in the background, your http request may get lost.
update your app delegate code to the following code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
[super application:application didFinishLaunchingWithOptions:launchOptions];
NSDictionary *remoteNotif = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if(remoteNotif)
{
//launched from push notification
}else{
//Did not launch from push notification (tapped on app icon, or from multi tasking)
//**Didn't click on notification**
}
}
and this:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
if([application applicationState] == UIApplicationStateActive) {
// app was open, did not display the push alert/banner/badge
// **Didn't click on notification**
}else{
//launched from push notification
}
}
Its quite self explanatory. you can track when app was opened by tapping on a push notification and when it was opened without tapping on a notification.
I guess the closest you can come to know who didn't click your notification is by checking in your AppDelegate's didFinishLaunchWithOptions method that your app didn't get launched as a result of the user tapping a notification after you send out the notification. In other words, I think you answered your own question in your question.

how to recieve local notification in ios when app is closed

I am using local notification for alarm system but I am facing some problem while handling local notification, When i clicked on alarm notification (When app is closed) its launches app but the problem is it should go to didFinishLaunchingWithOptions function but it's not going inside any of the function in appDelegate (I used breakpoints to check).
I'm using story board with navigation controller, I want to open a specific view controller on notification click when app is closed.
but when I'm launching that app normally it's going inside didFinishLaunchingWithOptions function.
Please suggest.
Any help would be appreciated.
main.m
#import "DEMOAppDelegate.h"
int main(int argc, char * argv[])
{
#autoreleasepool
{
return UIApplicationMain(argc, argv, nil, NSStringFromClass([DEMOAppDelegate class]));
}
}
DEMOAppDelegate.m
#import "DEMOAppDelegate.h"
#implementation DEMOAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UILocalNotification *localNotif = [launchOptions objectForKey: UIApplicationLaunchOptionsLocalNotificationKey];
if (localNotif)
{
NSLog(#"Recieved Notification %#",localNotif);
}
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
}
- (void)applicationWillTerminate:(UIApplication *)application
{
}
-(void) application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:notification.alertAction message:notification.alertBody delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
}
#end
It's not possible, when your app is not running notification it will not react on notification directly.
didFinishLaunchingWithOptions will contain information about notification only when user opened your app through this notifcation. If he cancels it and opens your app through dashboard icon you are not gonna see this in this method.
Unfortunatelly if you need to react on all notification that happened from last time the user opened the app, only way is to build your own tracking logic and get all events that were in the past based on time.
Also there is no way to even get a list of notification you scheduled for your app, so usually it is a good idea to build in time-based event logic and use notification on top of it, but all the logic happens on your own time-based code. This way even if user disable notifications your critical logic will work.
When the application is running in background you will get notification through application:didReceiveRemoteNotification. You can gather those notifications inside your app, and process them on applicationDidBecomeActive if you want to do anything specific inside your application for them after user comes back to the app from background.

How to handle user clicked local notification

I am using local notification in my app, the only thing i care about is what exact notification the user clicked.
the method
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
triggered when the notification is received but i need to handle the notification when the user clicked on it so this is useless to me.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
triggered only when app was not running, also not what i need
i searched the net but found only handles if the app is active or not running. how can i handle it when the app is on the background?
what i need in other words is to recognise the exact notification that the user clicked when the app was running on the background
When creating your UILocalNotification, you can set the userInfo to set any associated data/unique identifiers.
Ex.
UILocalNotification *someNotification = [[UILocalNotification alloc] init];
[someNotification setUserInfo:#{ kSomeUniqueIdentifierKey : #"identifier" }];
and then,
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
if ([notification.userInfo[kSomeUniqueIdentifierKey] isEqualToString:#"identifier"]) {
// We know what notification was responded to based on userInfo
}
}
The above method fires either immediately upon receiving the notification while the app was running or when the user taps the notification that fired while your app was in the background.
If you want to ignore these notifications while the app is running, you could always check the state of the application to determine if it's responding to the notification while running or in the background.
I am working with iOS 9, and the solution is to check the launchOptions within the AppDelegate didFinishLaunchingWithOptions. As follows:
// Were we launched from a local notification?
if let lo = launchOptions
{
if let ln = lo[UIApplicationLaunchOptionsLocalNotificationKey] as? UILocalNotification
{
// Do our thing...
}
}

Resources