Método de callback si el usuario rechaza el aviso de notificación de inserción.

Mi problema es que quiero mostrar una pantalla de carga para el mensaje de notificación de inserción inicial “La aplicación quiere enviarle notificaciones automáticas”.

Entonces, si el usuario responde que yes , puedo continuar y comenzar la aplicación en los métodos de delegado invocados:

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

Sin embargo, si el usuario responde que no , ninguno de estos métodos recibe una llamada, lo que tiene sentido. Mi pregunta es, ¿hay un método delegado diferente que se dispare si se niega?

Mi problema es si no se selecciona, las pantallas de carga nunca desaparecen. Entonces, de alguna manera, necesito saber cuándo el usuario ha terminado con la selección.

En iOS 7, cuando aparece el mensaje de notificación de inserción del sistema, la aplicación se vuelve inactiva y se dispara UIApplicationWillResignActiveNotification. De manera similar, cuando el usuario responde al aviso (presionando Sí o No), la aplicación se vuelve a activar y UIApplicationDidBecomeActiveNotification se activa.

Entonces puede escuchar esta notificación y luego esconder su pantalla de carga.

Nota: Mientras se muestra el mensaje, el botón Inicio, el Centro de notificaciones y el Centro de control están deshabilitados, por lo que no pueden activar una UIApplicationDidBecomeActiveNotification falsamente positiva. Sin embargo, si el usuario presiona el botón de Bloqueo, disparará UIApplicationDidBecomeActiveNotification.

Siempre puede obtener los tipos de notificación permitidos actuales de:

 UIRemoteNotificationType notificationTypes = [[UIApplication sharedApplication] enabledRemoteNotificationTypes]; 

Tenga en cuenta que el usuario también puede deshabilitar la notificación en la configuración del teléfono.

Si lo comprueba en didRegisterForRemoteNotificationsWithDeviceToken, debería ver si los tipos solicitados están habilitados.

Así es como lo hice en Swift 3. La clave aquí es realizar un seguimiento del estado del ciclo de vida de la aplicación internamente. Cuando se presenta el mensaje emergente, la aplicación se suspende activa, pero no ingresa al fondo. Esto es todo en mi AppDelegate.swift.

Este es un gran truco y no se recomienda en producción. Apple podría cambiar la forma en que se presentan estas alertas y esto podría romperse en cualquier momento. Esto se probó usando varios iPhones y iPads con iOS 9 y 10.

 /// 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) } 

Uso:

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

¿No podrías hacer lo siguiente?

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

Este método debe ser la callback al aviso de notificaciones push, y desde allí, puede verificar la máscara de bits para ver si las notificaciones push se habilitaron o no.

Supongo que puede tener una variable BOOL para verificarla en su AppDelegate porque parece que no hay otra manera que usar API externas. Mira esto

 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]; } 

Ahora supongo que puede acceder a esta variable BOOL para diferenciar cuando no se presiona No permitir o no.

Aquí hay un ejemplo del código SWIFT 2 para ustedes … Es un poco complicado, pero espero que mis comentarios lo ayuden a entenderlo.

Definir variables

 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) } 

Controle la acción

 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++ } 

Puede detectar si el usuario ha cancelado el aviso de notificación en el método didRegisterUserNotificationSettings que se didRegisterUserNotificationSettings después de llamar a registerForRemoteNotificationTypes marcando los didRegisterUserNotificationSettings notificationSettings.types .

Si ha solicitado una serie de configuraciones, pero notificationSettings.types == UIUserNotificationTypeNone significa que ese usuario ha cancelado el aviso.

¡Pero no olvide que el método registerForRemoteNotificationTypes ahora está en desuso!

Para Swift 3 y Swift 4.0, utilizando NotificationCenter y el método AppDelegate didRegister notificationSettings . NotificationSettings muestra si los usuarios optaron por insignias, sonidos, etc. y será una matriz vacía si rechazan las notificaciones push. Se activa específicamente cuando los usuarios responden al aviso de notificaciones automáticas y parece ser lo que la mayoría de los desarrolladores usan, ya que es más específico que verificar la función de respuesta no deseada. Pero Apple podría cambiar esto. ¿Quién sabe?

Desafortunadamente, NotificationCenter no tiene un nombre de notificación preestablecido, por lo que tiene que configurar y extender (ver final) o usar el valor en bruto (SO tiene más sobre esto).

En 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]) } 

Luego, observe donde lo necesite, por ejemplo, en un controlador de vista:

 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 } } } 

Un par de cosas: Primero, para Swift 4.0, estoy usando “@objc” frente a un método, pero no es necesario para Swift 3.
Además, para usar NotificationCenter, en la práctica no usé “rawValue”. En cambio, hice una extensión como esta:

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

Que podría usar así:

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

Algunas de las respuestas aquí ya no son relevantes, o son más complicadas de lo que deberían ser, ya que UserNotifications framework y iOS 10 pueden obtener fácilmente estos datos así:

 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. }