目录

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.ceraps.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 中开启推送权限

需要在 xcode 中为 app 开启推送权限。 Targets → Capability → Push Notifications

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。你会收到来自该会话的所有推送通知,而对于其他会话来说,你只会收到提及你的消息的推送通知。

免打扰模式

  1. 你可以在 app 级别指定 DND 时长和 DND 时间范围。环信 IM 在这两个时间段内不发送离线推送通知。
  2. 会话仅支持 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 获取多个会话的推送通知设置
  1. 你可以在每次调用中最多获取 20 个会话的设置。
  2. 如果会话继承了 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。