The problem:
- (void) application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
is not called sometimes with iOS7.
It doesn't mater how we schedule the notification:
alarm.fireDate = [[NSDate date] dateByAddingTimeInterval:0.1];
[app scheduleLocalNotification:alarm];
or:
[app presentLocalNotificationNow:alarm];
My thoughts:
This happens in the case when the user slides before the notification alert animation is finished.
And if he waits just a half second before he slides - the notification is fired and the App proceeds as expected.
The problem probably is that application enters foreground before the notification is received.
Did anyone meet this? Is it a bug? Any solution? Thank you!
Is your application in the background or foreground? If it's in the foreground, I'm pretty sure that method is called. If it isn't, maybe you aren't putting that method in your application delegate.
If it's on the background, here's a few possible scenarios:
Your app has been killed by the user or the OS. In this case when the user wake up your app by tapping on the notification on the notification centre (or swiping in lock screen), your application delegate will have the application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions method called. You can get the notification from this method by:
[launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
Your app is in background, and the user tap on the notification in notification centre or lock screen. In this case, no delegate methods will be called. The documentation specifically said that didReceiveLocalNotification: is for when the app is in the foreground:
If the app is running in the foreground, there is no alert, badging,
or sound; instead, the application:didReceiveLocalNotification: method
is called if the delegate implements it.
So hopefully you can make an informed decision about what to do when you receive the notification. I personally find it a little bit weird that we don't get the notification object when the user launches the app by tapping the icon (not the notification). But I currently just write my logic around it.
Apple explicitly mentions in their documentation (Local and Remote Notification Programming Guide) that different methods get called depending on what state the app is in and what action the user takes (as Enrico mentioned).
Summary:
The user taps a custom action button in an iOS 8 notification.
In this case, iOS calls either application:handleActionWithIdentifier:forRemoteNotification:completionHandler: or application:handleActionWithIdentifier:forLocalNotification:completionHandler:. In both methods, you get the identifier of the action so that you can determine which button the user tapped. You also get either the remote or local notification object, so that you can retrieve any information you need to handle the action.
The user taps the default button in the alert or taps (or clicks) the app icon. ... the system launches the app and the app calls its delegate’s application:didFinishLaunchingWithOptions: method, passing in the notification payload (for remote notifications) or the local-notification object (for local notifications). ...
The notification is delivered when the app is running in the foreground. The app calls the UIApplicationDelegate method application:didReceiveLocalNotification: or application:didReceiveRemoteNotification:fetchCompletionHandler:.
So didReceiveLocalNotification is only fired when the app is already running and is in the foreground. You should also handle the second scenario where the isn't running, for which apple has the following code example:
Objective-C:
- (BOOL)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UILocalNotification *localNotif = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (localNotif) {
NSString *itemName = [localNotif.userInfo objectForKey:ToDoItemKey];
[viewController displayItem:itemName]; // custom method
app.applicationIconBadgeNumber = localNotif.applicationIconBadgeNumber-1;
}
[window addSubview:viewController.view];
[window makeKeyAndVisible];
return YES;
}
Swift (approximation provided by myself):
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if let localNotification = launchOptions?[UIApplicationLaunchOptionsLocalNotificationKey] as? UILocalNotification {
if let itemName = localNotification.userInfo?[ToDoItemKey] as? String {
handleNotification(localNotification)
application.applicationIconBadgeNumber = localNotification.applicationIconBadgeNumber - 1
}
}
.
.
.
}
Related
I have some UILocalNotifications in my app. When I receive one while I am in the app or if I press on the notification banner from the block screen or when it appears, the didReceiveLocalNotification works just fine. However, if I do not use the application and if I press on the icon of it (not on the banner) after notification banner appeared, the didReceiveLocalNotification method is not being called.
What should I do in this case, if I still want receive information about local notifications? Can I get it somehow in the applicationDidBecomeActive: method for example?
If you're app is completely shut down and you open it with a local notification, your local notification info will be passed into application:didFinishLaunchingWithOptions:.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UILocalNotification *localNotification = launchOptions[UIApplicationLaunchOptionsLocalNotificationKey];
if (localNotification)
{
// Do whatever
}
// ...
}
I am doing a project in which push notification feature is one of the key feature.
It is working fine when I am in the app, I receive notification and handle that notification.
But the issue is when I am in background and notification receives I see badge on my app icon
and when I click on the icon my app is launching but the didReceiveRemoteNotification method is not called so I am unable to handle that notification.
And another issue is some times it shows the notification message in device notification list and some times it didn't .
When I am entering in my app through clicking on notification list item the didReceiveRemoteNotification calls and I am successfully able to handle notification.
I write following code in didFinishLaunchingWithOptions:(NSDictionary *)launchOptions method
NSDictionary* remoteNotif = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (remoteNotif != nil)
{
NSLog(#"didFinishLaunchingWithOptions\nNotification recieved:\n%#",remoteNotif);
notificationData=[[NSDictionary alloc]initWithDictionary:remoteNotif];
[notif saveNotification:remoteNotif];
}
Help me to resolve this .
Thanks in advance.
Things which you are doing in didfinishlaunch method do in didReceiveRemoteNotification as well.
When you will come from background, didfinishlaunch won't be called.
- (void)application:(UIApplication*)application didReceiveRemoteNotification:
(NSDictionary*)userInfo
{
UIApplicationState state = [application applicationState];
if (state == UIApplicationStateActive)
{
//What you want to do when your app was active and it got push notification
}
else if (state == UIApplicationStateInactive)
{
//What you want to do when your app was in background and it got push notification
}
}
From Apple's Local And Push Notification programming guide
Handling Local and Remote Notifications
Let’s review the possible scenarios when the system delivers a local
notification or a remote notification for an application.
The notification is delivered when the application isn’t running in the foreground.
In this case, the system presents the notification, displaying an
alert, badging an icon, perhaps playing a sound.
As a result of the presented notification, the user taps the action
button of the alert or taps (or clicks) the application icon.
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 . If the application icon is clicked on a
computer running OS X, the application calls the delegate’s
applicationDidFinishLaunching: method in which the delegate can
obtain the remote-notification payload.
The notification is delivered when the application is running in the foreground.
The application calls its delegate’s
application:didReceiveRemoteNotification: method (for remote
notifications) or application:didReceiveLocalNotification: method
(for local notifications) and passes in the notification payload or
the local-notification object.
So in your case, when application is running in background, and when you click the notification/alert, operating system brings your app into foreground. So it falls under second point.
You can implement application:didReceiveRemoteNotification: method to get the notification payload, if action button is tapped. But when the application icon is pressed instead of action message, the notification payload is not forwarded with the method. Your only option is to contact your server and sync the data. After all as per Apple's policy, push notification only tells that there is data on server, and either way you need to connect to server and get the data.
I did the same thing in one of my application. There is no way to handle notification when clicked on app icon. So, what you can do is to make a server call to get latestPushNotificationIdSync.
You must be storing your data somewhere on server so, you need to check on your server what is the latest latestPushNotificationId.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
notificationContentDict = launchOptions;
if([[launchOptions valueForKey:#"UIApplicationLaunchOptionsRemoteNotificationKey"]count]>0){
application.applicationIconBadgeNumber = 0;
NSString *key = [[launchOptions valueForKey:#"UIApplicationLaunchOptionsRemoteNotificationKey"] valueForKey:#"id"];
contentId = key;
////// you can use this content id ///// write your code here as per the requirement ..////
//// display your content on UI /// either get from server or local ...///
[self displayContent:[launchOptions valueForKey:#"UIApplicationLaunchOptionsRemoteNotificationKey"] application:application];
}
else
[application registerForRemoteNotificationTypes:UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound];
return YES;
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
NSLog(#"badge count ..%d", application.applicationIconBadgeNumber);
if( application.applicationIconBadgeNumber >0){
if(!notificationContentDict){
make a server call to get "latestPushNotificationIdSync"
application.applicationIconBadgeNumber = 0;
NSLog(#"badge count applicationDidBecomeActive.%d", application.applicationIconBadgeNumber);
}
}
}
With iOS 5, push notifications can appear as banner and disappear after a few seconds.
I understand that didReceiveRemoteNotification will be called when user taps on the banner.
My question is, if the banner has disappeared and my user sees that there is a badge number on the app, they will tap on the app icon to start the app. Now if the app is running in the background, how do I check that the app is brought to foreground and there has been a notification, and do the necessary?
The purpose of my notification is basically to inform user there has been an update to the app content and encourage them to run the app to get the latest contents. My app only checks for latest contents at launch time and doesn't check for updates periodically.
This question is a bit old, but I'll pop what I've found in here anyway.
There are two methods you need to implement in your app delegate to check if your app was either launched from the remote notification (From when the app is not running on your device), or received the remote notification while running (in the background or foreground).
First is a method that is already in your App Delegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
To check if this was launched from a remote notification, have some code similar to this:
// Check to see if launched from notification
if (launchOptions != nil)
{
NSDictionary* dictionary = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (dictionary != nil)
{
NSLog(#"Launched from push notification: %#", dictionary);
// DO SOMETHING HERE
}
}
The other method you will need to implement is specifically for the case that your application for when your application is running:
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
NSLog(#"Received notification: %#", userInfo);
}
How you handle the notification from there is up to you, but that's how your app knows about it!
In this second method, you can check the UIApplicationState of the passed application to find out if you were in the foreground or background.
I'm making an app that keeps track of some reminders that repeats with an user defined interval.
I've made it so when the alert displays, the action title says "Renew". When you click this, the app opens, and here I want to create the next reminder, but the problem is that I don't know if the app opens because the notification button was tapped or if the notification fired while the app was running.
Anyone got any ideas?
The 'correct' way to do this is to examine your NSApplication's applicationState property in the application:didReceiveRemoteNotification: method of your delegate.
From the documentation for handling local notifications:
iOS Note: In iOS, you can determine whether an application is launched
as a result of the user tapping the action button or whether the
notification was delivered to the already-running application by
examining the application state. In the delegate’s implementation of
the application:didReceiveRemoteNotification: or
application:didReceiveLocalNotification: method, get the value of the
applicationState property and evaluate it. If the value is
UIApplicationStateInactive, the user tapped the action button; if the
value is UIApplicationStateActive, the application was frontmost when
it received the notification.
This is similar to your solution using flags set in applicationWillEnterForeground and applicationDidBecomeActive but with system support.
I don't know if my question was unclear but it seems that I got 4 different answers that all misinterpreted my question :P
However, I discovered that the didReceiveLocalNotivication happens between willEnterForeground and didBecomeActive, so I did this to determine if the app was already open or not:
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
NSLog(#"Opened from notification? %#", wasInactive ? #"yes!" : #"no!");
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
wasInactive = YES;
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
wasInactive = NO;
}
Look up the documentation for UIApplication launch option keys. The last parameter to your application:didFinishLaunchingWithOptions: delegate method contains the information you need.
Also, look at application:didReceiveLocalNotification.
You're looking for
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
or
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
Documentation
If your application is already running you'll get this delegate message on the app delegate
application:didReceiveLocalNotification:
If it wasn't running you'll have to use
application:didFinishLaunchingWithOptions:
You need to respond appropriately in both methods to cover all cases
UPDATED
To detect if the user activated the action button requires a little more complexity. We can tell that application:didFinishLaunchingWithOptions: will have the local notification as a launch option, but it's more difficult with the application:didReceiveLocalNotification:.
Since the application becomes active after the user taps the button, we have to defer until we see that message (or not). Set an NSTimer in application:didReceiveLocalNotification and cancel it in didBecomeActive. That means the user pressed the action button. If the timer isn't cancelled the user was inside the app when it fired.
In your app delegate in this method:
- (BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
You have to examine the launchOptions looking at this key:
UIApplicationLaunchOptionsLocalNotificationKey
When you are already active this will be called:
- (void) application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
If a UILocalNotification fires with a sound set, and the user taps "Cancel" on the notification alert, the sound is stopped. But if the user taps "View", iOS then delivers the notification to the app and the sound keeps on playing. Is there any way to cancel this sound from the app? Canceling the notification in the app once the notification is delivered doesn't work (I didn't expect it to, the notification has already been delivered after all), and since I don't have the sound's system sound ID (and I didn't allocate it in the first place), I can't call AudioServicesDisposeSystemSoundID (can I?).
Is it possible to stop a UILocalNotification sound from playing if the user taps the Action button of the notification's alert?
It does not stop on the device too (5.1)
I have been trying to fix it but I can't figure it out.
I actually got it to stop on the simulator using this
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
UILocalNotification *localNotif = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (localNotif) {
[[UIApplication sharedApplication] cancelLocalNotification:localNotif];
}
return YES;
}
but It still doesn't stop on the device
Apparently the problem only exists on the simulator (iOS 4.2 sdk), not on the actual device.
It can be handled in application delegate method as follows, if the user taps the Action button of the notification's alert, then the following method will be called, there we can cancel the notification
- (void)application:(UIApplication *)app
didReceiveLocalNotification:(UILocalNotification *)notif {
[[UIApplication sharedApplication]cancelLocalNotification:notif];
}
I had the same problem when initially testing with an iPhone 4.
The problem appears to have gone away as of iOS 8 and now Local Notification Sounds stop when canceled after transitioning to the App.
iPhone4 [ iOS - 7.1.2 ] - Local Notification sound keeps playing in App no matter what
iPhone6 [ iOS - 8.1.1 ] - Local Notification sound stop when canceled programmatically
From what I can infer, it appears a fix exists somewhere in iOS 8.x
(Alas I didn't manage to find any documentation or release notes on the matter.)
The Problem becomes a non-issue for apps focusing on iOS 8
(provided you cancel the Local Notification coming in)