【iOS10】UNNotificationのstateと通知の削除

Sunday, June 19, 2016

iOS10の UserNotifications Framework で、UNNotificationの状態と、通知の削除についてさっとまとめてみました。
若干言い回しが曖昧かもしれませんが、ご了承ください。

実行環境

  • Xcode8 beta (Swift 3.0 preview-1)

情報としては、Apple Developerで公開されている情報を基にしています。
また、まだBeta段階ですので、今後変わる可能性はあります。スクリーンショットは掲載しておりません。

通知の状態

通知には、PendingDeliveredの2つの状態があります。

  • Pending : 通知がまだ発火していない状態
  • Delivered : 通知が発火したが、まだユーザーが見ていない(タップしたりしてdismissしていない)状態

UNUserNotificationCenterに、UNNotificationRequestを追加することで、その通知はまずPendingになります。
その後、triggerで指定した条件を満たし、発火することで、Deliveredに変わります。
そして、その通知を開いたりすることで、この状態からはずれ、通知が完了します。

それぞれの状態にある通知を取得する

PendingまたはDeliveredになっている通知の一覧を取得することができます。
それぞれ、UNUserNotificatoinCenterの、

public func getPendingNotificationRequests(completionHandler: ([UNNotificationRequest]) -> Swift.Void)

public func getDeliveredNotifications(completionHandler: ([UNNotification]) -> Swift.Void)

を使います。

UNUserNotificationCenter.current().getPendingNotificationRequests {
    print("Pending requests :", $0)
}
UNUserNotificationCenter.current().getDeliveredNotifications {
    print("Delivered notifications :", $0)
}

注意したいのが、Pendingの場合は、UNNotificationRequest型の配列で、Deliveredの場合は、UNNotification型の配列になっています。

ちなみに、UNNotificationRequestは生成時のidentifierが取得でき、UNNotificationは、その通知の基となっているrequestが取得できるので、

UNUserNotificationCenter.current().getPendingNotificationRequests {
    print("Pending request identifiers :", $0.map { $0.identifier })
}
UNUserNotificationCenter.current().getDeliveredNotifications {
    print("Delivered notification identifiers :", $0.map { $0.request.identifier })
}

こんな感じで通知リクエストのidentifierを取得することができます。

それぞれの状態になっている特定の通知を削除する

PendingDeliveredになっている特定の通知を削除したい場合は、

public func removePendingNotificationRequests(withIdentifiers identifiers: [String])

public func removeDeliveredNotifications(withIdentifiers identifiers: [String])

を使って、UNNotificationRequestのidentifierを渡して削除します。

let requestIdentifier = "SampleNotificationRequest"
let request = UNNotificationRequest(identifier: requestIdentifier,
                                    content: content,
                                    trigger: trigger)

// ...
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: ["SampleNotificationRequest"])

Pendingになっているものを削除すれば、通知は発火せずに終了し、
Deliveredになっているものを削除すれば、通知されたものが通知センターから消えます。

また、identifierは配列で渡すので、複数まとめて削除することもできます。

例えば、Pendingのうち、Hoge_をprefixにもつ通知だけ削除したい場合は、今までのを組み合わせて

UNUserNotificationCenter.current().getPendingNotificationRequests { requests in
    let identifiers = requests.map({ $0.identifier }).filter({ $0.hasPrefix("Hoge_") })
    UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: identifiers)
}

こんな感じで削除ができます。
また、それぞれの状態にない通知のidentifierを渡しても、エラーになったりはしません。

それぞれの状態の全ての通知を一括で削除する

それぞれ、removeAllPendingNotificationRequestsremoveAllDeliveredNotificationsを呼び出してあげることで、identifierに拘らず、一括で削除することができます。

// Pendingのものを全て削除
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
// Deliveredのものを全て削除
UNUserNotificationCenter.current().removeAllDeliveredNotifications()

あるidentifierに関する通知を完全に削除する

Pendingになっているのか、Deliveredになっているのかわからないけど、とりあえず綺麗に削除して新たに通知を作成したい場合や、
どちらの状態に拘らず通知を削除したい場合は、
こんな感じでextensionを用意しておけば、2行書く手間が省けます。

extension UNUserNotificationCenter {
    func removeNotificationsCompletely(withIdentifiers identifiers: [String]) {
        self.removePendingNotificationRequests(withIdentifiers: identifiers)
        self.removeDeliveredNotifications(withIdentifiers: identifiers)
    }
}

// ...

let identifiers = ["Hoge_Notification1", "Hoge_Notification2"]
UNUserNotificationCenter.current().removeNotificationsCompletely(withIdentifiers: identifiers)

これで通知が被ることも、通知センターに同じ通知が2つ出ることもなくなります。
ただ、Deliveredの通知まで消してしまうので、タイミングは考えないとですね。

最後に

今まで通知が発火する前なのか、発火したあとなのかをあまり意識していなかったのですが、今回からPendingDeliveredと区別されてAPIが提供されていることで、意識できるようになった気がします。
ちなみに、notification.stateや、request.stateみたいに、状態をプロパティで直に取得することはできないようです。

techiOS10UserNotificationsiOS

enumのnamingで困った

【iOS10】通知に画像や動画(アタッチメント)を付与する