Как я могу удалить ранее доставленные уведомления, когда приходит новое уведомление с 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 ответа

Вы можете проверить это демо

Я думаю, что вы хотите добиться функции под названием "уведомление об обновлении".

iOS 10 позволяет обновлять уведомления. Все, что вы делаете - сохраняйте уведомления с одинаковым идентификатором.

Давайте посмотрим демо:

  1. первое уведомление:

    NSURL * imageUrl = [[NSBundle mainBundle] URLForResource:@"dog" withExtension:@"png"];
    UNNotificationAttachment *imgAtt = [UNNotificationAttachment attachmentWithIdentifier:@"image" URL:imageUrl options:nil error:&error];
    
    NSURL * mp4Url = [[NSBundle mainBundle] URLForResource:@"media" withExtension:@"mp4"];
    UNNotificationAttachment *mediaAtt = [UNNotificationAttachment attachmentWithIdentifier:@"image" URL:mp4Url options:nil error:&error];
    
    UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc]init];
    //在通知中心显示的总是第一个多媒体资源
    content.attachments = @[imgAtt,mediaAtt];
    content.badge = @1;
    content.title = @"Wake Up";
    content.subtitle = @"First time";
    content.body = @"next time。。。 ";
    content.categoryIdentifier = @"wakeup";
    content.launchImageName = @"dog";
    content.sound = [UNNotificationSound defaultSound];
//    content.threadIdentifier = @"";
    content.userInfo = @{@"first":@"5:00 am",@"second":@"6:00"};
    
    UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:5.0 repeats:NO];
    
    UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"com.junglesong.pushtestdemo.wakeup" content:content trigger:trigger];
    
    [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
        NSLog(@"wake up message has been deliverd!");
    }];

  1. обновить первое уведомление:

    UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc]init];
    content.badge = @1;
    content.title = @"Update!dear,wake up";
    content.subtitle = @"Update! dear,please";
    content.body = @"Update!shall we have breakfast?";
    content.categoryIdentifier = @"wakeup";
    content.launchImageName = @"dog";
    content.sound = [UNNotificationSound defaultSound];
    //    content.threadIdentifier = @"";
    content.userInfo = @{@"first":@"5:00 am",@"second":@"6:00"};
    
    UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:5.0 repeats:NO];
    
    UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"com.junglesong.pushtestdemo.wakeup" content:content trigger:trigger];
    
    [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
        NSLog(@"wake up message has been updated!");
    }];

Теперь вы добавляете два уведомления. Но система обрабатывает их как одно и то же. Так что есть только одно. И второе заменяет второе. В iOS 10 вызовите это обновление.

Свойство "идентификатор" является идентификатором UNNotificationRequest, который может различать уведомления.

Вы можете просто использовать removeDeliveredNotifications(withIdentifiers:)

Другие вопросы по тегам