注意:
自集成推送仅支持普通消息推送,已停止维护。强烈建议您使用 推送插件 (TIMPush),只需进行简单配置,即可一键式集成接入多个厂商的推送服务,不仅支持普通消息推送,还支持全员推送和按用户标签推送,具备完整的推送生命周期查询、数据统计、问题排查等功能,为您提供稳定、及时、多样化的一站式综合推送服务。
本文旨在介绍如何自主集成苹果 APNS 推送服务。
配置离线推送
如想要接收 APNs 离线消息通知,需要遵从如下几个步骤:
1. 申请 APNs 证书。
2. 上传证书到 IM 控制台。
3. 在 App 每次登录时,向苹果获取 deviceToken。
4. 调用 setAPNS 接口将其上报到 IM 后台。
步骤1:申请 APNs 证书
开启 App 远程推送
1. 登录 苹果开发者中心 网站,单击 Certificates,Identifiers & Profiles 或者侧栏的 Certificates, IDS & Profiles,进入 Certificates, IDS & Profiles 页面。
?
?
?2. 单击 Identifiers 右侧的 +。
?
?
?3. 您可以参见如下步骤新建一个 AppID,或者在您原有的 AppID 上增加
Push Notification
的 Service
。说明:
您 App 的
Bundle ID
不能使用通配符 *
,否则将无法使用远程推送服务。4. 勾选 App IDs,单击 Continue 进行下一步。
?
?
?5. 选择 App,单击 Continue 进行下一步。
?
?
?6. 配置
Bundle ID
等其他信息,单击 Continue 进行下一步。?
?
?7. 勾选 Push Notifications,开启远程推送服务。
?
生成证书
1. 选中您的 AppID,选择 Configure。
?
2. 可以看到在 Apple Push Notification service SSL Certificates 窗口中有两个
SSL Certificate
,分别用于开发环境(Development)和生产环境(Production)的远程推送证书,如下图所示:?
3. ?
我
们先选择开发环境(Development)的 Create Certificate,系统将提示我们需要一个 Certificate Signing Request(CSR)。?
4. 在 Mac 上打开钥匙串访问工具(Keychain Access),在菜单中选择钥匙串访问 > 证书助理 > 从证书颁发机构请求证书(
Keychain Access - Certificate Assistant - Request a Certificate From a Certificate Authority
)。?
5. 输入用户电子邮件地址(您的邮箱)、常用名称(您的名称或公司名),选择存储到磁盘,单击继续,系统将生成一个
*.certSigningRequest
文件。?
6. 返回上述 步骤3 中 Apple Developer 网站刚才的页面,单击 Choose File 上传生成的
*.certSigningRequest
文件。?
7. 单击 Continue,即可生成推送证书。
?
8. 单击 Download 下载开发环境的
Development SSL Certificate
到本地。?
9. 再次按照上述步骤1 - 8,将生产环境的
Production SSL Certificate
下载到本地。说明:
生产环境的证书实际是开发(Sandbox)+ 生产(Production)的合并证书,可以同时作为开发环境和生产环境的证书使用。
?
10. 双击打开下载的开发环境和生产环境的
SSL Certificate
,系统会将其导入钥匙串中。11. 打开钥匙串应用,在登录 > 我的证书,右键分别导出刚创建的开发环境(
Apple Development IOS Push Service
)和生产环境(Apple Push Services
)的 P12
文件。?
?
?注意:
保存
P12
文件时,请务必要为其设置密码。步骤2:上传证书到控制台
1. 登录 即时通信 IM 控制台。
2. 进入设置应用的基础配置页面,单击推送管理 > 接入设置 > 添加证书添加推送证书。
?
?
?3. 选择证书类型,上传 iOS 证书(p.12),设置证书密码,单击确定。
?
?
?注意:
上传的证书名最好使用全英文(尤其不能使用括号等特殊字符)。
上传证书需要设置密码,如果没有密码将收不到推送。
发布 App Store 的证书需要设置为生产环境,否则无法收到推送。
上传的 p12 证书必须是自己申请的真实有效的证书。
4. ?
生成推送证书信息后,记录证书的 ID。
??
?
?步骤3:App 向苹果后台请求 deviceToken
您可以在您的 App 中添加如下代码,用来向苹果的后台服务器获取 deviceToken:
说明:
考虑到合规,建议您在用户同意隐私协议之后再向苹果请求 deviceToken。
// 向苹果后台请求 DeviceToken- (void)registNotification{if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0){[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge)categories:nil]];[[UIApplication sharedApplication] registerForRemoteNotifications];}else{[[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert)];}}?//在 AppDelegate 的回调中会返回 deviceToken,需要在登录后上报给腾讯云后台-(void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{//记录下 Apple 返回的 deviceToken_deviceToken = deviceToken;}
步骤4:登录 IM SDK 后上传 deviceToken 到腾讯云
- (void)onReportToken:(NSData *)deviceToken{if (deviceToken) {V2TIMAPNSConfig *confg = [[V2TIMAPNSConfig alloc] init];// 企业证书 ID// 用户自己到苹果注册开发者证书,在开发者账号中下载并生成证书(p12 文件),将生成的 p12 文件传到腾讯证书管理控制台,// 控制台会自动生成一个证书 ID,将证书 ID 传入一下 busiId 参数中。confg.businessID = self.apnsCertificateID;confg.token = deviceToken;confg.isTPNSToken = NO;[[V2TIMManager sharedInstance] setAPNS:confg succ:^{NSLog(@"%s, succ", __func__);} fail:^(int code, NSString *msg) {NSLog(@"%s, fail, %d, %@", __func__, code, msg);}];}}
注意:
推送格式
推送格式示例如下图所示。
?
通用推送规则
对于单聊消息,APNs 推送规则如下,其中昵称是发送方用户昵称,如果未设置昵称,则只显示内容。
昵称:内容
对于群聊消息,APNs 推送规则如下,其中名称展示优先级为消息发送者的
群名片
>昵称
,如果都没有,则不展示。名称(群名):内容
不同类型消息推送规则
APNs 推送内容部分由消息体中各个
Elem
内容组成,不同 Elem
的离线消息展示效果如下表所示。参数 | 说明 |
文本 Elem | 直接显示内容 |
语音 Elem | 显示 [语音] |
文件 Elem | 显示 [文件] |
图片 Elem | 显示 [图片] |
自定义 Elem |
说明:
多 App 互通
如果您需要在多个 App 之间互相接收推送消息,可以将多个 App 中的
SDKAppID
设置为相同值。说明:
自定义角标
默认情况下,当 App 进入后台后,IMSDK 会将当前 IM 未读消息总数设置为角标。
如果想自定义角标,可按照如下步骤设置:
1.1 App 调用 - (void)setAPNSListener:(id<V2TIMAPNSListener>)apnsListener 接口设置监听。
1.2 App 实现 - (uint32_t)onSetAPPUnreadCount 接口,并在内部返回需要自定义的角标。
如果 App 接入了离线推送,当接收到新的离线推送时,App 角标会在基准角标(默认是 IM 未读消息总数,如果自定义了角标,则以自定义角标为准)的基础上加 1 逐条递增。
// 1. 设置监听- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {// 监听推送[V2TIMManager.sharedInstance setAPNSListener:self];// 监听会话的未读数[[V2TIMManager sharedInstance] setConversationListener:self];return YES;}?// 2. 未读数发生变化后保存未读数- (void)onTotalUnreadMessageCountChanged:(UInt64)totalUnreadCount {self.unreadNumber = totalUnreadCount;}?// 3. App 推到后台后上报自定义未读数/** 程序进后台后,自定义 App 的未读数,如果不处理,App 未读数默认为所有会话未读数之和* <pre>** - (uint32_t)onSetAPPUnreadCount {* return 100; // 自定义未读数* }** </pre>*/- (uint32_t)onSetAPPUnreadCount {// 1. 获取自定义的角标uint32_t customBadgeNumber = ...// 2. 加上 IM 的消息未读数customBadgeNumber += self.unreadNumber;// 3. 通过 IMSDK 上报给 IM 服务器return customBadgeNumber;}
自定义推送提示音
iOS 推送提示音
说明:
离线推送声音设置(仅对 iOS 生效), 当 iOSSound = kIOSOfflinePushNoSound,表示接收时不会播放声音。
当 iOSSound = kIOSOfflinePushDefaultSound,表示接收时播放系统声音。
如果要自定义 iOSSound,需要先把语音文件链接进 Xcode 工程,然后把语音文件名(带后缀名)设置给 iOSSound。
V2TIMOfflinePushInfo *pushInfo = [[V2TIMOfflinePushInfo alloc] init];pushInfo.title = @"push title";pushInfo.iOSSound = @"phone_ringing.mp3"; // your voice file's name[[V2TIMManager sharedInstance] sendMessage:msg receiver:receiver groupID:groupID priority:V2TIM_PRIORITY_DEFAULT onlineUserOnly:NO offlinePushInfo:pushInfo progress:nil succ:^{?} fail:^(int code, NSString *msg) {?}];
Android 推送提示音
说明:
离线推送声音设置(仅对 Android 生效, 仅 imsdk 6.1 及以上版本支持) 只有华为和谷歌手机支持设置铃音提示,
小米铃音设置请您参见:小米开放平台 > 服务端Java SDK文档?
如果要自定义 AndroidSound,需要先把语音文件放到 Android 工程的 raw 目录中,然后把语音文件名(不需要后缀名)设置给 AndroidSound。
V2TIMOfflinePushInfo *pushInfo = [[V2TIMOfflinePushInfo alloc] init];pushInfo.title = @"push title";pushInfo.AndroidSound = @"phone_ringing"; // your voice file's name[[V2TIMManager sharedInstance] sendMessage:msg receiver:receiver groupID:groupID priority:V2TIM_PRIORITY_DEFAULT onlineUserOnly:NO offlinePushInfo:pushInfo progress:nil succ:^{?} fail:^(int code, NSString *msg) {?}];
自定义离线推送展示
请在调用 sendMessage 发送消息的时候设置 offlinePushInfo 的
title
和 desc
字段,其中 title
设置后,会在默认的推送内容上多展示 title
内容,desc
设置后,推送内容会变成 desc
内容。V2TIMOfflinePushInfo *info = [[V2TIMOfflinePushInfo alloc] init];info.title = @"Harvy"; // 离线推送展示的标题。info.desc = @"You hava a call invitation."; // 离线推送展示的内容?// receiver 和 groupID 不能同时为空,且同时只能有一个存在[[V2TIMManager sharedInstance] sendMessage:msg receiver:receiver groupID:groupID priority:V2TIM_PRIORITY_DEFAULT onlineUserOnly:NO offlinePushInfo:pushInfo progress:nil succ:^{?} fail:^(int code, NSString *msg) {?}];
?
说明:
您还可以使用 APNs 的
multable-content
字段,结合 NSNotification Service Extension
来自定义显示内容。自定义离线推送点击跳转逻辑
请在调用 sendMessage 发送消息的时候设置 offlinePushInfo 的
ext
字段,当用户收到离线推送启动 App 的时候,可以在 AppDelegate -> didReceiveRemoteNotification
系统回调获取到 ext
字段,然后根据 ext
字段内容跳转到指定的 UI 界面。本文以 “denny 给 vinson 发送消息” 的场景为例。
发送方:denny 需在发送消息时设置推送扩展字段
ext
:// denny在发送消息时设置 offlinePushInfo,并指定 ext 字段V2TIMMessage *msg = [[V2TIMManager sharedInstance] createTextMessage:@"文本消息"];V2TIMOfflinePushInfo *info = [[V2TIMOfflinePushInfo alloc] init];info.ext = @"jump to denny";[[V2TIMManager sharedInstance] sendMessage:msg receiver:@"vinson" groupID:nil priority:V2TIM_PRIORITY_DEFAULTonlineUserOnly:NO offlinePushInfo:info progress:^(uint32_t progress) {} succ:^{} fail:^(int code, NSString *msg) {}];
接收方:vinson 的 App 虽然不在线,但可以接收到 APNS 离线推送,当 vinson 点击推送消息时会启动 App:
// vinson 启动 App 后会收到以下回调- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfofetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {// 解析推送扩展字段 descif ([userInfo[@"ext"] isEqualToString:@"jump to denny"]) {//跳转到和 denny 的聊天界面}}
常见问题
普通消息为什么收不到离线推送?
首先,请检查下 App 的运行环境和证书的环境是否一致,如果不一致,收不到离线推送。
其次,检查下 App 和证书的环境是否为开发环境,如果是开发环境,向苹果申请
deviceToken
可能会失败,生产环境暂时没有发现这个问题,请切换到生产环境测试。自定义消息为什么收不到离线推送?
自定义消息的离线推送和普通消息不太一样,自定义消息的内容我们无法解析,不能确定推送的内容,所以默认不推送,如果您有推送需求,需要您在 sendMessage 的时候设置 offlinePushInfo 的
desc
字段,推送的时候会默认展示 desc
信息。如何关闭离线推送消息的接收?
收不到推送,且后台报错 bad devicetoken?
如果使用的是 Release 环境编译,则
- application:didRegisterForRemoteNotificationsWithDeviceToken:
回调返回的是发布环境的 token,此时 businessID 需要设置生产环境的 证书 ID。如果使用的是 Debug 环境编译,则
- application:didRegisterForRemoteNotificationsWithDeviceToken:
回调返回的是开发环境的 token,此时 businessID 需要设置开发环境的 证书 ID。V2TIMAPNSConfig *confg = [[V2TIMAPNSConfig alloc] init];/* 用户自己到苹果注册开发者证书,在开发者账号中下载并生成证书(p12 文件),将生成的 p12 文件传到腾讯证书管理控制台,控制台会自动生成一个证书 ID,将证书 ID 传入以下 busiId 参数中。*///推送证书 IDconfg.businessID = sdkBusiId;confg.token = self.deviceToken;[[V2TIMManager sharedInstance] setAPNS:confg succ:^{?} fail:^(int code, NSString *msg) {?}];
iOS 开发环境下,注册偶现不返回 deviceToken 或提示 APNs 请求 token 失败?
此问题现象是由于 APNs 服务不稳定导致的,可尝试通过以下方式解决:
给手机插入 SIM 卡后使用4G网络测试。
卸载重装、重启 App、关机重启后测试。
打生产环境的包测试。
更换其它 iOS 系统的手机测试。