用户拒绝推送通知提示时的回调方法?

时间:2013-09-27 14:19:37

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

我的问题是我想显示初始推送通知提示的加载屏幕“该应用想要向您发送推送通知。”

因此,如果用户点击yes,我可以继续使用随后调用的委托方法启动应用程序:

- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
{
  [self hideLoadingScreen];
}

- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error
{
  [self hideLoadingScreen];
}

但是,如果用户点击no,则不会调用这些方法,这是有道理的。我的问题是,是否有一个不同的委托方法,如果他拒绝会被解雇?

我的问题是如果选择no,加载屏幕永远不会消失。所以我需要知道用户何时完成选择。

11 个答案:

答案 0 :(得分:31)

在iOS 7中,当系统的推送通知提示出现时,应用程序变为非活动状态,并且UIApplicationWillResignActiveNotification将触发。类似地,当用户响应提示时(按“是”或“否”),应用程序再次变为活动状态,并且UIApplicationDidBecomeActiveNotification将触发。

因此,您可以收听此通知,然后隐藏加载屏幕。

注意:显示提示时,“主页”按钮,“通知中心”和“控制中心”将被禁用,因此无法触发误报UIApplicationDidBecomeActiveNotification。但是,如果用户按下“锁定”按钮,则会触发UIApplicationDidBecomeActiveNotification。

答案 1 :(得分:6)

您始终可以从以下位置获取当前允许的通知类型:

UIRemoteNotificationType notificationTypes = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];

请注意,用户还可以在手机设置中停用通知。

如果你在didRegisterForRemoteNotificationsWithDeviceToken上检查,你应该看看你要求的类型是否已启用。

答案 2 :(得分:2)

你不能做以下事情:

- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
    BOOL pushEnabled = notificationSettings.types & UIUserNotificationTypeAlert;
}

此方法应该是对该推送通知提示的回调,并且从那里,您可以检查位掩码以查看是否启用了推送通知。

答案 3 :(得分:2)

以下是我在Swift 3中的表现。这里的关键是在内部跟踪应用程序的生命周期状态。显示推送提示时,应用程序将重新激活,但不会进入后台。这都在我的AppDelegate.swift中。

这是一个非常大的黑客,不建议在生产中使用。 Apple可以改变这些警报的呈现方式,这可能会随时中断。这是使用运行iOS 9和10的各种iPhone和iPad进行测试的。

/// An internal value used to track application lifecycle state
enum ApplicationLifecycleState {
    case willResignActive
    case didEnterBackground
    case willEnterForeground
    case didBecomeActive
    case unknown
}

/// This is used purely for tracking the application lifecycle for handling the system push notification alert
var internalLifecycleState: ApplicationLifecycleState = .unknown {
    didSet {
        // If we're not in the middle of asking for push permissions, none of the below applies, just bail out here
        if !isAskingForPushPermissions { return }

        // WARNING: Application lifecycle trickery ahead
        // The normal application lifecycle calls for backgrounding are as follows:
        // applicationWillResignActive -> applicationDidEnterBackground -> applicationWillEnterForeground -> applicationDidBecomeActive
        // However, when the system push notification alert is presented, the application resigns active, but does not enter the background:
        // applicationWillResignActive -> [user taps on alert] -> applicationDidBecomeActive
        // We can use this discrepancy to our advantage to detect if the user did not allow push permissions

        // If applicationDidBecomeActive
        // AND the previous state was applicationWillResignActive
        // AND the notification types bitmask is 0, we know that the user did not allow push permissions
        // User denied permissions
        if internalLifecycleState == .didBecomeActive
            && oldValue == .willResignActive
            && UIApplication.shared.currentUserNotificationSettings?.types.rawValue == 0 {
            // We're done
            firePushCompletionBlockAndCleanup(registered: false)
        } else {
            // The state below can only be entered on iOS 10 devices.
            // If the user backgrounds the app while the system alert is being shown,
            // when the app is foregrounded the alert will dismiss itself without user interaction.
            // This is the equivalent of the user denying push permissions.
            // On iOS versions below 10, the user cannot background the app while a system alert is being shown.

            if #available(iOS 10, *), internalLifecycleState == .didBecomeActive {
                firePushCompletionBlockAndCleanup(registered: false)
            }
        }
    }
}

/// Used internally to track if the system push notification alert is currently being presented
var isAskingForPushPermissions = false

typealias PushNotificationRegistrationCompletionBlock = ((_ registered: Bool) -> Void)

// ...

func applicationWillResignActive(_ application: UIApplication) {    
    internalLifecycleState = .willResignActive
}

func applicationDidEnterBackground(_ application: UIApplication) {
    internalLifecycleState = .didEnterBackground
}

func applicationWillEnterForeground(_ application: UIApplication) {
    internalLifecycleState = .willEnterForeground
}

func applicationDidBecomeActive(_ application: UIApplication) {
    internalLifecycleState = .didBecomeActive
}

// ...

func setupPushNotifications(_ application: UIApplication = UIApplication.shared, completion: @escaping PushNotificationRegistrationCompletionBlock) {
    isAskingForPushPermissions = true
    pushCompletionBlock = completion
    let settings = UIUserNotificationSettings(types: [.alert, .sound, .badge], categories: nil)
    application.registerUserNotificationSettings(settings)
    application.registerForRemoteNotifications()
}

fileprivate func firePushCompletionBlockAndCleanup(registered: Bool) {
    pushCompletionBlock?(registered)
    pushCompletionBlock = nil
    isAskingForPushPermissions = false
}

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {

    // application:didRegisterForRemoteNotificationsWithDeviceToken may be called more than once (once for each notification type)
    // By checking that the notification types bitmask is greater than 0, we can find the final time this is called (after the user actually tapped "allow")
    // If the user denied push permissions, this function is never called with a positive notification type bitmask value
    if UIApplication.shared.currentUserNotificationSettings?.types.rawValue ?? 0 > 0 {
        firePushCompletionBlockAndCleanup(registered: true)
    }
}

func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
    print("Failed to register for notifications with error: " + error.localizedDescription)
    firePushCompletionBlockAndCleanup(registered: false)
}

用法:

appDelegate.setupPushNotifications(completion: { [weak self] (registered) in
    // If registered is false, the user denied permissions
})

答案 4 :(得分:2)

由于UserNotifications框架和iOS 10,您可以轻松获得以下数据:

let center = UNUserNotificationCenter.current()

// Request permission to display alerts and play sounds.
center.requestAuthorization(options: [.alert, .sound]) 
{ (granted, error) in
  // Enable or disable features based on authorization.
}

答案 5 :(得分:1)

Swift 3 Swift 4.0 使用NotificationCenter和AppDelegate方法didRegister notificationSettings。 NotificationSettings显示用户是否选择了徽章,声音等,如果拒绝推送通知则会显示为空数组。当用户响应推送通知提示并且似乎是大多数开发人员使用时,它会被特别触发,因为它比检查didBecomeActive更具体。但苹果可能会改变这一点。谁知道?

不幸的是,NotificationCenter没有预设的通知名称,因此您必须设置和扩展(请参阅结束)或使用原始值(SO有更多内容)。

在AppDelegate中:

    func application(_ application: UIApplication, didRegister notificationSettings: UIUserNotificationSettings) {
      // if not registered users will have an empty set of settings
      let accepted: Bool = !notificationSettings.types.isEmpty
      NotificationCenter.default.post(name: Notification.Name(rawValue: "didRespondToPrompt"), object: self, userInfo: ["didAccept" : accepted])
}

然后观察您需要的位置,例如在视图控制器中:

class MyViewController: UIViewController {

//MARK: - Lifecycle
   override func viewDidLoad() {
      super.viewDidLoad()
      NotificationCenter.default.addObserver(self, selector: #selector(MyViewController.didRespondToPushPrompt(_:)), name: NSNotification.Name(rawValue: "didRespondToPrompt"), object: nil)

   }
    @objc func didRespondToPushPrompt(_ notification: Notification) {

       if let userInfo: [AnyHashable : Any] = notification.userInfo, let didAccept: Bool = userInfo[NSNotificationKeyNames.didAccept] as? Bool, !didAccept {
        //if user doesn't accept, do this...

       } else  {
       //all other situations code goes here
      }

   }
}

一些事情:首先,对于Swift 4.0,我在一个方法前使用“@objc”,但Swift 3没有必要。
另外,对于使用NotificationCenter,实际上我没有使用“rawValue”。相反,我做了一个像这样的扩展:

import Foundation

extension NSNotification.Name {
   static let DidRegisterForPushNotifications = NSNotification.Name("DidRegisterForPushNotifications")
}

我可以这样使用:

NotificationCenter.default.post(name: Notification.Name.DidRegisterForPushNotifications, object: self, userInfo: ["didAccept" : myBool]) 等等。

答案 6 :(得分:0)

我想你可以在你的AppDelegate中有一个BOOL变量来检查它,因为除了使用外部API之外似乎没办法。请参阅this

AppDelegate.m

// declare a BOOL 
BOOL allow = NO;

- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
{
allow = YES;
  [self hideLoadingScreen];
}

- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error
{
  allow = YES;
  [self hiedLoadingScreen];
}

现在我想你可以访问这个BOOL变量来区分何时不按下。

答案 7 :(得分:0)

这是一个 SWIFT 2 代码示例,对你们来说......这有点复杂,但我希望我的评论可以帮助你理解它。

定义变量

var appDidBecomeActiveCount = 0
var userDefaults:NSUserDefaults!

AppDelegate - didFinishLaunchingWithOptions

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

        userDefaults = NSUserDefaults.standardUserDefaults()
        if userDefaults.valueForKey("FirstLaunche") == nil {
            userDefaults.setBool(true, forKey: "FirstLaunche")
            userDefaults.synchronize()
        }

        // Register for notification
        //iOS 8+
        let settings:UIUserNotificationSettings = UIUserNotificationSettings(forTypes: [UIUserNotificationType.Alert , UIUserNotificationType.Badge ,UIUserNotificationType.Sound], categories: nil)
        UIApplication.sharedApplication().registerUserNotificationSettings(settings)
        UIApplication.sharedApplication().registerForRemoteNotifications()
}

AppDelegate - applicationDidBecomeActive

func applicationDidBecomeActive(application: UIApplication) {
            //Delay until alert get dismissed and notification type setted in app
            delay(0.5, closure: { () -> () in
                self.checkTheDilemma()
            })
}
//I love this short method <3_<3
func delay(delay:Double, closure:()->()) {
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))
        ),
        dispatch_get_main_queue(), closure)
}

检查操作

func checkTheDilemma (){
        //Checking if this user turned off push notifications or didn't allow it at all
        let notificationType = UIApplication.sharedApplication().currentUserNotificationSettings()?.types

        if userDefaults.valueForKey("FirstLaunche") as! Bool == true {
            //User now is asked for notification permission because it's app's first launche
            // if appDidBecomeActiveCount == 0 --> Pop up message will appeare
            // if appDidBecomeActiveCount == 1 --> Pop up message dismissed
            // if notificationType?.rawValue == 0 --> Notifications off
            // if notificationType?.rawValue > 0  --> Notifications on
            if notificationType?.rawValue == 0
                && appDidBecomeActiveCount == 1 { //If user disabled notifications from pop up alert
                    // ** User just tapped "Don't allow" btn :\
                    // Do what ever you are here for

                    //Now set FirstLaunche = false
                    userDefaults.setBool(false, forKey: "FirstLaunche")
                    userDefaults.synchronize()
            }
        } else {
            if notificationType?.rawValue == 0
                && appDidBecomeActiveCount == 0 { // This guy is not registered for push notification
                    // ** User disabled notifications in past (because this is not his first launch)
            }
        }
        appDidBecomeActiveCount++
    }

答案 8 :(得分:0)

您可以通过查看didRegisterUserNotificationSettings来检测用户是否已在registerForRemoteNotificationTypes方法中取消了通知提示,该方法在致电notificationSettings.types后触发。

如果您请求了许多设置,但notificationSettings.types == UIUserNotificationTypeNone表示该用户已取消提示。

但不要忘记现在已弃用registerForRemoteNotificationTypes方法!

答案 9 :(得分:0)

2019年5月2日

这是检查通知是否在您的应用程序中的任何时间得到实现的实现,只需调用此函数即可。

    private func checkNotificationsAuthorizationStatus() {
    let userNotificationCenter = UNUserNotificationCenter.current()
    userNotificationCenter.getNotificationSettings { (notificationSettings) in
        switch notificationSettings.authorizationStatus {
        case .authorized:
            print("The app is authorized to schedule or receive notifications.")
        case .denied:
            print("The app isn't authorized to schedule or receive notifications.")

        case .notDetermined:
            print("The user hasn't yet made a choice about whether the app is allowed to schedule notifications.")
        case .provisional:
            print("The application is provisionally authorized to post noninterruptive user notifications.")
        }
    }

}

答案 10 :(得分:0)

从iOS 13开始使用C#Xamarin的好方法

我将其放在计时器上提示并检查授权状态的页面上

可能可以获取实际的回调,但是这种方式对我有用

using System.Timers;

Timer notificationsPermissionTimer = new Timer();

public override void ViewDidLoad()
{
  SetupNotificationsPermissionTimer();
  base.ViewDidLoad();
}

public override void ViewWillDisappear(bool animated)
{
  this.notificationsPermissionTimer.Elapsed -= NotificationsPermissionTimer_Elapsed;
  base.ViewWillDisappear(animated);
}
private void SetUpNotificationsPermissionTimer()
{
  this.notificationsPermissionTimer = new Timer();
  this.notificationsPermissionTimer.Interval = 500;
  this.notificationsPermissionTimer.Start();
  this.notificationsPermissionTimer.Elapsed += NotificationsPermissionTimer_Elapsed;
}

private void NotificationsPermissionTimer_Elapsed(object sender, ElapsedEventArgs e)
{
  Task.Run(CheckNotificationsAuthorizationStatus);
}

private async Task CheckNotificationsAuthorizationStatus()
{
  var userNotificationCenter = await UNUserNotificationCenter.Current.GetNotificationSettingsAsync();
  switch(userNotificationCenter.AuthorizationStatus)
  {
    case UNAuthorizationStatus.Authorized:
      // Do Something
      break;
    case UNAuthorizationStatus.Denied:
      // Do Something
      break;
    case UNAuthorizationStatus.NotDetermined:
      // Do Nothing
      break;
    case UNAuthorizationStatus.Provisional:
      break;
  }
}