Swiftのローカル通知の更新は「更新」ではなく「置き換え」が正しい


Swiftのローカル通知の更新でハマったのでメモしておきます。結論はタイトルのとおりです。

ローカル通知の更新がうまくいかない

以下のようなローカル通知を登録したあと、通知の時間やメッセージ内容を変更したいときがありました。

let content = UNMutableNotificationContent()
content.title = "title"
content.body = "body"
content.sound = UNNotificationSound.default()

// 毎日8:30に通知する
var date = DateComponents()
date.hour = 8
date.minute = 30
let trigger = UNCalendarNotificationTrigger(dateMatching: date, repeats: true)
let request = UNNotificationRequest(identifier: "xxx", content: content, trigger: trigger)
let center = UNUserNotificationCenter.current()
center.add(request) { (error : Error?) in
     if let theError = error {
         // Handle any errors
     }
}

以下は更新後に通知されない例です。更新対象が通知時間とメッセージ内容だけだったので、content.bodytriggerの内容を設定して登録内容を更新しています。

let content = UNMutableNotificationContent()
content.body = "BODY" // 更新対象

// 毎日9:00に通知する
var date = DateComponents()
date.hour = 9 // 更新対象
date.minute = 0 // 更新対象
let trigger = UNCalendarNotificationTrigger(dateMatching: date, repeats: true)
let request = UNNotificationRequest(identifier: "xxx", content: content, trigger: trigger)
let center = UNUserNotificationCenter.current()
center.add(request) { (error : Error?) in
     if let theError = error {
         // Handle any errors
     }
}

ローカル通知の更新は「更新」ではなく「置き換え」が正しい

なぜ更新後に通知されないんだろうと思い、改めて公式ドキュメントを確認すると答えが書いてありました。要は、通知の更新 = 登録されている通知(identifierが同じ通知)との置き換えである、ということです。

The system uses the identifier parameter to determine how to handle the request:

  • If you provide a unique identifier, the system creates a new notification.
  • If the identifier matches a previously delivered notification, the system alerts the user again, replaces the old notification with the new one, and places the new notification at the top of the list.
  • If the identifier matches a pending request, the new request replaces the pending request.

init(identifier:content:trigger:) | Apple Developer Documentation

上記のコードだと、更新対象外のcontent.titlecontent.soundを設定しなかったことで、それぞれが空で登録され、通知されなかったということです。これを踏まえ、通知内容を正しく更新するコードは以下のとおりです。

let content = UNMutableNotificationContent()
content.title = "title" // 更新対象外の値も設定する
content.body = "BODY" // 更新対象
content.sound = UNNotificationSound.default() // 更新対象外の値も設定する

// 毎日9:00に通知する
var date = DateComponents()
date.hour = 9 // 更新対象
date.minute = 0 // 更新対象
let trigger = UNCalendarNotificationTrigger(dateMatching: date, repeats: true)
let request = UNNotificationRequest(identifier: "xxx", content: content, trigger: trigger)
let center = UNUserNotificationCenter.current()
center.add(request) { (error : Error?) in
     if let theError = error {
         // Handle any errors
     }
}

参考リンク