Push notifications beyond the basics

Push notifications are a great way to communicate with your users. They let you inform the user about what’s going on. Another good usecase is to remind potentially lost users about your app (a user who didn’t use the app in a while).

In this article, I’m gonna assume you know the basics of push notifications. If you’ve never implemented them yet, check out for example the Firebase Cloud Messaging getting started guide.


Today I want to talk about improving the user experience of push notifications. Sure, the basics work, but I think we can do better. One way to improve the UX is to think about these two cases:

Usually, we attach notification actions only to opening the notification, but that doesn’t have to be that way.

I recently thought about this for EventBattle - a scavenger hunt mobile game. We want to ask our users for feedback once they played for a while and we use push notifications for that. Let’s think about the two cases listed above. If the app is in the foreground, we can show the feedback dialog right away and there is no need to show the notification. If the user launches the app and has an active feedback notification, it would be nice to show the feedback dialog and cancel the active notification.

EventBattle push notifications flow diagram

Receive the notification while the app is in the foreground

UNUserNotificationCenterDelegate has function userNotificationCenter(_:willPresent:withCompletionHandler:), where you decide how to handle incoming notification while in foreground. In our case, we want to check the notification type and also verify the applicationState just to be sure we’re in the foreground.

func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        switch (notification.notificationType, UIApplication.shared.applicationState) {
        case (.feedback, .active):
            // Handle the notification here
            completionHandler([])
        default:
            completionHandler([.alert, .sound])
        }
    }

App launch with an active notification

We can use the getDeliveredNotifications function on an UNUserNotificationCenter to get active notifications, ie. notifications currently displayed to the user. Thus it’s simple to check if there is some active notification we are looking for.

extension UNUserNotificationCenter {
    func activeFeedbackNotificationExists(completionHandler: @escaping (Bool) -> Void) {
        getDeliveredNotifications { notifications in
            let activeFeedbackNotificationExists = notifications.contains(where: {
                $0.notificationType == .feedback
            })
            DispatchQueue.main.async {
                completionHandler(activeFeedbackNotificationExists)
            }
        }
    }
}

To cancel the active notification we need to get its identifier. We get it from the same getDeliveredNotifications function we used in the last example. Once we have the identifier, we can cancel the notification via removeDeliveredNotifications(withIdentifiers:).

extension UNUserNotificationCenter {
    func cancelActiveFeedbackNotifications() {
        getDeliveredNotifications { notifications in
            notifications
                .filter {
                    $0.notificationType == .feedback
                }
                .forEach { notification in
                    self.removeDeliveredNotifications(withIdentifiers: [notification.request.identifier])
                }
        }
    }
}

I hope I gave you a new perspective about push notification’s UX. It’s always good to think about outside the box. Yes, you can attach the action to click on the notification, but maybe other ways will make the application more pleasant for your users.