isRegisteredForRemoteNotifications が正常に動かなかったときの対処

PUSH 通知が有効/無効をアプリ上で表示(UISwitch等)して有効にしようとした場合にアプリの設定画面に遷移して有効にしてもらう。 といったことを実装することがあるかもしれません。

実装は「[Swift] iOS でプッシュ通知の有効・無効を判定する」を見てもらえれば分かりやすいと思います。

それが意図した通りに動かないケースに遭遇しました。

isRegisteredForRemoteNotifications で有効/無効を確認するのですが、PUSH 通知を許可していないのに true になったり、再インストールした時にダイアログが出ている状態なのに true になったりなどです。

解決方法

registerForRemoteNotifications を呼び出しているしている場所を確認してください。

ドキュメント を見ると application:didRegisterForRemoteNotificationsWithDeviceToken:application:didFailToRegisterForRemoteNotificationsWithError: 以外からの呼び出しは推奨していないようです。

今回はこれが原因でした。 なので 上記の delegate の中から呼び出すようにすればなおりました。

呼び出した場合どうなるか

解決方法で書いたようにちゃんと delegate の中で registerForRemoteNotifications を実行していればこんなことにはならないのですが、何かの役に立つかもしれないので検証した結果を書いておきます。

iOS9.2.1 で下記のコードで検証しました。

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    ...
    
    let types: UIUserNotificationType = [ .Badge, .Sound, .Alert ]
    let settings = UIUserNotificationSettings(forTypes: types, categories: nil)
    application.registerUserNotificationSettings(settings)
    print("before \(application. isRegisteredForRemoteNotifications)")
    application.registerForRemoteNotifications()
    print("after \(application. isRegisteredForRemoteNotifications)")
    
    ...
}

検証した限り初回インストールと、再インストールの場合アンインストールした時の通知設定の状況に左右されます。

初回インストール

isRegisteredForRemoteNotifications が正常な値を返す

再インストール

前回のインストールの状況時の PUSH 通知の許可状況に依存します。

アンインストール時に通知を不許可

isRegisteredForRemoteNotifications が正常な値を返す

isRegisteredForRemoteNotifications
before false
after false

アンインストール時に通知を許可

この時が曲者で、初回起動時のみ挙動が変わります。

registerForRemoteNotifications を呼び出したら isRegisteredForRemoteNotificationstrue になります。 そしてダイアログに出ているのに関わらず application:didRegisterForRemoteNotificationsWithDeviceToken: が呼び出され device token が取得できてしまいます。。。

isRegisteredForRemoteNotifications
before false
after true

次回起動時からは通知ダイアログの選択に依存し正常に動作します。

これのタチの悪い所はアンインストール前の通知の有効/無効に依存することです。 状況が不安定になってしまってデバッグ時に困るので delegate の中で registerForRemoteNotifications を呼び出すようしましょう。 (ドキュメントにはそう書いてありますが。。。)

あまり関係ありませんが、いつの間にかアプリを入れなおすと通知のダイアログが毎回出るようになったんですね。 以前は端末の時計を進めて再起動したりしないといけなかったのでお手軽になりましたね。