Notificationの落とし穴

ClockClipの仕上げで、アプリがバックグラウンドになっても、アラームの時間になったら通知を受け取れるようにしようとして、ハマってました。

時間トリガーの通知が、時間になってもこない

以下のようなコードで、通知を待ってましたが、こない・・・・

let content = UNMutableNotificationContent()
content.title = "Alarm from ClockClip"
content.body = "Alarm expired"
let alarmDateComp = self.alarmTime.dateComponents
var triggerDateComp = DateComponents()
triggerDateComp.hour = alarmDateComp.hour
triggerDateComp.minute = alarmDateComp.minute
print("\(triggerDateComp.hour):\(triggerDateComp.minute)")
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDateComp, repeats: false)
let request = UNNotificationRequest(identifier: "ClockClip", content: content, trigger: trigger)

UNUserNotificationCenter.current().add(request, withCompletionHandler: { error in
    print("request done")
})
//
print("request notification")

試行錯誤

以下のようなサンプルを作って試したところ、うまく動きました。

Button(action: {
    let content = UNMutableNotificationContent()
    content.title = "Title"
    content.subtitle = "Subtitle"
    content.body = "body"
//            let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 3, repeats: false)
    var dateComp = DateComponents()
    dateComp.hour = 14
    dateComp.minute = 35
    let trigger = UNCalendarNotificationTrigger(dateMatching: dateComp, repeats: false)
    let request = UNNotificationRequest(identifier: "ClockClip", content: content,
                                        trigger: trigger)
    UNUserNotificationCenter.current().add(request, withCompletionHandler: { _ in
        print("looks done")
    })
}, label: {
    Text("publish notification")
})

なぜ、期待通りに動かないか

上記のコードを試したのは、午後2時半すぎでした。DateComponentで指定しているのは、14時と35分です。
このコードはうまく動いたんです。2:35ぴったりに通知がでました。
普通に見えますが、よ〜く考えてみるとDateで保持しているのは、GMTのはずなので、(14-9)=5時と指定しないと、すぐには、表示されないはずです!
ということで、DateComponentには、GMT指定ではなく、ローカル時間で指定しなければいけないのではないか? という仮説を持ちました。

自分のコードでも、即値指定(alarmTimeを参考にせずに、直接数値指定)で動かしたところ、きちんと動きました!! 
つまり、設定すべきは、ローカル時間でした!

UNCalendarNotificationTriggerは、ローカル時間でのDateComponentを期待している

つまり、UNCalendarNotificationTriggerの1番目の引数のDateComponentは、ローカル時間での数値を期待しているということです。
ドキュメントを改めて読み返してみたのですが、そのように書かれている箇所はみつかりませんでした。(そうでないと書いている場所もないのですが)

以下のようなコードを使うことで指定時間に表示することができるようになりました。

let content = UNMutableNotificationContent()
content.title = "Alarm from ClockClip"
content.body = "Alarm expired"

let alarmDateComp = DateInRegion.init(self.alarmTime, region: Region.current).dateComponents
var triggerDateComp = DateComponents()
triggerDateComp.hour = alarmDateComp.hour
triggerDateComp.minute = alarmDateComp.minute
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDateComp, repeats: false)
let request = UNNotificationRequest(identifier: "ClockClip", content: content, trigger: trigger)

UNUserNotificationCenter.current().add(request, withCompletionHandler: { _ in
    print("request done")
})
//
print("request notification")

# 上記のコードは、ライブラリとして、SwiftDateがないと動作しません。

注意
上記のコードで、最初は、alarmTimeから返されるDateComponentをそのまま使って、Triggerに使っていたのですが、うまくトリガーされませんでした。
なぜかは、不明ですが、新しくDateComponentを作って指定したところうまく動きました。

まとめ

UNNotificationCenterをUNCalendarNotificationTriggerをtriggerとして使うときには、いかに気をつけて指定しないと期待通りに動きませんでした。

  • DateComponentに不要な情報がないようにする。
  • DateComponentに設定する数値は、ローカル時間を指定する必要がある。

念願の通知が表示されるようになって、嬉しい!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です