webentwicklung-frage-antwort-db.com.de

Rückrufmethode, wenn der Benutzer die Push-Benachrichtigungsaufforderung ablehnt?

Mein Problem ist, dass ich einen Ladebildschirm für die erste Push-Benachrichtigungsaufforderung anzeigen möchte. "Die App möchte Ihnen Push-Benachrichtigungen senden."

Wenn der Benutzer also yes drückt, kann ich fortfahren und die App in den dann aufgerufenen Delegatmethoden starten:

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

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

Wenn der Benutzer jedoch no drückt, wird keine dieser Methoden aufgerufen, was sinnvoll ist. Meine Frage ist, gibt es eine andere Delegat-Methode, die ausgelöst wird, wenn er ablehnt?

Mein Problem ist, wenn no ausgewählt ist, verschwinden die Ladebildschirme nie. Also muss ich irgendwie wissen, wann der Benutzer mit der Auswahl fertig ist.

33
最白目

Wenn in iOS 7 die Push-Benachrichtigungsaufforderung des Systems angezeigt wird, wird die App inaktiv und UIApplicationWillResignActiveNotification wird ausgelöst. Wenn der Benutzer auf die Aufforderung antwortet (entweder Ja oder Nein), wird die App wieder aktiv und UIApplicationDidBecomeActiveNotification wird ausgelöst.

Sie können also auf diese Benachrichtigung warten und dann den Ladebildschirm ausblenden. 

Hinweis: Während die Eingabeaufforderung angezeigt wird, sind die Startschaltflächen, das Benachrichtigungscenter und das Kontrollcenter deaktiviert, sodass keine falsch positiven UIApplicationDidBecomeActiveNotification ausgelöst werden kann. Wenn der Benutzer jedoch die Sperrtaste drückt, wird UIApplicationDidBecomeActiveNotification ausgelöst.

31
Jeff Mascia

Sie können immer aktuelle zulässige Benachrichtigungstypen erhalten von:

UIRemoteNotificationType notificationTypes = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];

Beachten Sie, dass der Benutzer die Benachrichtigung auch in den Telefoneinstellungen deaktivieren kann.

Wenn Sie dies unter didRegisterForRemoteNotificationsWithDeviceToken überprüfen, sollten Sie sehen, ob die von Ihnen gewünschten Typen aktiviert sind.

6

So habe ich es in Swift 3 gemacht. Der Schlüssel ist hier, den Lebenszyklusstatus der Anwendung intern zu verfolgen. Wenn die Push-Eingabeaufforderung angezeigt wird, tritt die Anwendung als aktiv zurück, tritt jedoch nicht in den Hintergrund ein. Dies ist alles in meiner AppDelegate.Swift.

Dies ist ein wirklich großer Hack und wird in der Produktion nicht empfohlen. Apple könnte die Art und Weise ändern, in der diese Benachrichtigungen angezeigt werden, und dies kann jederzeit unterbrochen werden. Dies wurde mit verschiedenen iPhones und iPads mit iOS 9 und 10 getestet.

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

Verwendungszweck:

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

Für Swift 3 und Swift 4.0 Verwenden von NotificationCenter und der AppDelegate-Methode didRegister notificationSettings. NotificationSettings zeigt an, ob sich die Benutzer für Badges, Sounds usw. entschieden haben, und ist ein leeres Array, wenn sie Push-Benachrichtigungen ablehnen. Es wird speziell ausgelöst, wenn Benutzer auf die Eingabeaufforderung für Push-Benachrichtigungen antworten, und scheint das zu sein, was die meisten Entwickler verwenden, da dies spezifischer ist als die Überprüfung von didBecomeActive. Aber Apple könnte das ändern. Wer weiß?

Leider verfügt NotificationCenter nicht über einen voreingestellten Benachrichtigungsnamen. Sie müssen also entweder eine Einrichtung und eine Erweiterung einrichten (siehe Ende) oder den Rohwert in verwenden (dazu hat SO mehr).

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

Dann beobachten Sie, wo immer Sie möchten, zum Beispiel in einem View-Controller:

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
      }

   }
}

Ein paar Dinge: Erstens verwende ich für Swift 4.0 "@objc" vor einer Methode, aber für Swift 3 ist dies nicht erforderlich.
Für die Verwendung von NotificationCenter habe ich in der Praxis "rawValue" nicht verwendet. Stattdessen habe ich eine Erweiterung so gemacht: 

import Foundation

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

Was könnte ich dann so gebrauchen:

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

1
davidrynn

Könnten Sie nicht einfach Folgendes tun:

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

Diese Methode sollte der Rückruf für die Eingabeaufforderung für Push-Benachrichtigungen sein. Von dort aus können Sie die Bitmaske überprüfen, um zu sehen, ob Push-Benachrichtigungen aktiviert waren oder nicht.

1

Einige der Antworten sind nicht mehr relevant oder komplizierter als sie sein sollten, da das UserNotifications Framework und iOS 10 diese Daten leicht erhalten können:

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

Ich schätze, Sie können eine BOOL-Variable haben, um sie in Ihrer AppDelegate zu überprüfen, da es scheinbar keine andere Möglichkeit gibt als die Verwendung externer APIs. Siehe 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];
}

Nun, ich vermute, Sie können auf diese BOOL-Variable zugreifen, um zu unterscheiden, ob Sie nicht zulassen drücken oder nicht.

0
Puneet Sharma

Hier ist ein Swift 2 Codebeispiel für Sie ... Es ist etwas kompliziert, aber ich hoffe, meine Kommentare werden Ihnen helfen, es zu verstehen.

Variablen definieren

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

Aktion prüfen

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

Sie können feststellen, ob der Benutzer die Benachrichtigungsaufforderung in der didRegisterUserNotificationSettings-Methode, die nach dem Aufruf von registerForRemoteNotificationTypes ausgelöst wird, abgebrochen hat, indem Sie den notificationSettings.types überprüfen.

Wenn Sie eine Reihe von Einstellungen angefordert haben, aber notificationSettings.types == UIUserNotificationTypeNone bedeutet, hat der Benutzer die Eingabeaufforderung abgebrochen. 

Vergessen Sie nicht, dass die registerForRemoteNotificationTypes-Methode jetzt veraltet ist!

0
Finskii

2. Mai 2019

Dies ist die Implementierung, um zu überprüfen, ob Benachrichtigungen jederzeit in Ihrer App autorisiert sind. Rufen Sie einfach diese Funktion auf.

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

}
0
Mussa Charles