通知那些事儿(五):远程通知有效载荷

本文译自《The Remote Notification Payload》

每个远程通知都包含一个有效载荷。有效载荷包含了系统如何提醒用户和你提供的自定义数据的相关信息。通知有效载荷的最大值取决于你服务器所调用的API。当你使用HTTP/2协议的API时,最大的有效载荷值是4096字节。如果采用的是之前的二进制接口,最大值为2048字节。APNs会拒绝任何超过最大长度的通知。

载荷体键值

对于每个通知,由一个JSON字典对象构成(在RFC4627中定义)。该字典内必须包含另个一键为aps的字典对象。aps包含一种或多种下面列举出的用户通知类型:

  • 展示给用户的提示消息;
  • 在应用上显示角标数字;
  • 播放提示音。

为了支持静默推送,在Info.plist文件里给UIBackgroundModes数组添加一个emote-notification值。关于UIBackgroundModes数组,参考UIBackgroundModes

如果你的目标应用在通知到达的时候没有正在运行,那么对应的提示消息、数字角标或提示音就会显示或播放。如果你的应用正在运行,系统会将该通知一一个字典对象传递给应用代理。

你的服务器可以在苹果预留的aps字典之外自定义特定字段的载荷。自定义的值必须是JSON结构,而且必须是基本类型:dictionary、array、string、number和Boolean。你不应在自定义的数据里面包含用户信息或者其他敏感数据,而应该像在设置上下文(用于用户界面)或内部度量之类的场景下使用它。比如,一个自定义载荷数据包或许作为一个即时通讯应用中作为会话标识,或者作为服务器发送该通知的时间戳。任何关联提示消息的操作都不应该被销毁,比如,不能删除设备上的数据。

重要:分发通知是“尽最大努力交付”,而不保证。它不是要发送数据给你的应用,而是通知用户这里有新的数据可以获取。

表5-1:aps字典的键值对

说明
alert string、dictionary 如果包含该属性,系统就会根据用户的设置来显示一个标准的提醒框或者横幅。你可以给该键指定一个字符串或者字典对象。1. 如果你指定了字符串,该字符串就会成为提示框的提示文本,该提示框有‘关闭’和‘查看’按钮,点击‘查看’就会启动应用。2.如果你指定了字典,可以参考表5-2该字典的键,而且不支持JSON ‘\U’符号,会将UTF8的字符显示在提示中
badge number 数字将会显示在应用角标上。如果该属性未使用,那么角标就不会改变。如果为了移除应用角标,可以将该值设为0
sound string 在应用bundle里的或者Library/Sounds应用数据容器里的文件夹中的音频文件名。在该文件中的声音将会被播放。如果该音频文件不存在或将该值设为default,那么默认的提示音就会被播放。该音频文件的音频格式必须与系统音频兼容,详见Preparing Custom Alert Sounds
content-available number 假如为该键赋值为1,就表明有新内容可以获取。检测到该键有值意味着,你的应用在后台或恢复后启动中调用了application:didReceiveRemoteNotification:fetchCompletionHandler:方法。
category string 提供该键一个字符串值,该值指的是你创建了自定义操作的UIMutableUserNotificationCategory对象的标识。

表5-2:alert字典的键值对

说明
title string 一个描述通知目的的简短字符串。Apple Watch会将该字符串作为通知界面的一部分。这个字符串能简洁的展示而且需要精心选定以方便快速理解。该键是从iOS8.2开始提供的。
body string 提示文本
title-loc-key string or null 该键为title国际化提供了解决方案,在Localizable.strings指定对应键即可。同时该键配合title-loc-args可以组成变量字符串如%@%n$@。该键也是从iOS8.2开始体用。更多参考 Localized Formatted Strings
title-loc-args array of strings or null title-loc-key中指定的变量可以在这里给出。从iOS8.2开始支持。
action-loc-key string or null 如果你指定了字符串,该字符串就会成为提示框的提示文本,该提示框有‘关闭’和‘查看’按钮,为你提供‘查看’国际化文本的选择。
loc-key string 该值类似title-loc-key,但是是针对alert message,即提示文本的国际化支持。
loc-args array of strings title-loc-args类似,为提示文本对应提供变量支持。
launch-image string 该键的值是一个图片文件名,可以包含也可不包含文件后缀。该图片用于在你点击操作按钮或者滑动操作按钮时的应用加载图片。如果你没有提供,那么系统就会使用之前的快照,使用Info.plistUILaunchImageFile指定的图片,或者也没有,就使用Default.png。从iOS4.0开始提供支持。

注:如果你想提供一个有‘关闭’和‘查看’按钮的提示框,那么直接为alert键提供一个字符串。而不是为为alert提供一个只包含body值的字典。

配置静默推送

aps字典也有包含一个content-available属性,指定该属性为1可以是远程通知表现和静默推送一致。当一个静默推送到来时,iOS会在后台唤醒你的应用,这样你就可以从你的服务器获取到新数据或者在后台处理这些信息。在静默推送中新数据或者改变的数据并不会告知用户,但是在应用下一次启动时,用户能够发现静默推送所做的工作。

对于静默推送,注意的是要确保在aps字典里没有alertsoundbadge等属性值。如果你没有遵循该规范,那么错误的配置可能会导致你的通知被阻塞或者通知无法分发到在后台的应用,而且不是以静默的方式,是以显示的方式展示给用户。

国际化格式化字符串

你可以通过两种方式来国际化提示信息:

  • 你的服务器在发送通知时,就将提示信息国际化。为此,必须先获得设备当前所选择的语言(详见Passing the Provider the Current Language Preference (Remote Notifications

  • 应用在本地bundle保存alert-message的国际化文本。服务器推送的通知中在aps包含loc-keyloc-args(针对通知title,对应的字段是title-loc-keytitle-loc-args)。当应用收到通知(确保你的应用在后台),应用就会根据aps字段的属性值找出当前语言的国际化文本并做好相应的格式化来展示给用户。

下面是关于第二种方法的更为详细的介绍:

应用能够为每一种语言的资源,如图片、声音或文本做国际化支持。国际化会收集资源并将它们放置于子目录下,该子目录的名称包含一个国家代码和后缀.lproj,比如fr.lproj。国际化的文本会以编程方式展现,它们都被放在一个叫Localizable.strings的文件中。在这个文件中每条都有一个键和一个本地化字符串值。值可以通过格式化指定变量进行替换。当应用请求某一特定资源——比如本地化字符串——它机会去获得用户当前所选中语言对应的资源。例如,如果首选语言是法语,那么为alert-message提供相应的该字符串值,就应该从fr.lproj目录下的Localizable.strings去获取。应用可以通过NSLocalizedString宏来完成这个请求。

注:该模式也适用于action-loc-key属性。该属性值同样作为在对应的语言资源包下的Localizable.strings文件里每一项中的键使用。iOS会根据该键获取到提示框右边按钮的显示文本。

下面提供了一个例子,更清楚的说明了这个过程:

"alert" : {
    "loc-key" : "GAME_PLAY_REQUEST_FORMAT",
    "loc-args" : [ "Jenna", "Frank"]
}

当收到通知后,利用GAME_PLAY_REQUEST_FORMAT作为键去查对应语言资源包下面的Localizable.strings文件,要确保的是在Localizable.strings文件里要有该键对应的项,比如像下面这样:

"GAME_PLAY_REQUEST_FORMAT" = "%@ and %@ have invited you to play Monopoly";

设备接收到通知后,就会显示“Jenna and Frank have invited you to play Monopoly”。

另外,针对格式化说明符%@,你可以用%n$@来替代对应的字符串变量。其中n是指loc-args数组中的字符串位置。所以,假如你在Localizable.strings文件:

"GAME_PLAY_REQUEST_FORMAT" = "%2$@ and %1$@ have invited you to play Monopoly";

那么对应显示的消息就是“Frank and Jenna have invited you to play Monopoly”。

关于更过国际化的内容,可参考Internationalization and Localization Guide

关于字符串格式化的讨论在String Programming Guide里的Formatting String Objects

注:一般来说,你只有在真正需要的时候,才应该使用loc-keyloc-args属性和alert字典。利用这些属性,特别是当他们都是很长的字符串的时候,会占用更高的带宽而影响性能。许多应用不需要这些属性因为他们的消息字符串来自于用户。

JSON Payload实例

实例1

{
    "aps" : { "alert" : "Message received from Bob" },
    "acme2" : [ "bang",  "whiz" ]
}

该实例通知的aps字典包含一个简洁、推荐的提示信息。并且提示按钮是默认的关闭查看。还包含自定义的数据数组acme2

实例2

{
    "aps" : {
        "alert" : {
            "title" : "Game Request",
            "body" : "Bob wants to play poker",
            "action-loc-key" : "PLAY"
        },
        "badge" : 5
    },
    "acme1" : "bar",
    "acme2" : [ "bang",  "whiz" ]
}

该实例通知包含一个aps是一个字典。其提示框右边的按钮需要根据action-loc-key去语言资源文件里查找对应的项,比如根据PLAY找打对饮的项为Play,就会显示在按钮上。同时,该通知会使得应用角标的数字变为5。

实例3

{
    "aps" : {
        "alert" : "You got your emails.",
        "badge" : 9,
        "sound" : "bingbong.aiff"
    },
    "acme1" : "bar",
    "acme2" : 42
}

该实例通知,会显示默认的提示框按钮,同时在应用角标显示9的时候,通知到来时会播放bingbong.aiff的音频作为提示音。

实例4

{
    "aps" : {
        "alert" : {
            "loc-key" : "GAME_PLAY_REQUEST_FORMAT",
            "loc-args" : [ "Jenna", "Frank"]
        },
        "sound" : "chime.aiff"
    },
    "acme" : "foo"
}

该实例通知,会根据alert字典中的loc-keyloc-args,去拼接成alert-message

实例5

{
   "aps" : {
      "category" : "NEW_MESSAGE_CATEGORY"
      "alert" : {
         "body" : "Acme message received from Johnny Appleseed",
         "action-loc-key" : "VIEW"
      },
      "badge" : 3,
      "sound" : “chime.aiff"
   },
   "acme-account" : "jane.appleseed@apple.com",
   "acme-message" : "message123456"
}

该实例通知,特别之处在于包含了一个用于交互式通知用到的分类信息。category即指定了分类。