iOS 9:如何检测用户何时说“不允许'推送通知请求?

时间:2016-02-01 08:56:05

标签: ios objective-c apple-push-notifications

在iOS 9中,是否有我可以阅读的系统级回调,它告诉我用户是否已经点击了“不允许”#39;推送通知请求?

我使用自定义屏幕提示用户,告知他们推送通知及其在我的应用中的价值。

Custom Tripnary Prompt

他们有两个选择,是或否。如果他们选择是,我会向操作系统请求推送通知,他们会看到如下图所示的弹出窗口。

iOS Level system prompt for push notifications

现在,如果用户点击YES,那么有一个名为didRegisterForRemoteNotificationsWithDeviceToken的函数告诉我此设备已注册推送通知。我可以使用它来进入下一个屏幕(并在注册后将它们带入第一个屏幕)

但是,我如何检测用户是否点击了DON' T允许?我需要知道这样我才能相应地将用户移动到下一个屏幕(并将其带入注册后的第一个屏幕)。如果用户点击“不允许”,则不会调用函数didFailToRegisterForRemoteNotificationsWithError

这个问题并不重复,因为该问题的答案是针对iOS 7的,因为我的问题是针对iOS 9的。

7 个答案:

答案 0 :(得分:26)

从iOS 8开始,通知注册流程发生变化,从必须授予权限的用户转移到远程通知。

您现在可以在技术上注册远程通知,而无需获得用户的许可。您需要的权限是用户通知设置(警报,声音和徽章)。现在这些通知对于本地和远程通知都是通用的,使得其他答案在技术上不正确。

您通过-[UIApplication registerUserNotificationSettings:]上的UIApplication方法请求权限,并且根据文档,您会收到-[UIApplicationDelegate application: didRegisterUserNotificationSettings:]委托方法的回调。

在标题中,有一条评论说明如下:

// This callback will be made upon calling -[UIApplication registerUserNotificationSettings:]. The settings the user has granted to the application will be passed in as the second argument.

这意味着如果用户未授予通知权限(本地和远程),则第二个参数将不包含任何值。

-[UIApplication isRegisteredForRemoteNotifications]只会告诉您应用程序是否已实际注册Apple的推送服务器并且已收到设备令牌:

  

返回值
  如果应用程序已注册远程通知并收到其设备令牌,则为YES;如果未发生注册,失败或已被用户拒绝,则为“是”。

值得阅读UIApplication文档,因为它包含您需要的所有信息。

https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplication_Class/#//apple_ref/occ/instm/UIApplication

答案 1 :(得分:20)

我刚刚设法解决了同样的问题,并且非常乐意分享我的做法(截至iOS 9.3)。

在我的情况下,我使用单个自定义按钮启用具有三种可能状态的通知:默认(意味着用户尚未提示启用通知),已完成(用户已被提示并同意获取通知)并失败(用户拒绝了通知提示)。该按钮仅在默认状态下启用。

现在,我不是在使用单一技术,而是使用一些(尽管是相关的)调用。

逻辑如下:即使用户拒绝通知提示(在用户删除并重新安装应用程序之前只显示一次),我们仍会注册远程通知。该过程将照常继续,设备将被注册,但用户在发布新通知时不会收到任何通知。然后,我们可以利用了解当前通知设置以及用户是否已注册远程通知以了解是否曾提示他们(因此该按钮获得默认状态)。

这种方法并不完美。如果用户最初同意收到通知,但稍后决定手动将其从“设置”中关闭,则该按钮将设置为默认状态,但在激活时,不会提示用户再次启用通知。但在大多数情况下,这并不重要,因为这种用户界面通常只在入职/注册过程中显示一次。

至于代码本身(Swift 2.2):

func updateButtonStatus() {
    // as currentNotificationSettings() is set to return an optional, even though it always returns a valid value, we use a sane default (.None) as a fallback
    let notificationSettings: UIUserNotificationSettings = UIApplication.sharedApplication().currentUserNotificationSettings() ?? UIUserNotificationSettings(forTypes: [.None], categories: nil)
    if notificationSettings.types == .None {
        if UIApplication.sharedApplication().isRegisteredForRemoteNotifications() {
            // set button status to 'failed'
        } else {
            // set button status to 'default'
        }
    } else {
        // set button status to 'completed'
    }
}

我们从视图控制器的viewWillAppear(animated)实现中调用此方法。

此时还需要做一些其他事情:首先,每当触摸按钮时(仅在处于默认状态时才会发生),我们必须提示用户接受或拒绝通知,我们也希望我们无论用户选择什么,UI都能正确反应:

@IBAction func notificationsPermissionsButtonTouched(sender: AnyObject) {
    let settings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
    UIApplication.sharedApplication().registerUserNotificationSettings(settings)
}

然后,我们需要实现适当的UIApplicationDelegate方法来处理事件。由于没有针对这些的全局UIApplication通知,我们会发送自己的通知:

// AppDelegate.swift

func application(application: UIApplication, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings) {
    application.registerForRemoteNotifications()
    if notificationSettings.types == .None {
        NSNotificationCenter.defaultCenter().postNotificationName("ApplicationDidFailToRegisterUserNotificationSettingsNotification", object: self)
    }
}

func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
    NSNotificationCenter.defaultCenter().postNotificationName("ApplicationDidRegisterForRemoteNotificationsNotification", object: self)
}

现在回到我们的视图控制器,我们需要处理这些通知。因此,在我们的viewWillAppear(animated)viewWillDisappear(animated)实现中,我们执行:

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(PermissionsViewController.applicationDidRegisterForRemoteNotificationsNotification(_:)), name: "ApplicationDidRegisterForRemoteNotificationsNotification", object: nil)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(PermissionsViewController.applicationDidFailToRegisterUserNotificationSettingsNotification(_:)), name: "ApplicationDidFailToRegisterUserNotificationSettingsNotification", object: nil)
    updateButtonStatus()
}

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    NSNotificationCenter.defaultCenter().removeObserver(self, name: "ApplicationDidRegisterForRemoteNotificationsNotification", object: nil)
    NSNotificationCenter.defaultCenter().removeObserver(self, name: "ApplicationDidFailToRegisterUserNotificationSettingsNotification", object: nil)
}

通知处理程序本身:

func applicationDidRegisterForRemoteNotificationsNotification(notification: NSNotification) {
    let notificationSettings: UIUserNotificationSettings = UIApplication.sharedApplication().currentUserNotificationSettings() ?? UIUserNotificationSettings(forTypes: [.None], categories: nil)
    if notificationSettings.types != .None {
        // set button status to 'completed'
    }
}

func applicationDidFailToRegisterUserNotificationSettingsNotification(notification: NSNotification) {
    // set button status to 'failed'
}

加成

如果用户拒绝了通知提示并且我们想要一个按钮来引导他们进入“设置”面板,他们可以重新启用它,并使我们的UI做出相应的反应,该怎么办?嗯,我很高兴你问。

有很少的已知机制可以深入链接到“设置”中的“应用”部分(自iOS 8以来它一直存在,但我几乎没有机会了解它,直到几个小时前)。在我们的设置按钮触摸处理程序中,我们执行此操作:

@IBAction func settingsButtonTouched(sender: AnyObject) {
    if let settingsURL = NSURL(string: UIApplicationOpenSettingsURLString) {
        UIApplication.sharedApplication().openURL(settingsURL)
    }
}

由于我们要更新用户界面以反映用户可能做出的任何更改,因此我们会在UIApplicationDidBecomeActiveNotification实施中为viewWillAppear(animated)添加通知监听器(不要忘记删除监听器)来自viewWillDisapper(animated)。最后,在相应的通知处理程序方法中,我们只调用现有的updateButtonStatus()

答案 2 :(得分:5)

在您的app委托中使用此方法

- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings

然后您可以使用

知道用户是否提供了通知权限

[[UIApplication sharedApplication] isRegisteredForRemoteNotifications]

或使用您收到的notificationSettings

答案 3 :(得分:4)

如果不允许APNS在应用程序中检测到推送通知,则无法进行检测。

使用此代码检查是否允许并导航应用程序以启用它:

UIRemoteNotificationType types = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
if (types == UIRemoteNotificationTypeNone)
{
    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"app-settings:"]];
}

希望这有帮助!

答案 4 :(得分:1)

有一种快速而廉价的方法可以做到这一点。 iOS9这个委托方法

- (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
显示对话框时,

称为一次,然后当用户点击“确定”时,第二次时间。只需在这里添加一个标志。

然后,只要您希望显示自定义“提醒用户如何启用推送”消息,只需检查标记和您当前的通知设置(详见上述许多答案)。< / p>

- (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {

    self.pushDialogShown = YES;
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 

    // not a great place to put this logic. For demonstration purposes ONLY
    if (self.pushDialogueShown && ![self pushMessageEnabled]) {
        [self showPushReminderMessage];
    }
}

答案 5 :(得分:1)

Use:

[[UIApplication sharedApplication] isRegisteredForRemoteNotifications];

and NSUserDefaults. Store a key (e.g HasSeenSystemPushNotification) to true when the system dialog for push notifications is presented.

Then you can check the NSUD key and the isRegisteredForRemoteNotifications bool to see whether it has been presented/accepted and do your work accordingly.

答案 6 :(得分:0)

通过此方法检查: -

[[UIApplication sharedApplication] isRegisteredForRemoteNotifications];

但如果您的应用支持的iOS 8版本不足,那么您必须按以下方式进行检查:

UIRemoteNotificationType types = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
if (types == UIRemoteNotificationTypeNone) 
{ 
  //notification is not enabled by user

}