Этот вопрос касается нового UserNotifications
фреймворка в iOS 10.
У меня есть приложение, которое планирует локальное уведомление каждые полчаса после того, как пользователь выполнил определенное действие в приложении, начиная с 1 часа.
Чтобы не загромождать экран блокировки пользователя или центр уведомлений, я хочу, чтобы одновременно отображалось только одно уведомление, поэтому есть только одно уведомление с самой последней актуальной информацией. Мой план для достижения этого - удалять все доставленные уведомления каждый раз, когда появляется новое уведомление.
Кажется, это должно быть возможно с новым willPresent
методом UNUserNotificationCenterDelegate
, но он ведет себя не так, как я ожидал.
Вот функция, которую я вызываю для настройки всех уведомлений, начиная через 1 час после события в приложении и планируя уведомление каждые полчаса до последнего в 23,5 часа после события:
func updateNotifications() {
for hour in 1...23 {
scheduleNotification(withOffsetInHours: Double(hour))
scheduleNotification(withOffsetInHours: Double(hour) + 0.5)
}
}
Это функция, которая фактически планирует уведомления на основе mostRecentEventDate
, который Date
установлен в другом месте:
func scheduleNotification(withOffsetInHours: Double) {
// set up a Date for when the notification should fire
let offsetInSeconds = 60 * 60 * withOffsetInHours
let offsetFireDate = mostRecentEventDate.addingTimeInterval(offsetInSeconds)
// set up the content of the notification
let content = UNMutableNotificationContent()
content.categoryIdentifier = "reminder"
content.sound = UNNotificationSound.default()
content.title = "Attention!"
content.body = "It has been \(withOffsetInHours) hours since the most recent event."
// set up the trigger
let triggerDateComponents = Calendar.current.components([.year, .month, .day, .hour, .minute, .second], from: offsetFireDate)
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDateComponents, repeats: false)
// set up the request
let identifier = "reminder\(withOffsetInHours)"
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
// add the request for this notification
UNUserNotificationCenter.current().add(request, withCompletionHandler: { (error) in
if error != nil {
print(error)
}
})
}
В моем UNUserNotificationCenterDelegate
метод willPresent
настроен следующим образом:
func userNotificationCenter(_: UNUserNotificationCenter, willPresent: UNNotification, withCompletionHandler: (UNNotificationPresentationOptions) -> Void) {
print("will present...")
UNUserNotificationCenter.current().removeAllDeliveredNotifications()
withCompletionHandler([.alert,.sound])
}
Я знаю, что вызывается функция willPresent
, потому что она печатает «представит ...», но существующие уведомления в центре уведомлений не удаляются. Кто-нибудь знает, почему это не работает? Или есть способ заставить его работать так, как я хочу?
РЕДАКТИРОВАТЬ: Я придумал альтернативный подход для достижения того же результата, но он, похоже, тоже не работает.
Моя идея заключалась в том, чтобы использовать willPresent
для отключения входящего запланированного уведомления при планировании немедленной доставки другого уведомления (без триггера). Все уведомления, которые должны прибыть немедленно, имеют один и тот же идентификатор, поэтому существующее уведомление с этим идентификатором всегда следует заменять, как в примере около 20:00 в этот доклад WWDC 2016 года о новой структуре UserNotifications. Вот мой обновленный метод willPresent
:
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent: UNNotification, withCompletionHandler: (UNNotificationPresentationOptions) -> Void) {
let identifier = willPresent.request.identifier
if identifier != "reminder" {
let offsetInHoursString = identifier.replacingOccurrences(of: "reminder", with: "")
let content = UNMutableNotificationContent()
content.categoryIdentifier = "reminder"
content.sound = UNNotificationSound.default()
content.title = "Attention!"
content.body = "It has been \(offsetInHoursString) hours since the most recent event."
let identifier = "hydrationReminder"
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: nil)
center.add(request, withCompletionHandler: { (error) in
if error != nil {
print(error)
}
})
withCompletionHandler([])
} else {
withCompletionHandler([.alert,.sound])
}
}
РЕДАКТИРОВАТЬ: я наконец понял, что willPresent
будет только вызываться, если приложение находится на переднем плане, как сказано прямо в верхней части эта страница, поэтому ни один из этих подходов не должен работать. Я думал, что willPresent
будет вызываться каждый раз при получении уведомления. Вернемся к чертежной доске с идеей «только самое новое и самое актуальное уведомление» ...
ОБНОВЛЕНИЕ (июль 2018 г.): с введением сгруппированных уведомлений в iOS 12 обновление старых уведомлений (с целью уменьшения беспорядка в уведомлениях) кажется менее актуальным. Я по-прежнему хотел бы иметь возможность сделать это, чтобы минимизировать появление беспорядка, и, похоже, должен быть паритет функций между удаленными push-уведомлениями, которые можно обновлять постфактум, и локальными уведомлениями, которые не могут быть обновлены позже. Однако, поскольку Apple представила сгруппированные уведомления, я бы ожидал, что они с меньшей вероятностью будут реализовывать возможность обновления старых локальных уведомлений в пользу того, чтобы приложения могли просто отправлять новые и группировать их вместе с существующими уведомлениями.