I have an app that uses local notification. Until recently everything worked fine, but after iOS 8 was released, my local notifications aren't working when app is not running(removed from processes). This is my code in app delegate where I set my local notifications.
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
[[NSNotificationCenter defaultCenter] postNotificationName:localReceived object:self userInfo:notification.userInfo];
// Set icon badge number to zero
application.applicationIconBadgeNumber = 0;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if ([UIApplication instancesRespondToSelector:#selector(registerUserNotificationSettings:)])
{
[application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:
UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];
}
else if ([UIApplication instancesRespondToSelector:#selector(registerForRemoteNotificationTypes:)])
{
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
}
...
// Local notificaiton example. Icon badge
UILocalNotification *locationNotification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
NSLog(#"locationNotification:%#",locationNotification.alertBody);
if (locationNotification) {
// Set icon badge number to zero
application.applicationIconBadgeNumber = 0;
// call local notification method
[self application:[UIApplication sharedApplication] didReceiveLocalNotification:locationNotification];
}
return YES;
}
When my app is not running, the didFinishLaunchingWithOptions: method is called. In it I call the didReceiveLocalNotification method again where I call postNotificationName. I put the observer in my ViewControllers ViewDidLoad method:
- (void)viewDidLoad
{
...
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(recieveLocalNotification:) name:localReceived object:nil];
...
}
The problem is that the method recieveLocalNotification: is never called when app is not running. It's called every time when app is in background or running. Can anybody tell me what I'm doing wrong here?
Thanks in advance!
-viewDidLoad is called after the view of view controller is instantiated, the view is instantiated after the viewController.view is accessed for the first time.
So you can try call your viewController.view before posting the notification.
Related
I have the following flow: on the first app launch, I present a view which explains to the user why I need him to accept push notifications. He then has two options: skip (if he does this, I will never register for push notifications), or accept push notifications, in which case I display the push notifications popup where he can either accept or refuse.
I have two issues. First of all, I always used to register for push notifications in the AppDelegate, with the following code:
if ([application respondsToSelector:#selector(registerUserNotificationSettings:)])
{
UIUserNotificationType types = (UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound);
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
[application registerUserNotificationSettings:settings];
[application registerForRemoteNotifications];
}
in the - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions method.
I am now attempting to use this exact same code in the view I display with the two buttons, and I've removed the code from the AppDelegate. Will this work anyway? The issue with having the code in the AppDelegate is that it directly prompts the user to either accept or refuse the push notifications on first launch, something I want to avoid.
Then, there is the issue that I need to know as soon as the user has either accepted or refused the push notifications (on the popup). I don't care what decision he's made, I just need to know as soon as he's made one, to be able to present the next view.
I've tried the following delegate methods:
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
and -(void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
But they don't get called when I click on either allow or don't allow.
What is the right way to do this?
I found the solution to this problem.
First of all, as commented by #ystack, you can absolutely invoke registration for remote notifications anywhere in the app.
Secondly, I added this method in my App Delegate:
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
[application registerForRemoteNotifications];
[[NSNotificationCenter defaultCenter]
postNotificationName:#"pushNotification"
object:self];
}
Then I added this in my particular ViewController:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(pushNotificationClicked:)
name:#"pushNotification"
object:nil];
And finally this method in the same ViewController:
- (void)pushNotificationClicked:(NSNotification *) notification {
if ([[notification name] isEqualToString:#"pushNotification"]){
//do whatever you want
}
}
This way I get a callback when the user presses either Allow or Don't Allow.
I have noticed that when I select a push notification for my app from notification center, that notification no longer appears in the list of notifications. However, when I receive a notification and tap the banner right away, the app opens as it should, but when I pull down to view the notification center, that notification is still there. I have the following push handling code in my delegate:
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo {
[[UIApplication sharedApplication] cancelAllLocalNotifications];
//Presenting view controllers...
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// Extract the notification data
NSDictionary *notificationPayload = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
if(notificationPayload)
{
[[UIApplication sharedApplication] cancelAllLocalNotifications];
}
return YES;
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
application.applicationIconBadgeNumber = 0;
[[UIApplication sharedApplication] cancelAllLocalNotifications];
}
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
// when you tap on any of notification this delegate method will call...
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
[[UIApplication sharedApplication] cancelLocalNotification:notification];
}
You have to use following to remove all notifications
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
[self clearNotifications];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
[self clearNotifications];
}
- (void)clearNotifications
{
[[UIApplication sharedApplication] setApplicationIconBadgeNumber: 1];
[[UIApplication sharedApplication] setApplicationIconBadgeNumber: 0];
[[UIApplication sharedApplication] cancelAllLocalNotifications];
}
You can implement this delegate method:
-(void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
[application cancelLocalNotification:notification];
}
I setup a background task with beginBackgroundTaskWithExpirationHandler:^{ } and even after ending the task using
if ([[UIDevice currentDevice] isMultitaskingSupported]) {
[[UIApplication sharedApplication] endBackgroundTask:backgroundTaskID];
backgroundTaskID = UIBackgroundTaskInvalid;
}
NSLog(#"App State -- %d", [[UIApplication sharedApplication] applicationState]);
if([[UIApplication sharedApplication] applicationState] == UIApplicationStateActive)
{
//OpenGL operations
}
what I get is UIApplicationStateActive. Is this a bug ? How else do I determine the app is indeed in background ?
Instead of relying on the applicationState value, how about posting a custom notification at the end of the background task?
Set up as an observer for your custom notification:
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(backgroundTaskDidFinish:) name:#"BackgroundTaskDidFinish" object:<either nil or the object that will post the notification>];
}
Post your custom notification when the background task completes
if ([[UIDevice currentDevice] isMultitaskingSupported]) {
[[UIApplication sharedApplication] endBackgroundTask:backgroundTaskID];
backgroundTaskID = UIBackgroundTaskInvalid;
[[NSNotificationCenter defaultCenter] postNotificationName:#"BackgroundTaskDidFinish" object:self]
}
Respond to the notification, which your app should receive when it really returns to an active state
- (void)backgroundTaskDidFinish:(NSNotification *)notification
{
//OpenGL operations
}
Alternatively, you could look for one of the other notification messages that would indicate that your app is truly in an active state.
For instance, add your object as an observer for UIApplicationWillEnterForeground. Set a BOOL value when you receive that notification. Then, look for that BOOL value along with the UIApplicationState value.
Of course, at some point you'll have to clear the BOOL - say, when you're starting that background task - so it will be NO unless the app really did become active.
You can try to determine it with NSNotification:
- (void)viewDidLoad {
[super viewDidLoad];
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification object:app];
}
- (void)applicationWillResignActive:(NSNotification *)notification {
//do what you want when your app is in background
}
I want to clear all push notifications of my application, once user selects one of the push notification.
I have seen other threads here which says it's not possible in iOS.
but I have one application downloaded from app store, which does the same thing.
If it is a local notification then to remove badge icon you have to do it like this
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
localNotification.applicationIconBadgeNumber = 1;
If it is push notification the you can do it by code written below
[UIApplication sharedApplication].applicationIconBadgeNumber=0;
You may call these methods anywhere you want. For example if you want to clear notification at the moment when the app is launched then write it in
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
if your app doesn't use the badge number you have to first set, then reset it to remove it from notification centre.
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:1];
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
All you need to do is
application.applicationIconBadgeNumber = 0;
in
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions.
EDIT
If you are not closing your app but just sending it to background. Then add this in your below function as well.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
if ( application.applicationState == UIApplicationStateInactive || application.applicationState == UIApplicationStateBackground )
{
//opened from a push notification when the app was on background
application.applicationIconBadgeNumber = 0;
}
}
[[UIApplication sharedApplication] cancelAllLocalNotifications];
[[UIApplication sharedApplication] setApplicationIconBadgeNumber: 0];'
I added the above code to didfinishLaunchingWithOptions but when a user taps a notification in his notification center and enters my app the notification does not gets cleared.
Edit:
I also tried adding this to my code:
You Also need to increment then decrement the badge in your
application:didReceiveRemoteNotification: method if you are trying
to clear the message from the message centre so that when a user
enters you app from pressing a notification the message centre will
also clear, ie:
[[UIApplication sharedApplication] setApplicationIconBadgeNumber: 1];
[[UIApplication sharedApplication] setApplicationIconBadgeNumber: 0];
[[UIApplication sharedApplication] cancelAllLocalNotifications];
as describes here: iOS application: how to clear notifications? but the notification still won't clear from the notification center
I just Added a Badge number manually to my application and pasted
- (void)applicationDidBecomeActive:(UIApplication *)application
{
application.applicationIconBadgeNumber = 0;
}
To my AppDelegate. For me this works like a charm.
Note that didfinishLaunchingWithOptions and applicationDidBecomeActive are not the same as Mouhammad Lamaa explained. If you paste this to your AppDelegate and tap the notification in notification center it should disapper. If it does not your App maybe creates a new Notification after becoming active?
add this code
- (void)applicationDidBecomeActive:(UIApplication *)application
{
application.applicationIconBadgeNumber = 0;
}
the didfinishlaunchingwithoptions launched at the initial launch of your app. if your app the running in the background, didfinishlaunchingwithoptions will not be launched.
When the user open application from notification action - it launches with
- (BOOL)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UILocalNotification *remoteNotif =
[launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (remoteNotif) {
//handle remote notification
}
....
}
But when the app was in background it calls
- application:didReceiveRemoteNotification:
Also method [[UIApplication sharedApplication] cancelAllLocalNotifications]; cancel registered LOCAL notifications only. Push notifications can't be canceled - they delivered immediately and executed only once.