Trying to work around a few corner cases for when push notifications are denied in the app and I have two questions:
1) Is there a way to reset whether the user has seen the notification request pop up?
2) Is there any way to determine if the user has said no to the notification request?
1) No, unless there's some private API that does that, but that's not allowed by Apple
2) The first time your app is started, after calling registerForRemoteNotificationTypes, you can check if didRegisterForRemoteNotificationsWithDeviceToken is called. If it's not, the user said "No thanks".
You can always check the status of the permissions if the user changes them, you can check them on applicationDidBecomeActive
- (void)applicationDidBecomeActive:(UIApplication *)application
{
if ([[UIApplication sharedApplication] respondsToSelector:#selector(isRegisteredForRemoteNotifications)]) {
if ([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]){
NSLog(#"Notifications Enabled ios 8");
} else {
NSLog(#"Notifications not Enabled ios 8");
}
} else {
UIRemoteNotificationType types = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
if (types & UIRemoteNotificationTypeAlert)
{
NSLog(#"Notifications Enabled");
}
else
{
NSLog(#"Notifications not Enabled");
}
}
}
updated to make it work on iOS 8 too
Related
This question is specific to iOS 10 APNS changes.
This is the flow of my app:
App Installed
App Starts ➝ Login Screen
Successful Login ➝ Home Screen
Push Notification ➝ Request
Push Notification ➝ Don't Allow
App Close
Settings ➝ User enabled Push Notification
App Open
How to check if settings updated?
App Close
Settings ➝ User disabled Push Notification
App Open
How to check if settings updated?
I am only requesting for push notification (step 4.) when the user logs in. So until a user logs out I will not able to re-request for the push.
Is there any neat and clear solution to this so that we can support iOS 10 changes while still supporting iOS 8 or 9?
UIUserNotificationSettings was deprecated back in iOS8. If you want to access the general status of your apps settings, check out UNUserNotifications, the new framework. My understanding is that it treats push and local as one thing. When you register notifications, you can then call to register push. But for the local permissions -- badging and so on, you still need to request user permission. That is, your device can accept push notifications without user permission in order to received data updates, but you can only show notifications via the center with permissions. Here's how to see what permissions have been granted.
Import the framework into your class
#import UserNotifications;
Query the settings
- (void)_queryNotificationsStatus
{
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings *settings){
//1. Query the authorization status of the UNNotificationSettings object
switch (settings.authorizationStatus) {
case UNAuthorizationStatusAuthorized:
NSLog(#"Status Authorized");
break;
case UNAuthorizationStatusDenied:
NSLog(#"Status Denied");
break;
case UNAuthorizationStatusNotDetermined:
NSLog(#"Undetermined");
break;
default:
break;
}
//2. To learn the status of specific settings, query them directly
NSLog(#"Checking Badge settings");
if (settings.badgeSetting == UNAuthorizationStatusAuthorized)
NSLog(#"Yeah. We can badge this puppy!");
else
NSLog(#"Not authorized");
}];
}
use this code-
if ([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]) {
// yes
}else{
// no
}
You can use getNotificationSettingsWithCompletionHandler whenever your app enters in forground.
-(void) IsNotifictaionEnabled :(void (^)(BOOL isActive))handler {
[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
if (settings.alertSetting == UNNotificationSettingEnabled) {
handler(YES);
} else {
handler(NO);
}
}];
}
///////////
Following is the original answer, but currentUserNotificationSettings is deprecated now.
you can use currentUserNotificationSettings whenever your app enters in foreground.
UIUserNotificationSettings *grantedSettings = [[UIApplication sharedApplication] currentUserNotificationSettings];
if (grantedSettings.types == UIUserNotificationTypeNone) {
NSLog(#"No permiossion granted");
}
else if (grantedSettings.types & UIUserNotificationTypeSound & UIUserNotificationTypeAlert ){
NSLog(#"Sound and alert permissions ");
}
else if (grantedSettings.types & UIUserNotificationTypeAlert){
NSLog(#"Alert Permission Granted");
}
If you want to check if the status has changed from the previous one, You can keep the previous value of currentUserNotificationSettings to some variable and compare it with current value overtime in applicationWillEnterForeground method.
I have following code in view controller button action:
- (IBAction)requestPermissions:(id)sender
{
DDLogInfo(#"Start permission requesting.");
if ([[UIApplication sharedApplication] respondsToSelector:#selector(registerForRemoteNotifications)]){
[[UIApplication sharedApplication] registerForRemoteNotifications];
} else {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didBecomeActive:)
name:UIApplicationDidBecomeActiveNotification
object:nil];
[[UIApplication sharedApplication]
registerForRemoteNotificationTypes:UIRemoteNotificationTypeAlert|UIRemoteNotificationTypeBadge|UIRemoteNotificationTypeSound];
}
}
After registration is finished didBecomeActive notification is posted and I'm making a check what user have done:
-(void)didBecomeActive:(NSNotification *)notification
{
if([[UIApplication sharedApplication] enabledRemoteNotificationTypes] == UIRemoteNotificationTypeNone){
// if user decline
}
} else {
// if user accept
}
So my problem is: If user decline to receive push notifications and later decides to enable them (Settings->Notification center->My App), there is a surprise: 'Alert' ,'Badge' and 'Sound' are enabled in Settings, but app returns UIRemoteNotificationTypeNone.
Any ideas what is wrong?
Note: I know these methods are deprecated, so please don't tell me to use registerForRemoteNotifications: and UIUserNotificationSettings, it is already done.
Well, possibly it returns a proper value after app restart, could you check that? I've found situations where iOS automatically kills your app when changing permissions in Settings while your app is in the background, f.e. Photos feed access; I suppose it's just a buggy implementation in iOS in this case, this wouldn't surprise me. I think I've hitted this situation you describe as well, and after finding it's returning invalid/old values until restart, I gave up and make the flow count with required restart.
Anyway, I don't think there's anything you could do about it.
For iOS >= 8, only
In my AppDelegate, I register for user notifications as follows:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSLog(#"didFinishLaunchingWithOptions called");
// iOS >= 8.0
// register to receive user notifications
[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
...
}
Upon completion, I register for remote notification as follows:
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
NSLog(#"didRegisterUserNotificationSettings called");
//register to receive remote notifications
[application registerForRemoteNotifications];
}
And upon completion, I check to see whether the app is registered
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
NSLog(#"didRegisterForRemoteNotificationsWithDeviceToken called");
BOOL pushNotificationOnOrOff = [[UIApplication sharedApplication] isRegisteredForRemoteNotifications]; // always returns TRUE
}
But app always indicates that push notification is enabled, even when the user has explicitly set the app's remote notification capability off (via the notification permissions alert that appears after first time installation of the app, or via app settings.)
I have set the app's Background Modes / Remote Notifications to TRUE. I have been debugging on an actual device (tethered via USB cable) compiled with a development certificate.
Help, I've been battling this for hours.
This seems to be a bug, I also discovered the same behaviour on iPhone 6, iOS 8.1.2.
[[UIApplication sharedApplication] isRegisteredForRemoteNotifications] always returns TRUE even if user declined the push notification permission (via the alert view) or by manually disabling the notification via Settings.app > Notifications.
After some research I discovered that if you have Background App Refresh enabled for the app, then [[UIApplication sharedApplication] isRegisteredForRemoteNotifications] will always returns TRUE.
When Background App Refresh set to FALSE then [[UIApplication sharedApplication] isRegisteredForRemoteNotifications] returns the correct value.
As a workaround, you can evaluate the [[UIApplication sharedApplication] currentUserNotificationSettings].types to determine whether the push notification is allowed or not.
typedef NS_OPTIONS(NSUInteger, UIUserNotificationType) {
UIUserNotificationTypeNone = 0, // the application may not present any UI upon a notification being received
UIUserNotificationTypeBadge = 1 << 0, // the application may badge its icon upon a notification being received
UIUserNotificationTypeSound = 1 << 1, // the application may play a sound upon a notification being received
UIUserNotificationTypeAlert = 1 << 2, // the application may display an alert upon a notification being received
} NS_ENUM_AVAILABLE_IOS(8_0);
Hope this helps.
I'm building an app that asks the user for permission to post notifications when the user enables a switch. I'm using this code:
- (IBAction)mySwitchValueChanged:(id)sender {
if ([UIApplication instancesRespondToSelector:#selector(registerUserNotificationSettings:)]){
[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]]; // ask the user for permission
}
if ([[UIApplication sharedApplication] respondsToSelector:#selector(currentUserNotificationSettings)]) { // Check it's iOS 8 and above
UIUserNotificationSettings *grantedSettings = [[UIApplication sharedApplication] currentUserNotificationSettings];
if (grantedSettings.types != UIUserNotificationTypeNone)
{
// Accepted
} else
{
[self.mySwitch setOn:NO]; // Declined
}
}
}
The desired behaviour is as follows:
User slides switch
Alert asks for permission
Code waits for user to decide
Either does whatever I code where I've put // accepted, or disables the switch.
The current behaviour makes the code run through at once, and doesn't wait for the user to decide. How can I change this to get the desired behaviour?
Thanks!
Note: This is workaround idea
Once you call this method :- [UIApplication sharedApplication] registerUserNotificationSettings and user grants push notification for the app, then didRegisterForRemoteNotificationsWithDeviceToken in AppDelegate get fired,
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
//call a notification when user granted Push Notifiaction
[[NSNotificationCenter defaultCenter]
postNotificationName:#"PushNotificationSuccess"
object:self];
}
So what you have to do, you can call a NSNotification from the mentioned method, to update the UI accordingly.
- (void)updateUIOnNotification:(NSNotification *) notification
{
// Accepted
}
//Call the below method in your else part and remove the line
//[self.mySwitch setOn:NO];
-(void)Showalert{
UIAlertview*Temp=[[UIAlertview alloc]initWithTitle:#"Need Permission to Send Notifications" message:#"The App Wants The Permission to Work Properly!\nPermit The App in Notification settings"delegate:self cancelButtonTitle:#"Okay!" otherButtonTitles:nil];
[Temp show];
}
#pragma mark Alertview delegate method
- (void)alertViewCancel:(UIAlertView *)alertView{
//Remove the Below line From Else part of your code
[self.mySwitch setOn:NO];
}
I have an iPhone development application with APNS enabled for the appID. In my applicationDidBecomeActive event I check to see whether or not push notifications are enabled by doing the following:
- (void)applicationDidBecomeActive:(UIApplication *)application {
NSLog(#"applicationDidBecomeActive");
//Check if remote notifications are enabled for this app, if not proceed with performSetupAndWebService calls
UIRemoteNotificationType types = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
if(types == UIRemoteNotificationTypeNone)
{
//User has push notifications DISABLED for this application.
NSLog(#"PUSH NOTIFICATIONS DISABLED");
[self performSetupAndWebServiceCalls];
}
else
{
//User has push notifications ENABLED for this application.
NSLog(#"PUSH NOTIFICATIONS ENABLED");
NSLog(#"Registering for push notifications...");
[[UIApplication sharedApplication]
registerForRemoteNotificationTypes:(UIRemoteNotificationType)(UIRemoteNotificationTypeBadge|UIRemoteNotificationTypeSound|UIRemoteNotificationTypeAlert)];
}
}
It seems that no matter what I do, my code always tells me that push notifications are disabled. My app icon is in Notification Center in Settings and it is included. I have tried turning off specific types of notification and turning them back on but types == UIRemoteNotificationTypeNone is always true.
Am I missing something obvious? The bad thing is this seems to be affecting users of our production app as well, but not all of them. Very inconsistent and very frustrating.
Any advice?
When notifications are disabled and you try to register for them you get the following UIApplicationDelegate method called:
application:didFailToRegisterForRemoteNotificationsWithError:
- (BOOL) getpushNotificationstatus
{
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
{
return ([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]);
}
else
{
UIRemoteNotificationType types = [[UIApplicationy sharedApplication] enabledRemoteNotificationTypes]; return (types & UIRemoteNotificationTypeAlert);
}
}
It will work in iOS8 as well