Deletable iOS Push

Delete iOS push notifications remotely just like Facebook does

This functionality is available on iOS 10 and above.

Overview

You might have noticed that Facebook app can do that – you receive a push notification about a new message from a friend on your device, but it’s cleared automatically if you read it on your desktop without any interaction with an iOS app. How is this possible?

In this article we’d like to shed some light on how you can achieve the same and explain the method to make push notifications deletable.

Guide

The key to this technique is to use a silent push notification that does not display any alert, and then to create a notification based on the push payload.

The very first step is to add “Required background modes” into Info.plist file with the value “App downloads content in response to push notifications”.

This allows iOS to launch your app in background when a silent push notification is received.

As we want push notification to be silent we cannot put any text in the notification’s title otherwise it would be presented by iOS itself. Therefore, we need to pass title as custom data.

Let’s assume the following push notifications payload:

  • To create a notification we send: {"crt”:NOTIFICATION_ID, "title”:TITLE_TEXT}
    Example: {"crt”:1, "title":"hello there”}
  • To delete a notification: {"dlt”:NOTIFICATION_ID}
    Example: {"dlt":1}

The code below handles these two payloads. Add this in your AppDelegate file:

    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any],
                     fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        let userData = PushNotificationManager.push().getCustomPushData(asNSDict: userInfo)
        
        let dltPushId = userData?["dlt"]
       
        if  let pushIdString = dltPushId as? String {
            //if push is to delete notification "dlt":string"
            //load notif and delete
            let pushNotificationId = "com.pushwoosh.notification." + pushIdString
            
            UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [pushNotificationId])
            UserDefaults.standard.removeObject(forKey: pushNotificationId)
            completionHandler(UIBackgroundFetchResult.newData)
            return
        }
        
        let createPushId = userData?["crt"]
        
        if let pushIdString = createPushId as? String {
            //create local notification of the push
            //save it
            
            //we don't need to care about sound or badge as this would be handled already by iOS itself
            
            let content = UNMutableNotificationContent()
            content.title = userData?["title"] as! String
            content.userInfo = userInfo
            
            let pushNotificationId = "com.pushwoosh.notification." + pushIdString
            let request = UNNotificationRequest(identifier: pushNotificationId, content: content, trigger: nil)
            
            //present notification now
            UNUserNotificationCenter.current().add(request, withCompletionHandler: { (error) in
                if error == nil {
                    UserDefaults.standard.set(pushNotificationId, forKey: pushNotificationId)
                }
            })
        }
        
        completionHandler(UIBackgroundFetchResult.noData)
    }
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    //get custom data from push notification
    NSDictionary *userdata = [[PushNotificationManager pushManager] getCustomPushDataAsNSDict:userInfo];
    
    //check if this is deletable push (check user info for key "dlt":dict)
    //if this is not null - the dict is the deletable push
    NSString * dltPushId = [userdata objectForKey:@"dlt"];
    
    if(dltPushId) {
        //if push is to delete notification "dlt":string"
        //load notif and delete
        NSString * pushNotificationId = [NSString stringWithFormat:@"com.pushwoosh.notification.%@", dltPushId];
        
        if(pushNotificationId) {
            [[UNUserNotificationCenter currentNotificationCenter] removeDeliveredNotificationsWithIdentifiers:@[pushNotificationId]];
            [[NSUserDefaults standardUserDefaults] removeObjectForKey:pushNotificationId];
            completionHandler(UIBackgroundFetchResultNewData);
            return;
        }
        
        NSLog(@"Push notification fetch :%@", userInfo);
    }
    
    //should we create new push notification
    NSString * createPushId = [userdata objectForKey:@"crt"];
    
    if(createPushId) {
        //create local notification of the push
        //save it
        
        //we don't need to care about sound or badge as this would be handled already by iOS itself
        UNMutableNotificationContent *content = [UNMutableNotificationContent new];
        content.title = [userdata objectForKey:@"title"];
        content.userInfo = userInfo;
        
        NSString * pushNotificationId = [NSString stringWithFormat:@"com.pushwoosh.notification.%@", createPushId];
        UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:pushNotificationId content:content trigger:nil];
        
        //present notification now
        [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
            if (!error) {
                [[NSUserDefaults standardUserDefaults] setObject:pushNotificationId forKey:pushNotificationId];
            }
        }];
    }
    
    completionHandler(UIBackgroundFetchResultNewData);
}

We also need to add extra handlers to our AppDelegate:

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
         print(notification)
    }
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {
    NSLog(@"%@", response.notification)
}

Time to Test It

Make sure you:

  • don’t put any text in the Push message field
  • select only iOS platform (otherwise push notification would not be created)
  • enable the Silent Push checkbox in iOS platform settings

Go to Action tab, and in the Custom Data field add extra payload. In this example it'll be {"crt":1, "title":"hello there"}.

Woosh!
Also, for this example we’ve sent another notification with the following custom data: {"crt":2, "title":"hello there again"}:

Deleting the Push

Now let’s delete the first notification – we’ll have to send another push with Custom Data set to: {"dlt":1}:

Woosh!
Let's take a look at the notification center on the device – the first notification that we've sent is gone without user interaction:

The drawback is that your application drains a little bit more battery as iOS needs to launch the app every time a notification is received and/or it may delay push notification delivery to your app if your device is low on battery.

Deletable iOS Push

Delete iOS push notifications remotely just like Facebook does