In this talk, Craig Clayton covers how you can build a unique user experience using custom UI for notifications, in particular, how he created a custom experience for NFL teams to use year round for fan engagement.
Today we’re going to look how notifications are handled in iOS 10, and cover iOS 11 features. Finally, I’m going to show you how - in the sports environment - we can engage users throughout the entire year.
Setting Up Notifications
-application: didRegisterForRemoteNotifications...:
-userNotificationCenter: willPresent: completionHandler:
-userNotificationCenter: didReceive: completionHandler:
These are the three basic methods that we need to use.
didRegisterForRemoteNotifications
, is used to check whether the device has received permissions. If there was no permission, we ask the user for permission to send notifications, then we can access the device token.
The next method to implement is willPresent
. willPresent
is called when the app is in the foreground. didReceive
is called when the notification is delivered to the user. Those are the three basic methods you need to get started with from iOS 10 and 11.
Basic Notifications
{
"aps": {
"alert": {
"title": "Keys to the Starting...",
"subtitle": "Presented by CarMax",
"body": "Saturday night Bill..."
},
"badge": 0
},
}
Here’s what an iOS notification in JSON looks like. We have a dictionary of aps
that has the alert, title, subtitle, body. Everything is encapsulated in this dictionary.
Notifications with Images
In iOS 10, this was a feature that was not commonly used. You add a URL to attach images to the notifications.
{
"aps" : {
"alert": "New Message",
"mutable-content" : 1,
"sound": "default"
"badge": 0
},
"title": "Keys to the Starting...",
"subtitle": "Presented by CarMax",
"body": "Saturday night Bill...",
"url": "image-url-here.jpg"
}
The main key to this JSON is we have mutable content set to 1. This tells Apple that we want to use our own content here.
We push all the data that we want to display outside of the aps
because we’re not going to actually show it.
currentDownloadTask = URLSession.shared.downloadTask(with: url, completionHandler: { fileURL, _, error in
if let error = error { ... }
...
Then, we create a service extension to use an URL session, which allows us to download our image into our app.
if let fileURL = fileURL,
...
{
mutableContent.attachments = [attachment]
mutableContent.title = title
mutableContent.subtitle = subtitle
mutableContent.body = body
}
Finally, we set some attachments: the title, the body, and the subtitle.
iOS 11 Features
Let’s go over the iOS 11 features: hidden content, combining notifications, customizing rich notifications. We’ll review how we can use these features.
Hidden Notification Content
Hidden content is new to iOS 11. Users can now go into their settings and turn notification previews off. This means that users only see the text “notification”, as opposed to the substantive notification contents.
let scoreCategory = UNNotificationCategory(
identifier: "score-category",
actions: [],
intentIdentifiers: [],
hiddenPreviewsBodyPlaceholder: NSLocalizedString("SCORE_CATEGORY",
comment: "score placeholder"),
options: [])
First, we need to do is create a category. Our category can be for anything - articles, scores, etc.
Finally, we create a plist for localization. We add this to our notifications, and instead of just getting notifications, we can get a “score”, an “article”, or a “video” which gives some context to the user.
Combining Notifications
What happens if we have multiple notifications from different categories and want to combine those together? You don’t want to put everything in one notification when one category is a “score” and another is an “article”.
{ "aps": {
"alert": "New Message",
"sound": "default",
"thread-id": "123"
}
...
}
We can use what is known as a thread-id
, which was an iOS 10 feature. To do this, add a simple thread-id
to our push, and Apple handles everything else.
Customizing Rich Notifications
Last year we could use service notifications and be able to get data and download images, which was pretty much all the flexibility that Apple gave us.
This year, we are able to create what’s known as an iOS notification content extension
, which gives us the ability to do whatever we want.
{ "aps": {
//"alert": "New Draft Pick",
//"mutable-content": 1,
"category": "draft-category"
},
...
The first thing you need to do is add a category. Here, I have a draft-category
.
...
"round": "6",
"pick": "199",
"first_name": "Tom",
"last_name": "Brady",
"position": "QB",
...
}
It contains some extra data.
We have a plist that we have to sync with it - we have to use the same ID from our push with what is in the plist. I have draft-category
under UNNotificationExtensionCategory
. Here, I can create as many extensions as I want and create any kind of content that I want.
You also have UNNotificationExtensionInitialContentSizeRatio
. Currently, it’s set at 100%, but you can go from 1.0, to 0.1 or 0.5 - down to the size of your content.
Lastly, you have your UNNotificationExtensionDefaultContentHidden
. If you keep it as NO
, and Apple will display the default content. If you set it to YES
, it’s all yours.
func didReceive(_ notification: UNNotification) {
let content = notification.request.content
title = content.title
update(with: content)
}
In the second to last file, there is a didReceive
method in the notification content extension, which is where you write all of your code. Suppose you have a push that has JSON and you want to go to a feed, grab extra data and put it in here. This is everything that you need to do.
func update(with content: UNNotificationContent) {
if let round = content.userInfo["round"] as? String {
lblRound.text = "R\(round)"
}
if let pick = content.userInfo["pick"] as? String {
lblPick.text = pick
}
if let pos = content.userInfo["position"] as? String {
lblPosition.text = pos
}
...
}
In the update method, I’m setting the round in my round label, updating my pick label, my position label, and my images.
Engaging Users Year Round
In sports, there is both an on, and offseason. There are fans who are very engaged and want to be involved in the NFL, basketball, or hockey, etc. all year long.
How do we engage these users?
In-season
Instead of just regular push notifications, I can do notifications such as “Player of the Game”.
I can do “Game HUD”: instead of showing a simple score. I can do “Player of the Week (POTW)” voting. I can do custom user input such as having a whole poll that lets fans and users be engaged instead of seeing another notification that they can get rid of.
<img align="left" src="//images.contentful.com/s72atsk5w5jo/5ZxqRlDNeMIO4AoU2AAo00/1398042df8cb14c48d29acd92a612129/CraigClayton_CustomNotificationsOffSeason1.png" width="49%" vspace="20">
<img align="right" src="//images.contentful.com/s72atsk5w5jo/5ditPi92p2syoASkuQGWcW/fa0620e278f7ee87ff3b67d4af7f0aa3/CraigClayton_CustomNotificationsOffSeason2.png" width="49%" vspace="20">
Off-season
As soon as the season’s over, the NFL has an event set up every month until football season restarts.
February is the NFL Combine. In March, we have Free Agency, where all fans are engaged. In April is the NFL Draft. In May, is the Rookie Minicamp where everybody has been drafted and people want to see videos, articles, or anything that has to do with football.
In June, fans get OTAs. Videos or galleries can be sent to users within the notification without having them go into the app.
The Future of Notifications
The future of notifications includes hidden notification content, combining notifications, creating custom rich notifications, and engaging users year round.
In your app, you may not have a set on/offseason, but your users still leave and come back. Find ways to engage users in notifications, even if it’s a game.
Here are some useful links: Intro to Notifications, Advanced Notifications, and Best Practices and What’s New in User Notifications. These three will get you where you need to go.
About the content
This talk was delivered live in September 2017 at try! Swift NYC. The video was recorded, produced, and transcribed by Realm, and is published here with the permission of the conference organizers.