3.
Remote Notification Payload
Written by Scott Grosch
As you learned in Chapter 2, “Push Notifications,” a push happens by sending data across the internet. That data is referred to as a payload, and it contains all of the information necessary to tell your app what to do when the push notification arrives. The Cloud service is responsible for constructing that payload and sending it, along with one or more unique device tokens, to APNs.
Originally, notifications used a packed-binary interface, where each bit of the packet had a specific meaning. Using bitfields is much harder to handle and was confusing for many developers. Apple changed the payload structure and decided to use a single, simple, JSON structure. By using JSON, Apple ensured that the payload is simple to parse and construct in any language, while also providing the flexibility needed for the future, as new capabilities are added to push notifications.
There are a few keys specifically defined by Apple, some of which are mandatory, but the rest of the keys and values are up to you to define as needed. This chapter will cover those predefined keys.
For regular remote notifications, the maximum payload size is currently 4KB (4,096 bytes). If your notification is too large, Apple will simply refuse it and you’ll get an error from APNs. If you’re suddenly worried that your push notifications won’t work because you’ve got a sizable image to send, for example, don’t worry! You’ll handle how to download attachments in Chapter 10, “Modifying the Payload.”
In this chapter, you’ll learn how to construct the payload, what the various payload keys mean, how to supply custom data and how to handle collapsed and grouped notifications.
The aps
dictionary key
The aforementioned JSON object is well-structured to hold all of the key pieces of data necessary to describe a push notification. The rest of this chapter will describe each key in detail.
The aps
dictionary key is the main hub of the notification payload wherein everything defined and owned by Apple lives. Within the object at this key, you’ll configure such items as:
- The message to be displayed to the end user.
- What the app badge number should be set to.
- What sound, if any, should be played when the notification arrives.
- Whether the notification happens without user interaction.
- Whether the notification triggers custom actions or user interfaces.
There are several keys you can use, each with their own considerations.
Alert
The key you’ll use most often is the alert
key. This key allows you to specify the message that will be displayed to your user. When push notifications were first released, the alert
key simply took a string with the message. While you can, for legacy reasons, continue to set the value to a string, it’s preferred that you instead use a dictionary. The most common payload for a message would include a simple title
and body
:
{
"aps": {
"alert": {
"title": "Your food is done.",
"body": "Be careful, it's really hot!"
}
}
}
Using a dictionary, instead of the legacy string, enables you to utilize both the title and body data points for your message. If you don’t want a title
, for example, simply leave that key/value pair out.
You may, however, run into some issues with this because of localization.
Localization
Yes, the localization monster rears its ugly head, again! If the whole world could just agree on a single language, life would be so much simpler for us developers. You can quickly tell how using a dictionary isn’t going to work for your non-English speaking users. There are two options to work around this issue:
- Call
Locale.preferredLanguages
at registration and send the list of languages your user speaks to your server. - Store localized versions of all your notifications in your app bundle.
There are pros and cons to each approach, and it really depends on the quantity and type of notifications you’ll be sending. If you keep everything on the server, and send each person the proper translation, you’ll never have to push a new version of your app when you add new notification messages.
Conversely, that means more work on the server side and more customized push notification code versus just letting iOS handle the translations for you.
If you decide to handle localization on the app side, instead of passing along title
and body
keys, you can use title-loc-key
and title-loc-args
for the title, and loc-key
and loc-args
for the body.
For example, your payload might end up looking like this:
{
"aps": {
"alert": {
"title-loc-key": "FOOD_ORDERED",
"loc-key": "FOOD_PICKUP_TIME",
"loc-args": ["2018-05-02T19:32:41Z"]
}
}
}
When iOS gets the notification, it’ll look in the proper Localizable.strings file inside your app to automatically get the correct translation, and then substitute the date and time into the proper location. This might result in an English language speaker seeing:
You can pick up your order at 5:32 p.m.
Whereas a person reading Mandarin would see this instead:
你可以五㸃半領取
To keep the rest of the examples in this chapter simple, only the title
and body
keys will be used.
Grouping notifications
Starting with iOS 12, adding the thread_identifier
key to the alert
dictionary will let iOS combine all notifications with the same identifier value into a single group in the notification center. Try to use a guaranteed unique value representing some thread of messages, such as the primary key from a database or a UUID.
In a game app, you might use this feature so that all notifications related to a specific game session are grouped together and not merged in with all other game sessions. If you leave this key out, iOS will default to grouping everything from your app together into one group. Keep in mind “grouping” notifications is different from “collapsing” notifications.
Be aware that your users are able to turn off notification grouping in the iOS Settings app, if they so desire!
Badge
Since your users might not have had their phones handy when the alert message came through, you can also badge the app icon. If you’d like your app icon to display the numerical badge number, simply specify it using the badge
key. To clear the badge and remove it, set the value to 0
.
Note: The badge number is not a mathematical addition or subtraction. It’s an absolute value that will be set on your app icon.
What this means is that your server is going to have to know what number to display to the end user, which sometimes makes this key more trouble than it’s worth. In Chapter 10, “Modifying the Payload,” when we discuss service extensions, you’ll learn a trick to work around this issue.
{
"aps": {
"alert": {
"title": "Your food is done.",
"body": "Be careful, it's really hot!"
},
"badge": 12
}
}
Sound
When alerts arrive, it’s possible to have a notification sound play. The most common value is simply the string default
, which tells iOS to play the standard alert sound. If you want to use a custom sound included in your app’s bundle, you can instead specify the name of a sound file in your app’s main bundle.
Sounds must be 30 seconds or less. If they’re any longer than 30 seconds, iOS will ignore your custom sound and fall back to the default sound.
Note: Be very careful with custom or long sounds! It seems like a great idea when developing your app, but ask yourself this — will your end users appreciate your unique sound or the length of the sound? Be sure to do some user acceptance testing before deploying to the App Store.
I had a client, for example, who provided a sports team management app. When a notification was delivered, it played the sound of a baseball being hit by a bat and the crowd roaring. Everybody thought it was pretty cool for about two days, and then he started getting bug reports begging him to remove it.
You can use the afconvert
tool on your Mac to convert your custom sound to one of the four acceptable formats:
- Linear PCM
- MA4 (IMA/ADPCM)
- 𝝁Law
- aLaw
For example, if you have an existing .mp3 file you would run a command like so:
$ afconvert -f caff -d LEI16 filename.mp3 filename.caf
Then, you can just add that new filename.caf to your Xcode project and include its name in your payload:
{
"aps": {
"alert": {
"title": "Your food is done.",
"body": "Be careful, it's really hot!"
},
"badge": 12,
"sound": "filename.caf"
}
}
Critical alert sounds
If your app needs to display a critical alert, which will be discussed in Chapter 4, “Xcode Project Setup,” you’ll need to use a dictionary as the value of the sound
key, instead of just a string:
{
"aps": {
"alert": {
"title": "Your food is done.",
"body": "Be careful, it's really hot!"
},
"badge": 12,
"sound": {
"critical": 1,
"name": "filename.caf",
"volume": 0.75
}
}
}
Notice the three keys used in the sound
dictionary above:
-
critical
: Setting this to1
will specify this sound is a critical alert. -
name
: The sound file in the app’s main bundle, as explained above. -
volume
: A value between 0.0 (silent) and 1.0 (full volume).
Other predefined keys
Apple defines three other keys as part of the aps
dictionary, which will be discussed in greater detail in later chapters. These can be used for background update notifications, custom notification types, user interfaces and groupings of notifications.
Your custom data
Everything outside of the aps
key is for your personal use. You’ll frequently find that you need to pass extra data to your app along with a push notification, and this is where you can do so. For example, if you’re writing a geocaching app, you might want to send the user the next set of coordinates to investigate. You will, therefore, send a payload like so:
{
"aps": {
"alert": {
"title": "Save The Princess!"
}
},
"coords": {
"latitude": 37.33182,
"longitude": -122.03118
}
}
In Chapter 8, “Handling Common Scenarios,” you’ll learn more about how to retrieve this data inside your app. As long as all of your custom data is kept outside of the aps
dictionary, you’ll never have to worry about conflicting with Apple.
HTTP headers
As discussed earlier, the payload is only one of a few things your server sends to APNs. Aside from a unique device token, you can send additional HTTP header fields to specify how Apple should handle your notification and how it is delivered to the user’s device. It’s unclear why Apple chose to place these as headers, instead of part of the payload.
Collapsing notifications
One of those headers is the apns-collapse-id
HTTP header field. Apple recently added the ability to collapse multiple notifications down to one when a newer notification supersedes an older one. For example, if you’re using a notification to alert users as to how many people have completed the scavenger hunt so far, you really only need to know the current total.
While you’re still diligently searching for that elusive item, three other people might have completed the game. Each time another person finds all their items, a push notification is sent to you. When you get the time to check on the status, you really don’t want to see three separate notifications saying somebody has finished. Wouldn’t you rather see a single notification saying three people are done? That’s exactly what this header field is for. You can put any unique identifier into the field, up to 64 bytes. When a notification is delivered, and this value is set, iOS will remove any other notification previously delivered that has the same value.
In the previous example of the scavenger hunt, it would make sense to use the unique ID from your database that represents that specific game. Shy away from using things like the name of the hunt to avoid inadvertently collapsing notifications that don’t relate. Try to use guaranteed unique values instead. Any type of primary key from a database or a UUID are good examples of values to use.
Note: If you’re using a third-party delivery service, they’ll have to provide a specific location for you to identify the
apns-collapse-id
. If this is a feature you think you’ll utilize, be sure to look for it explicitly when you’re shopping for a vendor.
Push type
As of iOS 13 you are now required to specify, in the headers, what type of push notification is being sent. You should specify a value of alert
when the delivery of your notification displays an alert, plays a sound or updates the badge. For silent notifications that do not interact with the user you instead specify the value of background
.
Apple’s documentation states, “The value of this header must accurately reflect the contents of your notification’s payload. If there is a mismatch, or if the header is missing on required systems, APNs may delay the delivery of the notification or drop it altogether.”
Priority
The third header you’re likely to use is apns-priority
. The default, if not specified, is 10. Specifying a priority of 10 will send the notification immediately, but is only appropriate for notifications which include an alert, sound or badge update.
Any notification which includes the content-available
key must specify a priority of 5. Notifications with a priority of 5 might be grouped and delivered together at a later point in time.
Note: Apple’s documentation states that it is an error to specify a priority of 10 for a notification whose payload contains the
content-available
key.
Key points
In this chapter, you covered the basics of the remote notification payload. Some key things to remember:
-
Prefer to use a dictionary instead of a string for the
alert
key. -
Consider how you’re going to deal with localization issues: server-side vs. client-side.
-
Utilize the
apns-collapse-id
HTTP header field when “overriding” or “updating” your notification is more appropriate than sending an additional notification. -
Place all of your custom data outside of the
aps
key to future-proof your custom keys. -
Think about whether grouping and/or collapsing your notifications makes sense.
-
Ensure you are providing a value for the the new
apns-push-type
HTTP header.
Where to go from here?
If you want to learn more about notification payloads, you might be interested in reviewing Apple’s documentation at https://apple.co/2Ia9iUf. For information on sending notification requests to APNs, including other headers and status codes, refer to Apple’s documentation at https://apple.co/2mn04ih.
With remote notification payloads covered, you’re now ready to set up your app to receive notifications in Chapter 4, “Xcode Project Setup.”