I made a very simple app that can run in the background while a timer runs. If the app is still in the background and the timer ends it will send a local notification and set the application badge to 1. When I start the app I always clear it. I noticed that after installing Xcode 6 I was getting this message every time I started the app:
"Attempting to badge the application icon but haven't received permission from the user to badge the application"
Clearly that text is generated by my app setting the badge to 0 to clear it. Where do I set these permissions or request them? Is it now regarded as a push notification?
The problem has been fixed and the answer is posted below. The bottom line is that you need to get confirmation from the user for any kind of notification, while that used to be true only for push notifications.
I ended up not using the Application Badge at all and I abandoned the initial code snippet that I have posted here in the mean while. Since there are still people reading and commenting on this question I will add my working current solution here as well. It does contains checks for iOS7 but I do not use the callback method. Also this version doesn't just ask for Application Badge permission anymore.
Solution
UIUserNotificationSettings* notificationSettings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings];
This is what I use now
.h file
#import <Foundation/Foundation.h>
#interface NotificationPermissionHandler : NSObject
+ (void)checkPermissions;
+ (bool)canSendNotifications;
#end
.m file:
#import "NotificationPermissionHandler.h"
#implementation NotificationPermissionHandler
static const UIUserNotificationType USER_NOTIFICATION_TYPES_REQUIRED = UIUserNotificationTypeAlert | UIUserNotificationTypeSound;
static const UIRemoteNotificationType REMOTE_NOTIFICATION_TYPES_REQUIRED = UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound;
+ (void)checkPermissions;
{
bool isIOS8OrGreater = [[UIApplication sharedApplication] respondsToSelector:#selector(registerUserNotificationSettings:)];
if (!isIOS8OrGreater)
{
[NotificationPermissionHandler iOS7AndBelowPermissions];
return;
}
[NotificationPermissionHandler iOS8AndAbovePermissions];
}
+ (void)iOS7AndBelowPermissions
{
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:REMOTE_NOTIFICATION_TYPES_REQUIRED];
}
+ (void)iOS8AndAbovePermissions;
{
if ([NotificationPermissionHandler canSendNotifications])
{
return;
}
UIUserNotificationSettings* requestedSettings = [UIUserNotificationSettings settingsForTypes:USER_NOTIFICATION_TYPES_REQUIRED categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:requestedSettings];
}
+ (bool)canSendNotifications;
{
UIApplication *application = [UIApplication sharedApplication];
bool isIOS8OrGreater = [application respondsToSelector:#selector(currentUserNotificationSettings)];
if (!isIOS8OrGreater)
{
// We actually just don't know if we can, no way to tell programmatically before iOS8
return true;
}
UIUserNotificationSettings* notificationSettings = [application currentUserNotificationSettings];
bool canSendNotifications = notificationSettings.types == USER_NOTIFICATION_TYPES_REQUIRED;
return canSendNotifications;
}
#end
This was my first solution
I kept it just as a reference for to the initial discussion. This code is not maintained.
UIUserNotificationSettings* notificationSettings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge];
[[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings];
You can also stack permissions into one request by doing this:
UIUserNotificationSettings* notificationSettings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings];
Also since iOS 8 it's possible to figure out what kind of alerts are allowed by the user:
UIUserNotificationSettings* notificationSettings = [[UIApplication sharedApplication] currentUserNotificationSettings];
if (notificationSettings.types == UIUserNotificationTypeBadge)
{
// change the badge
}
I ended up using this code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if (![defaults objectForKey:#"first_run"])
{
[self setDefaults];
}
[self askAlertPermissions];
if ([self canChangeBadge])
{
[self setBadge:0];
}
return YES;
}
- (void)setDefaults;
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:[NSNumber numberWithBool:NO] forKey:#"alerts_allowed"];
[defaults setObject:[NSDate date] forKey:#"first_run"];
// More defaults if needed
[defaults synchronize];
}
- (void)askAlertPermissions;
{
UIUserNotificationSettings* notificationSettings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings];
}
// This will be called only after confirming your settings
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings;
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// There is also a built in method to find out if the user has appropriate settings, you might want to use that instead if you just want to know what the setting is
[defaults setObject:[NSNumber numberWithBool:YES] forKey:#"alerts_allowed"];
}
- (bool)canChangeBadge;
{
UIUserNotificationSettings* notificationSettings = [[UIApplication sharedApplication] currentUserNotificationSettings];
return notificationSettings.types == UIUserNotificationTypeBadge;
}
More to read:
https://developer.apple.com/library/content/releasenotes/General/WhatsNewIniOS/Articles/iOS8.html
https://developer.apple.com/documentation/uikit/uiapplication
Related
I need to check if user have enabled push notifications in Settings. For this I use this code:
if ([[UIApplication sharedApplication] isRegisteredForRemoteNotifications] == NO) {
NSLog(#"NO");
// NO
}
if ([[UIApplication sharedApplication]respondsToSelector:#selector(currentUserNotificationSettings)]){
UIUserNotificationSettings *noticationSettings = [[UIApplication sharedApplication] currentUserNotificationSettings];
if (!noticationSettings || (noticationSettings.types == UIUserNotificationTypeNone)) {
NSLog(#"NO");
// here it does not work, I enabled push notifications
// in settings and noticationSettings.types returns UIUserNotificationTypeNone
// NO
} else {
NSLog(#"YES");
// YES
}
}
but problem is that noticationSettings.types returns alwyas UIUserNotificationTypeNone, regardless whether are push notifications enabled in Settings or not.
As you can see on the picture, I have enabled push notifications, but noticationSettings.types returns me UIUserNotificationTypeNone, can anybody tell me where could be the problem? Thanks
Your screenshot indicates that you did not call registerUserNotificationSettings with UIUserNotificationTypeBadge or UIUserNotificationTypeSound or UIUserNotificationTypeAlert.
Try the following:
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge
|UIUserNotificationTypeSound
|UIUserNotificationTypeAlert) categories:nil];
UIApplication *application = [UIApplication sharedApplication];
[application registerUserNotificationSettings:settings];
Can someone please take a look at the following code and tell me why the local notification isn't firing. Im running the app in XCode and using the debug option to simulate a background fetch but the local notification doesn't fire.
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
NSLog(#"performFetchWithCompletionHandler");
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
if (localNotif) {
localNotif.alertBody = #"Update demo text!";
localNotif.alertAction = #"OK";
localNotif.soundName = UILocalNotificationDefaultSoundName;
localNotif.fireDate = nil;
NSLog(#"Local Notification");
[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
}
//Perform some operation
completionHandler(UIBackgroundFetchResultNewData);
}
Do you query the user to allow push?
if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0) {
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound categories:nil];
[application registerUserNotificationSettings:settings];
}
Instead of checking device version, use below code that checks RespondToSelector:
if ([UIApplication instancesRespondToSelector:#selector(registerUserNotificationSettings:)])
{
[application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if ([UIApplication instancesRespondToSelector:#selector(registerUserNotificationSettings:)]){
[application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];
}
This code is for registering for UILocalNotification and this will also popup this:
Question 1: At this state when user has not chosen any option, How do I get notified when user choose one of the option which is Don't Allow or Ok? So I can execute app accordingly.
--
UIUserNotificationSettings *current = [[UIApplication sharedApplication] currentUserNotificationSettings];
UIUserNotificationType required = UIUserNotificationTypeSound | UIUserNotificationTypeAlert;
if(current.types & required) {
NSLog(#"Permission present: %lu", (unsigned long)current.types);
} else {
NSLog(#"Permission not present: %lu", (unsigned long)current.types);
}
When app will launch after first time, with this code I am trying to fetch which permission user has allowed (maybe he goto settings and disabled all types of notification alerts).
Question 2: I am simply getting numbers in log like 7 for the types of permission I am checking and 0 if user has not allowed the UILocalNotification. How to check for permissions correctly?
I used the below method in one of my project to determine whether the user has given permission or not, or whether he/she actually turned off the notifications in settings. Put this method in Appdelegate and check
-(BOOL)notificationServicesEnabled {
BOOL isEnabled = NO;
if ([[UIApplication sharedApplication] respondsToSelector:#selector(currentUserNotificationSettings)]){
UIUserNotificationSettings *notificationSettings = [[UIApplication sharedApplication] currentUserNotificationSettings];
if (!notificationSettings || (notificationSettings.types == UIUserNotificationTypeNone)) {
isEnabled = NO;
} else {
isEnabled = YES;
}
} else {
UIRemoteNotificationType types = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
if (types & UIRemoteNotificationTypeAlert) {
isEnabled = YES;
} else{
isEnabled = NO;
}
}
return isEnabled;
}
Then you can simple check with condition
if([self notificationServicesEnabled])
Hi I am stuck with this issue for over a week now. I followed this tutorial here to add Push Notification in my app. On initial run of my app the "App would like to send you push notification" is not appearing. But when I go to the Apps Notification it is already registered for notification for sounds and banners only and the Badge App Icon is not ON.
But when I Login in my app the Badge App Icon Notification Type is no longer there. (sorry cant post image)
And when I check my logs I have this run time error: Attempting to badge the application icon but haven't received permission from the user to badge the application
Here's my code in my AppDelegate.m
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
{
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert |
UIUserNotificationTypeBadge |
UIUserNotificationTypeSound categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
[[UIApplication sharedApplication] registerForRemoteNotifications];
}
else
{
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
UIRemoteNotificationTypeAlert |
UIRemoteNotificationTypeBadge |
UIRemoteNotificationTypeSound];
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
NSString *hexToken= [[[[deviceToken description]
stringByReplacingOccurrencesOfString: #"<" withString: #""]
stringByReplacingOccurrencesOfString: #">" withString: #""]
stringByReplacingOccurrencesOfString: #" " withString: #""];
[[NSUserDefaults standardUserDefaults] setObject:hexToken forKey:#"deviceToken"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
Any help will be appreciated.
Thanks.
I just tried using below way & it worked..
#ifdef __IPHONE_8_0
//Right, that is the point
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIRemoteNotificationTypeBadge
|UIRemoteNotificationTypeSound
|UIRemoteNotificationTypeAlert) categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
#else
//register to receive notifications
UIRemoteNotificationType myTypes = UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound;
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:myTypes];
#endif
Add below method for iOS 8.0
#ifdef __IPHONE_8_0
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
//register to receive notifications
[application registerForRemoteNotifications];
}
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void(^)())completionHandler
{
//handle the actions
if ([identifier isEqualToString:#"declineAction"]){
}
else if ([identifier isEqualToString:#"answerAction"]){
}
}
#endif
I have an app which uses local notifications and ti used to work fine in previous versions. I have updated the app for iOS 8 and tested and worked fine. After submitting the update to app store, a small number of users are complaining that they don't get any local notifications. However, a larger number of users that I've checked are fine and don't observe any issues.
For the users with the error (at least one of them), they can not see the "Notifications" item in the "Settings->myApp" screen; The whole option is missing not that it is disabled. "Location" and "Use Cellular Data" are in that screen but not the Notifications. I have tried to change the settings under "Settings->Notifications->myApp" and it work as expected.
Any suggestions for how to debug this issue would be very helpful. Thanks!
Try this for Objective-C:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions
{
// are you running on iOS8?
if ([application respondsToSelector:#selector(registerUserNotificationSettings:)])
{
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge|UIUserNotificationTypeAlert|UIUserNotificationTypeSound) categories:nil];
[application registerUserNotificationSettings:settings];
}
else // iOS 7 or earlier
{
UIRemoteNotificationType myTypes = UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound;
[application registerForRemoteNotificationTypes:myTypes];
}
}
For Swift:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
// Override point for customization after application launch.
if(UIApplication.instancesRespondToSelector(Selector("registerUserNotificationSettings:")))
{
application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: UIUserNotificationType.Sound | UIUserNotificationType.Alert | UIUserNotificationType.Badge, categories: nil))
}
else
{
//
}
return true
}
First You have to Register to Use Local Push Notification
UIApplication *application = [UIApplication sharedApplication];
if ([application respondsToSelector:#selector(registerUserNotificationSettings:)]) {
[application registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]];
}
then you can send Local Push Notification By This
UILocalNotification *notification = [UILocalNotification new];
notification.alertBody = #"Local Push !!!";
notification.applicationIconBadgeNumber=1;
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
void EnableLocalNotificationIOS8
{
UIApplication *app = [UIApplication sharedApplication];
if ([app respondsToSelector:#selector(registerUserNotificationSettings:)])
{
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:nil];
[app registerUserNotificationSettings:settings];
[app registerForRemoteNotifications];
}
}