People have been replacing content with the latest since 1938. Photo by Central Press for Getty Images.

How to replace the content of an iOS notification

Making the most of iOS 10’s new notification features.

iOS notifications have typically been relatively untouchable. Once you send one, it will live on the user’s phone until they swipe it away. But since the release of iOS 10, new APIs have given us much more control over notifications after they’ve been posted, including the ability to replace the content of an existing notification. We used them to keep our inauguration live video notifications up to date, with the same principle we’ve used with web notifications to deliver election results. Unfortunately, implementing on iOS is not as easy as it could be, so here’s a quick overview of how to do it:

The easy way

If you are lucky enough to be running your own push service, or you’re using one that implements Apple’s relatively new HTTP2 API for sending notifications, you’re in luck. There’s a new attribute you can send as part of a push payload called “apns-collapse-id”. When an iOS device receives this notification, it will replace any existing notification with that collapse ID currently showing in the user’s notification tray (for those of you that have dabbled in the web Notification API, this is more or less equivalent to the tag attribute used there). So, all you need to do is generate a unique ID for each alert you send, then, if you want to replace it, send another notification using that same ID, which will take its place.

There’s one big problem here, though: many push notification providers have not implemented this new functionality. When last I checked, neither Amazon’s SNS or Google Firebase Cloud Messaging support it. So if you can’t implement thread IDs, you need to add some additional code to your app.

Update: Firebase Cloud Messaging now does support apns-collapse-id. Thanks to Dan Trenz for pointing it out.

The hard way: Notification Service Extensions

Apple introduced a new category of app extension in iOS 10 called a Notification Service Extension. It’s a small chunk of code that is run just before the phone shows a notification you’ve sent, and it lets you customise the content of that notification before the user ever sees it. It’s primarily intended to be used to attach photos or video to a notification, but it also lets you change most attributes of a notification before it is shown. Unfortunately it *doesn’t* let you customise the thread ID, but we can emulate that behaviour.

When we send a notification, we can attach our own custom identifier to the payload (something all push services *do* support). Then, when our notification service launches, grab that ID:

class NotificationService: UNNotificationServiceExtension {

override func didReceive(
_ request: UNNotificationRequest,
withContentHandler contentHandler:
@escaping (UNNotificationContent) -> Void)
{

let userInfo = request.content.userInfo
let customID = userInfo["custom-payload-id"] as? String

}
}

Then, through the UNUserNotificationCenter API, we can get a list of all the currently visible notifications we have on a user’s phone, and find one that matches our ID:

let customID = userInfo["custom-payload-id"] as? String
UNUserNotificationCenter.current()
.getDeliveredNotifications { notifications in
    let matching = notifications.first(where: { notify in
let existingUserInfo = notify.request.content.userInfo
let id = existingUserInfo["custom-payload-id"] as? String
return id == customID
})

}

and, if it exists, remove it:

if let matchExists = matching {
UNUserNotificationCenter.current().removeDeliveredNotifications(
withIdentifiers: [matchExists.request.identifier]
)
}

The collapse ID-based solution replaces immediately, while our homebrewed hack clearly shows the notification disappearing then reappearing:

But all things considered, it works okay.

Next steps

Ideally, we wouldn’t have to do any of this. I suspect that push service providers haven’t implemented the apns-collapse-id field because it requires you to be using the HTTP2 API, which would mean they will have to re-engineer their entire push messaging stacks, and that will take some time. So Apple could make the collapse ID available via their old API, or they could let us customise the collapse ID as part of a Notification Service Extension. We’d still need extra local code but at least we’d be doing things the way they are supposed to be done.

Also, keep these new APIs in mind when you’re working on anything to do with notifications. The more we can do to ensure that a user is getting the very latest information whenever they look at their lock screen, the better!


The Guardian Mobile Innovation Lab operates with the generous support of the John S. and James L. Knight Foundation.