IOS7 и iOS8: как определить, когда пользователь указал "Нет" на запрос для push-уведомлений

Я создаю приложение, которое предназначено для iOS7 и iOS8. Я прошу у пользователя разрешения на отправку push-уведомлений. Но по какой-то причине ни iOS7, ни iOS8 никогда не назовет моего обработчика application:didFailToRegisterForRemoteNotificationsWithError, если я нажму "No", когда вас попросят разрешить push.

Мой вопрос:, как я узнаю, как на iOS7, так и на iOS8, когда пользователь отклонил запрос на push-уведомления - и как узнать, отклонили ли они запрос?

Я посмотрел на кучу ответов StackOverflow и реализовал то, что они предлагают, но он не работает как документированный:

  • iOS7: если пользователь одобряет запрос, система вызывает мой обработчик application:didRegisterForRemoteNotificationsWithDeviceToken:. Поэтому я могу сказать, что они одобрили это. Но если пользователь отказывает в запросе, тогда я не получаю обратный вызов application:didFailToRegisterForRemoteNotificationsWithError. Это похоже на ошибку, и я не могу сказать, что пользователь отказал в запросе.
  • iOS8: если пользователь одобряет или отклоняет запрос, система вызывает мой обработчик application:didRegisterUserNotificationSettings. Я могу посмотреть параметр notificationSettings, чтобы убедиться, что пользователь одобрил или отклонил мой запрос, так что это удобно. Однако, если я впоследствии вызову isRegisteredForRemoteNotifications() (например, когда приложение станет активным позже), он всегда возвращает true, даже если пользователь отказал в запросе. Поэтому я получаю ложный результат. Это похоже на ошибку, и я вижу, что другие тоже это заметили. Обновление: если я впоследствии позвоню let settings = UIApplication.sharedApplication().currentUserNotificationSettings(), я могу проверить settings.types чтобы узнать, разрешены ли предупреждения. Итак, для iOS8, похоже, я все настроен.

Я использую NSUserDefaults boolean для отслеживания того, спросил ли я пользователя о предоставлении разрешения.

Я использую аппаратные устройства для тестирования (iPhone 4S с iOS7 и iPhone 5 с iOS8), а не симулятор.

Я сброс моего устройства после каждого теста, чтобы он снова показывал запрос.

Здесь, как я регистрирую для push-уведомлений. Ветвь if берется на iOS8, а ветвь else берется на iOS7:

    let application = UIApplication.sharedApplication()

    if (application.respondsToSelector("registerUserNotificationSettings:")) {
        let settings = UIUserNotificationSettings(forTypes: .Badge | .Alert | .Sound,
            categories: nil )
        application.registerUserNotificationSettings(settings)
    } else {
        application.registerForRemoteNotificationTypes(.Badge | .Alert | .Sound)
    }

(В iOS8, когда вызывается application:didRegisterUserNotificationSettings:, я вызываю application.registerForRemoteNotifications()).

Ответы

Ответ 1

Ваш метод didFailToRegisterForRemoteNotificationsWithError не вызывается, потому что регистрация не сработала - его даже не пытались, потому что пользователь отклонил запрос.

На iOS7 вы можете сделать несколько вещей:

  • Предположим, что удаленные уведомления недоступны до тех пор, пока didRegisterForRemoteNotificationsWithDeviceToken не будет вызван
  • Проверьте значение enabledRemoteNotificationTypes объекта UIApplication

Ответ 2

Я пытался найти способ обойти эту же проблему.

Когда отображается разрешение push-уведомления UIAlert, оно отображается за пределами моего приложения. После того, как пользователь выбрал "Не разрешать" или "ОК", мое приложение снова становится активным.

У меня есть контроллер представлений, который я представляю, прежде чем запрашивать у пользователя разрешения Push. В этом представлении контроллера я слушаю UIApplicationDidBecomeActiveNotification, а затем отключаю свой контроллер представления.

Это работает очень хорошо, тогда как все другие решения, которые я видел, не работали для меня вообще.

Ответ 3

Предполагая, что вы компилируете iOS8 или новее, вы можете использовать следующий метод делегата:

func application(application: UIApplication, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings) {
    print("Notifications status: \(notificationSettings)")
}

то на вашем didFinishLaunching (или где подходит для ваших нужд) вы должны вызывать:

let app = UIApplication.sharedApplication()
let settings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
app.registerUserNotificationSettings(settings)
app.registerForRemoteNotifications()

На этом этапе вашим пользователям будет предложено типичное сообщение "Разрешить/не разрешать". Независимо от вашего выбора пользователя вы получите вызов вышеописанного метода, который позволяет получить мелкую конфигурацию вашего приложения. Результатом будет что-то вроде:

Notification Status: <UIUserNotificationSettings: 0x7fa261701da0; types: (none);>

Notification Status: <UIUserNotificationSettings: 0x7ff032e21dd0; types: (UIUserNotificationTypeAlert UIUserNotificationTypeBadge UIUserNotificationTypeSound);>

В первом случае вы заметите, что пользователь отклонил уведомления. Второй вариант - вариант разрешить.