web-dev-qa-db-fra.com

Méthode de rappel si l'utilisateur refuse l'invite de notification push?

Mon problème est que je souhaite afficher un écran de chargement pour l'invite de notification Push initiale "L'application souhaite vous envoyer des notifications Push."

Ainsi, si l'utilisateur clique sur yes, je peux continuer et lancer l'application dans les méthodes de délégation alors invoquées:

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

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

Cependant, si l'utilisateur clique sur no, aucune de ces méthodes n'est appelée, ce qui est logique. Ma question est la suivante: existe-t-il une méthode de délégation différente qui est congédiée s’il refuse?

Mon problème est que si no est sélectionné, les écrans de chargement ne disparaissent jamais. J'ai donc besoin de savoir quand l’utilisateur a fini la sélection.

33
最白目

Dans iOS 7, lorsque l'invite de notification Push du système apparaît, l'application devient inactive et UIApplicationWillResignActiveNotification se déclenche. De même, lorsque l'utilisateur répond à l'invite (en appuyant sur Oui ou sur Non), l'application redevient active et UIApplicationDidBecomeActiveNotification se déclenche.

Vous pouvez donc écouter cette notification, puis masquer votre écran de chargement. 

Remarque: tant que l'invite est affichée, le bouton Accueil, le Centre de notifications et le Centre de contrôle sont désactivés. Ils ne peuvent donc pas déclencher une UIApplicationDidBecomeActiveNotification faux positif. Cependant, si l'utilisateur appuie sur le bouton Verrouiller, cela déclenchera UIApplicationDidBecomeActiveNotification.

31
Jeff Mascia

Vous pouvez toujours obtenir les types de notification autorisés actuels à partir de:

UIRemoteNotificationType notificationTypes = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];

Gardez à l'esprit que l'utilisateur peut également désactiver la notification dans les paramètres du téléphone.

Si vous vérifiez cela sur didRegisterForRemoteNotificationsWithDeviceToken, vous devriez voir si les types que vous avez demandés sont activés.

6
Grzegorz Krukowski

Voici comment je l’ai fait dans Swift 3. L’important est de garder l’état interne du cycle de vie de l’application. Lorsque l'invite Push est présentée, l'application démissionne, mais n'entre pas en arrière-plan. Tout cela dans mon AppDelegate.Swift.

Ceci est un très gros bidouillage et n'est pas recommandé en production. Apple pourrait changer la manière dont ces alertes sont présentées et cela pourrait casser à tout moment. Ceci a été testé avec différents iPhones et iPads fonctionnant sous iOS 9 et 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)
}

Usage:

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

Pour Swift 3 et Swift 4.0 Utilisation de NotificationCenter et de la méthode AppDelegate didRegister notificationSettings. NotificationSettings indique si les utilisateurs ont opté pour les badges, les sons, etc. et constituera un tableau vide s'ils refusent les notifications Push. Il est déclenché spécifiquement lorsque les utilisateurs répondent à l'invite de notifications Push et semble être ce que la plupart des développeurs utilisent, car il est plus spécifique que de vérifier didBecomeActive. Mais Apple pourrait changer cela. Qui sait?

Malheureusement, NotificationCenter n’ayant pas de nom de notification prédéfini, vous devez soit configurer et étendre (voir fin), soit utiliser la valeur brute dans (le responsable de la sécurité dispose de plus d'informations à ce sujet).

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

Observez ensuite chaque fois que vous en avez besoin, par exemple dans un contrôleur de vue:

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
      }

   }
}

Quelques choses: Premièrement, pour Swift 4.0, j'utilise "@objc" devant une méthode, mais ce n'est pas nécessaire pour Swift 3.
En outre, pour utiliser NotificationCenter, en pratique, je n'ai pas utilisé "rawValue". Au lieu de cela, j'ai fait une extension comme ceci: 

import Foundation

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

Que je pourrais ensuite utiliser comme ceci:

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

1
davidrynn

Ne pourriez-vous pas simplement faire ce qui suit:

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

Cette méthode doit être le rappel de cette invite de notifications Push, et à partir de là, vous pouvez vérifier le masque de masque pour voir si les notifications Push ont été activées ou non.

1
NYC Tech Engineer

Certaines des réponses ici ne sont plus pertinentes, ou sont plus compliquées qu’elles ne le devraient, puisque le cadre UserNotifications et iOS 10 vous permettent d’obtenir facilement ces données de la manière suivante:

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.
}
1
Nermin Sehic

Je suppose que vous pouvez avoir une variable BOOL pour la vérifier dans votre AppDelegate, car il semble n'y avoir aucun autre moyen que d'utiliser des API externes. Voir 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];
}

Maintenant, j'imagine que vous pouvez accéder à cette variable BOOL pour différencier les cas où l'option Ne pas autoriser est activée ou non.

0
Puneet Sharma

Voici un exemple de code Swift 2 pour vous les gars ... C’est un peu compliqué, mais j’espère que mes commentaires vous aideront à comprendre.

Définir les 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)
}

Action de contrôle

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
Husam

Vous pouvez détecter si l'utilisateur a annulé l'invite de notification dans la méthode didRegisterUserNotificationSettings qui se déclenche après l'appel de registerForRemoteNotificationTypes en vérifiant le notificationSettings.types.

Si vous avez demandé un certain nombre de paramètres mais que notificationSettings.types == UIUserNotificationTypeNone signifie, cet utilisateur a annulé l'invite. 

Mais n'oubliez pas que la méthode registerForRemoteNotificationTypes est maintenant obsolète!

0
Finskii