How can I remove previously delivered notification

2019-01-25 08:29发布

问题:

This question is about the new UserNotifications framework in iOS 10.

I have an app that schedules a local notification every half hour after the user did a certain action in the app, starting at 1 hour.

To avoid cluttering a user's lock screen or notification center, I only want one notification to show up at a time, so there is only one notification with the most recent, relevant information. My plan to achieve this is to clear all delivered notifications any time a new notification is presented.

It seems this should be possible with the new willPresent method of UNUserNotificationCenterDelegate, but it's not behaving as I would expect.

Here's a function that I call to set up all the notifications, starting at 1 hour after the event in the app, and scheduling a notification every half hour until the last one at 23.5 hours after the event:

func updateNotifications() {

    for hour in 1...23 {
        scheduleNotification(withOffsetInHours: Double(hour))
        scheduleNotification(withOffsetInHours: Double(hour) + 0.5)
    }
}

This is the function that actually schedules the notifications based on the mostRecentEventDate, which is a Date set elsewhere:

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)
        }
    })
}

In my UNUserNotificationCenterDelegate I have the willPresent method set up like this:

func userNotificationCenter(_: UNUserNotificationCenter, willPresent: UNNotification, withCompletionHandler: (UNNotificationPresentationOptions) -> Void) {

    print("will present...")

    UNUserNotificationCenter.current().removeAllDeliveredNotifications()

    withCompletionHandler([.alert,.sound])
}

I know the willPresent function is being called because it prints "will present..." but the existing notifications in the notification center don't get cleared. Does anyone know why this doesn't work? Or if there's a way to get it working the way I want it to?


EDIT: I came up with an alternate approach to achieve the same thing, but it also doesn't seem to work.

My idea here was to use willPresent to silence the incoming scheduled notification while scheduling another notification to arrive immediately (without a trigger). All the notifications being scheduled to arrive immediately have the same identifier, so the existing notification with that identifier should always be replaced, like in the example around 20:00 in this 2016 WWDC talk about the new UserNotifications framework. Here's my updated willPresent method:

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])
    }
}

EDIT: I finally realized that willPresent will only be called if the app is in the foreground, as it says right at the top of this page, so neither of these approaches should actually work. I thought willPresent would be called every time a notification was received. Back to the drawing board on this "only the newest, most relevant notification" idea...


UPDATE (July 2018): With the introduction of grouped notifications in iOS 12, updating an older notification (with the goal of reducing notification clutter) seems less relevant. I'd still like to be able to do it to minimize the appearance of clutter, and it seems there should be feature parity between remote push notifications, which can be updated after the fact, and local notifications, which can not be updated later. However, since Apple has introduced grouped notifications, I'd expect its less likely they'd implement the ability to update old local notifications in favor of letting apps just send new ones and having them group together with existing notifications.

回答1:

You can check this demo。

I think you want to achieve the function is called "update notification".

iOS 10 allow update the notification.All you just do——keep the notifications have the same identifier.

Let's see a demo:

  1. first notification:

    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. update the first notification:

    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!");
    }];

Now, you add two notification.But the system treats them as the same.So there is just only one.And the sencond replaces the first one.In iOS 10,call this update.

The property "identifier" is the id of UNNotificationRequest,which can differentiate notifications.



回答2:

You can just use removeDeliveredNotifications(withIdentifiers:)