iOS 推送设置
更新时间:2022-07-28
APNs 是苹果官方提供的推送解决方案,主要用于在 app 处于不活跃状态时向设备发送实时通知。使用环信即时通讯 IM SDK 时,当消息接收方不在线,环信即时通讯 IM 的服务器会通过苹果的 APNs 服务向接收方设备发消息推送,该服务器会暂时保存这些消息,等用户再次上线时通过环信即时通讯 IM 的长连接投递。同时开发者可根据需要设置推送的显示样式,免打扰时间段等。
比如:当你的 app 处于不活跃状态时,有用户向你发送了消息,此时你的手机的通知中心会弹出消息相关的通知,当你再次打开 app 并登录成功,环信即时通讯 IM SDK 会主动拉取你不在线时的消息。
你不需要单独处理和消息相关的 APNs,通过 APNs 收到的只是消息通知,等用户账号登录成功后才会把消息从服务器拉过来。这里角标表示的是离线消息数,并不是实际的未读消息数。
技术原理
前提条件
1. 使用消息推送前,需要在你的设备开启推送权限,并将推送证书上传到环信即时通讯 IM 管理后台。
2. 若使用 3.9.2 及以上版本提供的推送高级功能,包括设置推送通知模式、免打扰模式和自定义推送模板,你需要在环信即时通讯云控制后台中激活推送高级功能。
开启推送权限并上传推送证书
1. 生成 CSR 文件
生成 Certificate Signing Request(CSR):
填写你的邮箱(这个邮箱是申请 App ID 的付费帐号)和常用名称(一般默认是计算机名,不用更改),并选择保存到硬盘:
在本地生成了名为 EMImDemoAPS.certSigningRequest
的 CSR 文件 。
2. 创建 App ID
生成 App ID ,如果已经有 App ID 可以跳至第3步。
选择 App ID,点击 Continue
;
选择 App, 点击 Continue
;
输入你的 App ID 描述信息,可以输入工程名;Bunble ID(在工程的 General 信息中),一般格式为 com.youcompany.youprojname。
选择需要支持 Push Notification
,点击 Continue
;
确定信息无误,点击 Register
;
3. 创建 app 的 APS 证书
回到 App IDs 选择你需要推送的 app。
找到 Push Notifications
, 点击 Configure
。
如果是开发模式,点击 Development SSL Certificate
下的 Create Certificate
。如果是生产模式,点击 Production SSL Certificate
下的 Create Certificate
。
Platform
选择 iOS
, Choose File
选择第一步中创建的 CSR
文件,点击 Continue
。
aps 文件创建成功了,点击 Download
下载到本地。(文件名:开发版本为 aps_development.cer,发布版本为 aps.cer):
4. 生成 Push 证书
导入证书
双击上一节下载的文件(aps_development.cer
和 aps.cer
)将其安装到电脑,在“钥匙串访问”中,可以看到已经导入的证书。
右键选择导出为 p12 文件, (例:存储为 EMImDemoAPS.p12
):
5. 生成 Provisioning Profile 文件(PP 文件)
选择 iOS App Development
(这里演示开发版描述文件的创建, 发布版本的创建流程一样,如果发布版本,请选择 App Store),点击 Continue。
App ID 选择需要创建 PP 文件的 App ID, 点击 Continue
。
选择需要加入开发的设备,只有加入了的设备才能进行真机调试,创建发布版本时没有这个步骤,点击 Continue
。
输入 PP 文件的名称,点击 Generate
。
PP 文件生成完成, 点击 Download
。
6. 上传到环信即时通讯 IM 管理后台
在客户端实现推送
1. 在 app 中开启推送权限
2. 将证书名称传递给 SDK
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 注册推送。
[application registerForRemoteNotifications];
// 初始化 `Options`,设置 App Key。
EMOptions *options = [EMOptions optionsWithAppkey:@"easemob-demo#easeim"];
// 填写上传证书时设置的名称。
options.apnsCertName = @"PushCertName";
[EMClient.sharedClient initializeSDKWithOptions:options];
return YES;
}
3. 获取并将 device token 传递给 SDK
DeviceToken 注册后,iOS 系统会通过以下方式将 DeviceToken 回调给你,你需要把 DeviceToken 传给 SDK。
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
[EMClient.sharedClient registerForRemoteNotificationsWithDeviceToken:deviceToken completion:^(EMError *aError) {
if (aError) {
NSLog(@"bind deviceToken error: %@", aError.errorDescription);
}
}];
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
NSLog(@"Register Remote Notifications Failed");
}
4. 设置离线推送
环信 IM 3.9.2 及以上版本对离线消息推送进行了优化。你可以对 app 以及各类型的会话开启和关闭离线推送功能,关闭时可设置关闭时长。
环信 IM 支持你对离线推送功能进行如下配置:
- 设置推送通知,包含设置推送通知方式和免打扰模式。
- 配置推送翻译和推送模板。
4.1 设置推送通知
为优化用户在处理大量推送通知时的体验,环信 IM 在 app 和会话层面提供了推送通知和免打扰(DND)模式的细粒度选项,如下表所示:
模式 |
选项 |
App |
会话 |
---|---|---|---|
推送通知方式 |
All:接收所有离线消息的推送通知。 |
✓ |
✓ |
MentionOnly:仅接收提及消息的推送通知。 |
✓ |
✓ |
|
NONE:不接收离线消息的推送通知。 |
✓ |
✓ |
|
免打扰模式 |
silentModeDuration:在指定时长内不接收推送通知。 |
✓ |
✓ |
silentModeStartTime & silentModeEndTime:在指定的时间范围内不接收推送通知。 |
✓ |
✗ |
推送通知方式
会话级别的推送通知方式设置优先于 app 级别的设置,未设置推送通知方式的会话默认采用 app 的设置。
例如,假设 app 的推送方式设置为 MentionOnly
,而指定会话的推送方式设置为 All
。你会收到来自该会话的所有推送通知,而对于其他会话来说,你只会收到提及你的消息的推送通知。
免打扰模式
- 你可以在 app 级别指定 DND 时长和 DND 时间范围。环信 IM 在这两个时间段内不发送离线推送通知。
- 会话仅支持 DND 时长设置;DND 时间范围的设置不生效。
对于 app 和 app 中的所有会话,DND 模式的设置优先于推送通知方式的设置。
例如,假设在 app 级别指定了免打扰时间段,并将指定会话的推送通知方式设置为 All
。免打扰模式与推送通知方式的设置无关,即在指定的免打扰时间段内,你不会收到任何推送通知。
或者,假设为会话指定了免打扰时间段,而 app 没有任何免打扰设置,并且其推送通知方式设置为 All
。在指定的免打扰时间段内,你不会收到来自该会话的任何推送通知,而所有其他会话的推送保持不变。
4.1.1 设置 app 的推送通知
你可以调用 setSilentModeForAll
设置 app 级别的推送通知,并通过指定 EMSilentModeParam
字段设置推送通知方式和免打扰模式,如下代码示例所示:
//设置推送通知方式为 `MentionOnly`。
EMSilentModeParam *param = [[EMSilentModeParam alloc]initWithParamType:EMSilentModeParamTypeRemindType];
param.remindType = EMPushRemindTypeMentionOnly;
//设置 app 的离线推送免打扰模式。
[[EMClient sharedClient].pushManager setSilentModeForAll:param completion:^(EMSilentModeResult *aResult, EMError *aError) {
if (aError) {
NSLog(@"setSilentModeForAll error---%@",aError.errorDescription);
}
}];
//设置离线推送免打扰时长为 15 分钟。
EMSilentModeParam *param = [[EMSilentModeParam alloc]initWithParamType:EMSilentModeParamTypeDuartion];
param.silentModeDuration = 15;
//设置离线推送的免打扰时间段为 8:30 到 15:00。
EMSilentModeParam *param = [[EMSilentModeParam alloc]initWithParamType:EMSilentModeParamTypeInterval];
param.silentModeStartTime = [[EMSilentModeTime alloc]initWithHours:8 minutes:30];
param.silentModeEndTime = [[EMSilentModeTime alloc]initWithHours:15 minutes:0];
4.1.2 获取 app 的推送通知设置
你可以调用 getSilentModeForAll
获取 app 级别的推送通知设置,如以下代码示例所示:
[[EMClient sharedClient].pushManager getSilentModeForAllWithCompletion:^(EMSilentModeResult *aResult, EMError *aError) {
if (!aError) {
//获取 app 的推送通知方式。
EMPushRemindType remindType = aResult.remindType;
//获取 app 的离线推送免打扰过期 Unix 时间戳。
NSTimeInterval ex = aResult.expireTimestamp;
//获取 app 的离线推送免打扰时段的开始时间。
EMSilentModeTime *startTime = aResult.silentModeStartTime;
EMSilentModeTime *endTime = aResult.silentModeEndTime;
}else{
NSLog(@"getSilentModeForAll error---%@",aError.errorDescription);
}
}];
4.1.3 设置单个会话的推送通知
你可以调用 setSilentModeForConversation
设置指定会话的推送通知,并通过指定 EMSilentModeParam
字段设置推送通知方式和免打扰模式,如以下代码示例所示:
//设置推送通知方式为 `MentionOnly`。
EMSilentModeParam *param = [[EMSilentModeParam alloc]initWithParamType:EMSilentModeParamTypeRemindType];
param.remindType = EMPushRemindTypeMentionOnly;
//设置离线推送免打扰时长为 15 分钟。
EMSilentModeParam *param = [[EMSilentModeParam alloc]initWithParamType:EMSilentModeParamTypeDuartion];
param.silentModeDuration = 15;
EMConversationType conversationType = EMConversationTypeGroupChat;
[[EMClient sharedClient].pushManager setSilentModeForConversation:@"conversationId" conversationType:conversationType params:param completion:^(EMSilentModeResult *aResult, EMError *aError) {
if (aError) {
NSLog(@"setSilentModeForConversation error---%@",aError.errorDescription);
}
}];
4.1.4 获取单个会话的推送通知设置
你可以调用 getSilentModeForAllWithCompletion
获取指定会话的推送通知设置,如以下代码示例所示:
[[EMClient sharedClient].pushManager getSilentModeForAllWithCompletion:^(EMSilentModeResult *aResult, EMError *aError) {
if (!aError) {
//获取会话的推送通知方式。
if(aResult.isSetConversationRemindType){
EMPushRemindType remindType = aResult.remindType;
}
//获取会话的离线推送免打扰过期 Unix 时间戳。
NSTimeInterval ex = aResult.expireTimestamp;
//获取会话的离线推送免打扰时段的开始时间。
EMSilentModeTime *startTime = aResult.silentModeStartTime;
EMSilentModeTime *endTime = aResult.silentModeEndTime;
}else{
NSLog(@"getSilentModeForAll error---%@",aError.errorDescription);
}s
}];
4.1.5 获取多个会话的推送通知设置
- 你可以在每次调用中最多获取 20 个会话的设置。
- 如果会话继承了 app 设置或其推送通知设置已过期,则返回的字典不包含此会话。
你可以调用 getSilentModeForConversations
获取多个会话的推送通知设置,如以下代码示例所示:
NSArray *conversations = @[conversation1,conversation2];
[[EMClient sharedClient].pushManager getSilentModeForConversations:conversationArray completion:^(NSDictionary<NSString*,EMSilentModeResult*>*aResult, EMError *aError) {
if (aError) {
NSLog(@"getSilentModeForConversations error---%@",aError.errorDescription);
}
}];
4.1.6 清除单个会话的推送通知方式的设置
你可以调用 clearRemindTypeForConversation:
清除指定会话的推送通知方式的设置。清除后,默认情况下,此会话会继承 app 的设置。
以下代码示例显示了如何清除会话的推送通知方式的设置:
//清除指定会话的推送通知方式的设置。清除后,该会话会采取 app 的设置。
[[EMClient sharedClient].pushManager clearRemindTypeForConversation:@"" conversationType:conversationType completion:^(EMSilentModeResult *aResult, EMError *aError) {
if (aError) {
NSLog(@"clearRemindTypeForConversation error---%@",aError.errorDescription);
}
}];
4.2 设置显示属性
4.2.1 设置推送通知的显示属性
你可以调用 updatePushDisplayName
设置推送通知中显示的昵称,如以下代码示例所示:
[EMClient.sharedClient.pushManager updatePushDisplayName:@"displayName" completion:^(NSString * aDisplayName, EMError * aError) {
if (aError)
{
NSLog(@"update push display name error: %@", aError.errorDescription);
}
}];
你也可以调用 updatePushDisplayStyle
设置推送通知的显示样式,如下代码示例所示:
[EMClient.sharedClient.pushManager updatePushDisplayStyle:EMPushDisplayStyleSimpleBanner completion:^(EMError * aError)
{
if(aError)
{
NSLog(@"update display style error --- %@", aError.errorDescription);
}
}];
DisplayStyle
是枚举类型。
参数 | 描述 |
---|---|
EMPushDisplayStyleSimpleBanner | 显示“你有一条新消息”。 |
EMPushDisplayStyleMessageSummary | 显示消息内容。 |
4.2.2 获取推送通知的显示属性
你可以调用 getPushNotificationOptionsFromServerWithCompletion
获取推送通知中的显示属性,如以下代码示例所示:
[EMClient.sharedClient.pushManager getPushNotificationOptionsFromServerWithCompletion:^(EMPushOptions * aOptions, EMError * aError)
{
if (aError)
{
NSLog(@"get push options error --- %@", aError.errorDescription);
}
}];
EMPushOptions
推送配置对象。
属性名 | 描述 |
---|---|
displayName | 对方收到推送时发送方展示的名称。 |
displayStyle | 推送显示类型。 |
4.3 设置推送翻译
如果用户启用 自动翻译功能并发送消息,SDK 会同时发送原始消息和翻译后的消息。
推送通知与翻译功能协同工作。作为接收方,你可以设置你在离线时希望接收的推送通知的首选语言。如果翻译消息的语言符合你的设置,则翻译消息显示在推送通知中;否则,将显示原始消息。
以下代码示例显示了如何设置和获取推送通知的首选语言:
//设置离线推送的首选语言。
[[EMClient sharedClient].pushManager setPreferredNotificationLanguage:@"EU" completion:^(EMError *aError) {
if (aError) {
NSLog(@"setPushPerformLanguageCompletion error---%@",aError.errorDescription);
}
}];
//获取离线推送的首选语言。
[[EMClient sharedClient].pushManager getPreferredNotificationLanguageCompletion:^(NSString *aLaguangeCode, EMError *aError) {
if (!aError) {
NSLog(@"getPushPerformLanguage---%@",aLaguangeCode);
}
}];
4.4 设置推送模板
环信 IM 支持自定义推送通知模板。使用前,你可参考以下步骤在环信即时通讯云管理后台上创建推送模板:
- 登录环信 IM Console,进入首页。 - 在 应用列表 区域中,点击对应 app 的 操作 一栏中的 查看 按钮。 - 在环信 IM 配置页面的左侧导航栏,选择 即时通讯 > 功能配置 > 消息推送 > 模板管理,进入推送模板管理页面。
- 点击 添加推送模板。弹出以下页面,进行参数配置。
在环信即时通讯云管理后台中完成模板创建后,用户可以在发送消息时选择此推送模板作为默认布局,如下代码示例所示:
//下面以文本消息为例,其他类型的消息设置方法相同。
EMTextMessageBody *body = [[EMTextMessageBody alloc]initWithText:@"test"];
EMChatMessage *message = [[EMChatMessage alloc]initWithConversationID:@"conversationId" from:@"currentUsername" to:@"conversationId" body:body ext:nil];
//设置推送模板。设置前需在环信即时通讯云管理后台上创建推送模板。
NSDictionary *pushObject = @{
@"name":@"templateName",//设置推送模板名称。
@"title_args":@[@"titleValue1"],//设置填写模板标题的 value 数组。
@"content_args":@[@"contentValue1"]//设置填写模板内容的 value 数组。
};
message.ext = @{
@"em_push_template":pushObject,
};
message.chatType = EMChatTypeChat;
[[EMClient sharedClient].chatManager sendMessage:message progress:nil completion:nil];
5. 解析收到的推送字段
当设备收到推送并点击时,iOS 会通过 launchOptions
将推送中的 JSON 传递给 app,这样就可以根据推送的内容定制 app 的一些行为,比如页面跳转等。 当收到推送通知并点击推送时,app 获取推送内容的方法:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
NSDictionary *userInfo = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
}
userInfo:
{
"aps":{
"alert":{
"body":"你有一条新消息"
},
"badge":1,
"sound":"default"
},
"f":"6001",
"t":"6006",
"g":"1421300621769",
"m":"373360335316321408"
}
参数 | 描述 |
---|---|
body | 显示内容。 |
badge | 角标数。 |
sound | 提示铃声。 |
f | 消息发送方 ID。 |
t | 消息接收方 ID。 |
g | 群组 ID,单聊无该字段。 |
m | 消息 ID。 |
更多功能
自定义字段
向推送中添加自定义业务字段满足业务需求,例如,通过这条推送跳转到某个活动页面。
EMTextMessageBody *body = [[EMTextMessageBody alloc] initWithText:@"test"];
EMChatMessage *message = [[EMChatMessage alloc] initWithConversationID:conversationId from:currentUsername to:conversationId body:body ext:nil];
message.ext = @{@"em_apns_ext":@{@"extern":@"custom string"}};
message.chatType = EMChatTypeChat;
[EMClient.sharedClient.chatManager sendMessage:message progress:nil completion:nil];
参数 | 描述 |
---|---|
body | 消息体。 |
ConversationID | 消息属于的会话 ID。 |
from | 消息发送方,一般为当前登录 ID。 |
to | 消息接收方 ID,一般与 ConversationID 一致。 |
em_apns_ext | 消息扩展,使用扩展的方式向推送中添加自定义字段,该值为固定值,不可修改。 |
extern | 自定义字段 key,用于设置自定义的内容,该值为固定值,不可修改。 |
custom string | 自定义字段内容。 |
解析的内容
{
"apns": {
"alert": {
"body": "test"
},
"badge": 1,
"sound": "default"
},
"e": "custom string",
"f": "6001",
"t": "6006",
"m": "373360335316321408"
}
参数 | 描述 |
---|---|
body | 显示内容。 |
badge | 角标数。 |
sound | 提示铃声。 |
f | 消息发送方 ID。 |
t | 消息接收方 ID。 |
e | 自定义信息。 |
m | 消息 ID。 |
自定义显示
自定义显示内容时,你可以随意设置 APNs 通知时显示的内容。
EMTextMessageBody *body = [[EMTextMessageBody alloc] initWithText:@"test"];
EMChatMessage *message = [[EMChatMessage alloc] initWithConversationID:conversationId from:currentUsername to:conversationId body:body ext:nil];
message.ext = @{@"em_apns_ext":@{
@"em_alert_title": @"customTitle",
@"em_alert_subTitle": @"customSubTitle",
@"em_alert_body": @"customBody"
}};
message.chatType = EMChatTypeChat;
[EMClient.sharedClient.chatManager sendMessage:message progress:nil completion:nil];
参数 | 描述 |
---|---|
body | 消息体。 |
ConversationID | 消息属于的会话 ID。 |
from | 消息发送方,一般为当前登录 ID。 |
to | 消息接收方 ID,一般与 ConversationID 一致。 |
em_apns_ext | 消息扩展,使用扩展的方式向推送中添加自定义字段,该值为固定值,不可修改。 |
em_alert_title | 推送通知的自定义标题。 |
em_alert_subTitle | 推送通知的自定义副标题。 |
em_alert_body | 推送通知展示的自定义内容。 |
解析的内容
{
"aps":{
"alert":{
"body":"custom push content"
},
"badge":1,
"sound":"default"
},
"f":"6001",
"t":"6006",
"m":"373360335316321408",
}
参数 | 描述 |
---|---|
body | 显示内容。 |
badge | 角标数。 |
sound | 提示铃声。 |
f | 消息发送方 ID。 |
t | 消息接收方 ID。 |
m | 消息 ID。 |
自定义铃声
推送铃声是指用户收到推送时的提示音,你需要将音频文件加入到 app 中,并在推送中配置使用的音频文件名称。
- 支持格式 Linear PCM MA4 (IMA/ADPCM) µLaw aLaw。
- 音频文件存放路径 AppData/Library/Sounds,时长不得超过 30 秒。
更多内容可以参考苹果官方文档Generating a Remote Notification.
EMTextMessageBody *body = [[EMTextMessageBody alloc] initWithText:@"test"];
EMChatMessage *message = [[EMChatMessage alloc] initWithConversationID:conversationId from:currentUsername to:conversationId body:body ext:nil];
message.ext = @{@"em_apns_ext":@{@"em_push_sound":@"custom.caf"}};
message.chatType = EMChatTypeChat;
[EMClient.sharedClient.chatManager sendMessage:message progress:nil completion:nil];
参数 | 描述 |
---|---|
body | 消息体。 |
ConversationID | 消息属于的会话 ID。 |
from | 消息发送方,一般为当前登录 ID。 |
to | 消息接收方 ID,一般与 ConversationID 一致。 |
em_apns_ext | 消息扩展,使用扩展的方式向推送中添加自定义字段,该值为固定值,不可修改。 |
em_push_sound | 自定义字段,用于设置自定义要显示的内容,该值为固定值,不可修改。 |
custom.caf | 音频文件名称。 |
解析的内容
{
"aps":{
"alert":{
"body":"你有一条新消息"
},
"badge":1,
"sound":"custom.caf"
},
"f":"6001",
"t":"6006",
"m":"373360335316321408"
}
参数 | 描述 |
---|---|
body | 显示内容。 |
badge | 角标数。 |
sound | 提示铃声。 |
f | 消息发送方 ID。 |
t | 消息接收方 ID。 |
m | 消息 ID。 |
强制推送
使用该方式设置后,本条消息会忽略接收方的免打扰设置,不论是否处于免打扰时间段都会正常向对方推送通知;
EMTextMessageBody *body = [[EMTextMessageBody alloc] initWithText:@"test"];
EMChatMessage *message = [[EMChatMessage alloc] initWithConversationID:conversationId from:currentUsername to:conversationId body:body ext:nil];
message.ext = @{@"em_force_notification":@YES};
message.chatType = EMChatTypeChat;
[EMClient.sharedClient.chatManager sendMessage:message progress:nil completion:nil];
参数 | 描述 |
---|---|
body | 消息体。 |
ConversationID | 消息属于的会话 ID。 |
from | 消息发送方,一般为当前登录 ID。 |
to | 消息接收方 ID,一般与 ConversationID 一致。 |
em_force_notification | 标志是否为强制推送的关键字,不可修改。 |
基于 UNNotificationServiceExtension 的扩展功能
在 iOS10 之后生效,目的是为了唤醒 UNNotificationServiceExtension,让你可以做更多的扩展。
EMTextMessageBody *body = [[EMTextMessageBody alloc] initWithText:@"test"];
EMChatMessage *message = [[EMChatMessage alloc] initWithConversationID:conversationId from:currentUsername to:conversationId body:body ext:nil];
message.ext = @{@"em_apns_ext":@{@"em_push_mutable_content":@YES}};
message.chatType = EMChatTypeChat;
[EMClient.sharedClient.chatManager sendMessage:message progress:nil completion:nil];
参数 | 描述 |
---|---|
body | 消息体。 |
ConversationID | 消息属于的会话 ID。 |
from | 消息发送方,一般为当前登录 ID。 |
to | 消息接收方 ID,一般与 ConversationID 一致。 |
em_apns_ext | 消息扩展,使用扩展的方式向推送中添加自定义字段,该值为固定值,不可修改。 |
em_push_mutable_content | 是否使用推送扩展的关键字,不可修改。 |
解析的内容
{
"aps":{
"alert":{
"body":"test"
},
"badge":1,
"sound":"default",
"mutable-content":1
},
"f":"6001",
"t":"6006",
"m":"373360335316321408"
}
参数 | 描述 |
---|---|
body | 显示内容。 |
badge | 角标数。 |
sound | 提示铃声。 |
mutable-content | 苹果要求的关键字,存在之后才可唤醒 UNNotificationServiceExtension。 |
f | 消息发送方 ID。 |
t | 消息接收方 ID。 |
m | 消息 ID。 |