Как я могу удалить ранее доставленные уведомления, когда приходит новое уведомление с UNUserNotificationCenterDelegate в iOS 10?

Этот вопрос о новом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 представила сгруппированные уведомления, я ожидаю, что менее вероятно, что они реализуют возможность обновлять старые локальные уведомления в пользу того, чтобы приложения просто отправляли новые и группировали их вместе с существующими уведомлениями.

Ответы на вопрос(2)

Ваш ответ на вопрос