Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I remove previously delivered notifications when a new notification arrives with UNUserNotificationCenterDelegate in iOS 10?

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.

like image 820
gohnjanotis Avatar asked Aug 12 '16 16:08

gohnjanotis


People also ask

How do I get rid of Apple push notifications?

iOS enables you to both disable push notifications entirely, or turn them off for individual apps. To access iOS notifications settings, go into the Settings > Notifications menu. From that list, you can configure each app's notifications settings or disable them.

How do I change push notifications on iOS?

Go to Settings and tap Notifications. Select an app under Notification Style. Under Alerts, choose the alert style that you want. If you turn on Allow Notifications, you can choose when you want the notifications to be delivered — immediately or in the scheduled notification summary.

How do I see notifications I already clicked on?

Pull down your Notification Shade once and then scroll down to the bottom of your notifications. You should now see a History button (Figure 3). The History button has been added to your Notification Shade. Tap History to access your past 24 hours of notifications (Figure 4).


1 Answers

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.

like image 99
wenghengcong Avatar answered Oct 10 '22 05:10

wenghengcong