Pregunta Método de devolución de llamada 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 llega yes Puedo continuar y ejecutar la aplicación en los métodos de delegado invocados a continuación:

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

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

Sin embargo, si el usuario llega no, ninguno de estos métodos se llama, 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.


32
2017-09-27 14:19


origen


Respuestas:


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.


29
2018-04-24 22:17



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.


6
2017-09-27 14:35



¿No podrías hacer lo siguiente?

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

Este método debe ser la devolución de llamada 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.


1
2018-01-04 21:47



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

1
2018-03-19 01:28



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

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.


0
2017-09-27 14:33



Aquí hay un SWIFT 2 ejemplo de código 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++
    }

0
2018-01-26 20:08



Puede detectar si el usuario ha cancelado el aviso de notificación en didRegisterUserNotificationSettings método que se activa después de llamar registerForRemoteNotificationTypesal marcar el notificationSettings.types.

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

Pero no olvides eso registerForRemoteNotificationTypes método ahora está en desuso!


0
2017-12-05 05:47



por Swift 3 y Swift 4.0  Usar 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étera etcétera.


0
2017-12-22 23:02



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

0
2017-07-13 07:04