web-dev-qa-db-ja.com

iOS 9:ユーザーがプッシュ通知リクエストに対して「許可しない」と言ったときを検出する方法

IOS 9では、ユーザーがプッシュ通知リクエストで「許可しない」をタップしたかどうかを知ることができるシステムレベルのコールバックがありますか?

ユーザーにプッシュ通知とアプリで持つ値について通知するカスタム画面をユーザーに表示します。

Custom Tripnary Prompt

それらには、yesまたはnoの2つの選択肢があります。 [はい]を選択した場合、プッシュ通知をオペレーティングシステムに要求すると、下の画像のようなポップアップが表示されます。

iOS Level system Prompt for Push notifications

ここで、ユーザーが[はい]をタップすると、didRegisterForRemoteNotificationsWithDeviceTokenという関数があり、このデバイスがプッシュ通知用に登録されたことを通知します。これを使用して、次の画面に進むことができます(サインアップ後に最初の画面に移動します)

ただし、ユーザーが[許可しない]をタップした場合、どのように検出しますか?それを知って、ユーザーを次の画面に移動します(サインアップ後に最初の画面に移動します) )。ユーザーが「許可しない」をタップした場合、関数didFailToRegisterForRemoteNotificationsWithErrorは呼び出されません。

この質問は、その質問に対して受け入れられた回答がiOS 7に固有のものであるため、重複していません。私の質問はiOS 9に固有のものです。

19
goelv

IOS 8では、通知登録プロセスが変更され、ユーザーはremote通知のみに許可を与える必要がなくなりました。

ユーザーから許可を得る必要なく、リモート通知を技術的に登録できるようになりました。許可が必要なのは、ユーザー通知設定(アラート、サウンド、バッジ)です。これらはローカル通知とリモート通知の両方に汎用的になり、他の回答は技術的に不正確になりました。

UIApplication-[UIApplication registerUserNotificationSettings:]メソッドを介して許可を要求し、ドキュメントに従って、-[UIApplicationDelegate application: didRegisterUserNotificationSettings:]デリゲートメソッドへのコールバックを取得します。

ヘッダーには、次のコメントがあります。

// This callback will be made upon calling -[UIApplication registerUserNotificationSettings:]. The settings the user has granted to the application will be passed in as the second argument.

これは、ユーザーが通知(ローカルとリモートの両方)のアクセス許可を付与しなかった場合、2番目のパラメーターに値が含まれないことを意味します。


-[UIApplication isRegisteredForRemoteNotifications]は、アプリケーションが実際にAppleのプッシュサーバーに登録され、デバイストークンを受信したかどうかを通知します。

戻り値
アプリがリモート通知用に登録され、デバイストークンを受信した場合はYES、登録が発生していない、失敗している、またはユーザーによって拒否されている場合はNO。

必要な情報がすべて揃っているので、UIApplicationのドキュメントを読む価値があります。

https://developer.Apple.com/library/ios/documentation/UIKit/Reference/UIApplication_Class/#//Apple_ref/occ/instm/UIApplication

27
liamnichols

私はこのまったく同じ問題をどうにか解決できたので、どうやってそれをやったかを共有したい(iOS 9.3現在)。

私の場合、単一のカスタムボタンを使用して、3つの可能な状態の通知を有効にします。デフォルト(ユーザーはまだ通知を有効にするように求められていないことを意味します)、完了(ユーザーは通知を受け取るように求められ、同意されています)および失敗しました(ユーザーは通知プロンプトを拒否しました)。このボタンは、デフォルトの状態でのみ有効になります。

今、私はここで単一のテクニックを使用しているのではなく、いくつかの(関連している)呼び出しの組み合わせを使用しています。

ロジックは次のとおりです。ユーザーが通知プロンプトを拒否した場合でも(ユーザーがアプリを削除して再インストールするまで一度だけ表示されます)、引き続きリモート通知に登録します。プロセスは通常どおり続行され、デバイスは登録されますが、ユーザーは新しい通知が投稿されても通知を受け取りません。次に、現在の通知設定と、ユーザーがすでにリモート通知に登録されているかどうかを確認して、プロンプトが表示されたかどうかを確認できます(したがって、ボタンはデフォルトのステータスになります)。

この方法は完璧ではありません。ユーザーが最初に通知を受け取ることに同意したが、後で設定から手動でオフにすることにした場合、ボタンはデフォルトの状態に設定されますが、アクティブ化すると、通知を再度有効にするように求められません。ただし、この種のUIは通常、オンボーディング/サインアッププロセス中に1回だけ表示されるため、ほとんどの場合、これは重要ではありません。

コード自体については(Swift 2.2):

func updateButtonStatus() {
    // as currentNotificationSettings() is set to return an optional, even though it always returns a valid value, we use a sane default (.None) as a fallback
    let notificationSettings: UIUserNotificationSettings = UIApplication.sharedApplication().currentUserNotificationSettings() ?? UIUserNotificationSettings(forTypes: [.None], categories: nil)
    if notificationSettings.types == .None {
        if UIApplication.sharedApplication().isRegisteredForRemoteNotifications() {
            // set button status to 'failed'
        } else {
            // set button status to 'default'
        }
    } else {
        // set button status to 'completed'
    }
}

View ControllerのviewWillAppear(animated)実装からこのメソッドを呼び出します。

この時点で、さらにいくつかのことを行う必要があります。最初に、ボタンがタッチされるたびに(デフォルト状態でのみ発生します)、通知を受け入れるか拒否するかをユーザーに確認する必要があり、UIにも反応させる必要があります適切に、ユーザーが選択したもの:

@IBAction func notificationsPermissionsButtonTouched(sender: AnyObject) {
    let settings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
    UIApplication.sharedApplication().registerUserNotificationSettings(settings)
}

そして、適切なUIApplicationDelegateメソッドを実装してイベントを処理する必要があります。これらのグローバルUIApplication通知はないため、独自の通知を送信します。

// AppDelegate.Swift

func application(application: UIApplication, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings) {
    application.registerForRemoteNotifications()
    if notificationSettings.types == .None {
        NSNotificationCenter.defaultCenter().postNotificationName("ApplicationDidFailToRegisterUserNotificationSettingsNotification", object: self)
    }
}

func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
    NSNotificationCenter.defaultCenter().postNotificationName("ApplicationDidRegisterForRemoteNotificationsNotification", object: self)
}

View Controllerに戻り、これらの通知を処理する必要があります。したがって、viewWillAppear(animated)およびviewWillDisappear(animated)実装では、次のことを行います。

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(PermissionsViewController.applicationDidRegisterForRemoteNotificationsNotification(_:)), name: "ApplicationDidRegisterForRemoteNotificationsNotification", object: nil)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(PermissionsViewController.applicationDidFailToRegisterUserNotificationSettingsNotification(_:)), name: "ApplicationDidFailToRegisterUserNotificationSettingsNotification", object: nil)
    updateButtonStatus()
}

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    NSNotificationCenter.defaultCenter().removeObserver(self, name: "ApplicationDidRegisterForRemoteNotificationsNotification", object: nil)
    NSNotificationCenter.defaultCenter().removeObserver(self, name: "ApplicationDidFailToRegisterUserNotificationSettingsNotification", object: nil)
}

そして、通知ハンドラー自体:

func applicationDidRegisterForRemoteNotificationsNotification(notification: NSNotification) {
    let notificationSettings: UIUserNotificationSettings = UIApplication.sharedApplication().currentUserNotificationSettings() ?? UIUserNotificationSettings(forTypes: [.None], categories: nil)
    if notificationSettings.types != .None {
        // set button status to 'completed'
    }
}

func applicationDidFailToRegisterUserNotificationSettingsNotification(notification: NSNotification) {
    // set button status to 'failed'
}

ボーナス

ユーザーが通知プロンプトを拒否し、それを再度有効にすることができる設定パネルにそれらを案内し、それに応じてUIを反応させるボタンが必要な場合はどうなりますか?まあ、あなたが尋ねてくれてうれしいです。

[設定]内の[アプリ]セクションにディープリンクする既知のメカニズムはほとんどありません(iOS 8から存在しますが、数時間前までそれについて学ぶ機会がありませんでした)。 settingsボタンタッチハンドラーでこれを行います。

@IBAction func settingsButtonTouched(sender: AnyObject) {
    if let settingsURL = NSURL(string: UIApplicationOpenSettingsURLString) {
        UIApplication.sharedApplication().openURL(settingsURL)
    }
}

ユーザーが行った変更を反映するようにUIを更新するため、viewWillAppear(animated)実装にUIApplicationDidBecomeActiveNotificationの通知リスナーを追加します(viewWillDisapper(animated)。そして最後に、対応する通知ハンドラーメソッド内から、既存のupdateButtonStatus()を呼び出すだけです。

21
boliva

アプリのデリゲートでこのメソッドを使用します

- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings

を使用して、ユーザーが通知許可を与えたかどうかを知ることができます

[[UIApplication sharedApplication] isRegisteredForRemoteNotifications]

または、受け取ったnotificationSettingsを使用します。

5
Eli Braginskiy

許可されていない場合、アプリケーション内のAPNSからのプッシュ通知を検出する方法はありません。

このコードを使用して、許可されているかどうかを確認し、アプリをナビゲートして有効にします。

UIRemoteNotificationType types = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
if (types == UIRemoteNotificationTypeNone)
{
    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"app-settings:"]];
}

お役に立てれば!

4

つかいます:

[[UIApplication sharedApplication] isRegisteredForRemoteNotifications];

およびNSUserDefaults。プッシュ通知のシステムダイアログが表示されたら、キー(HasSeenSystemPushNotification)をtrueに保存します。

次に、NSUDキーとisRegisteredForRemoteNotifications boolをチェックして、それが提示/受け入れられたかどうかを確認し、それに応じて作業を行うことができます。

1
Josh O'Connor

これを行うには、迅速で安価な方法があります。 iOS9このデリゲートメソッド

- (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken

と呼ばれるonceダイアログが表示されたとき、そしてsecondユーザーがタップした時間 "Ok"。ここにフラグを追加するだけです。

次に、カスタムの「)Pushを有効にする方法をユーザーに通知する」メッセージを表示する場合は、フラグと現在の通知設定を確認します(詳細は上記の回答の多く)。

- (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {

    self.pushDialogShown = YES;
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 

    // not a great place to put this logic. For demonstration purposes ONLY
    if (self.pushDialogueShown && ![self pushMessageEnabled]) {
        [self showPushReminderMessage];
    }
}
1
ajmccall

この方法で確認:-

[[UIApplication sharedApplication] isRegisteredForRemoteNotifications];

ただし、アプリがiOS 8未満のバージョンをサポートしている場合は、次のようなチェックを行う必要があります:-

UIRemoteNotificationType types = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
if (types == UIRemoteNotificationTypeNone) 
{ 
  //notification is not enabled by user

}
0
Vizllx