iOS

概述

即时通信 IM 的终端用户需要随时都能够得知最新的消息,而由于移动端设备的性能与电量有限,当 App 处于后台时,为了避免维持长连接而导致的过多资源消耗,即时通信 IM 推荐您使用 Apple 提供的系统级推送通道(APNs)来进行消息通知,APNs 相比第三方推送拥有更稳定的系统级长连接,可以做到随时接受推送消息,且资源消耗大幅降低。
注意:
在没有主动退出登录的情况下,应用退后台、手机锁屏、或者应用进程被用户主动杀掉三种场景下,如果想继续接收到 IM 消息提醒,可以接入即时通信 IM 离线推送。
如果应用主动调用 logout 退出登录,或者多端登录被踢下线,即使接入了 IM 离线推送,也收不到离线推送消息。

集成 TUIOfflinePush 跑通离线推送功能

集成 TUIOfflinePush 组件之前,需要先向 Apple 申请 APNs 推送证书,然后 上传推送证书到 IM 控制台 。之后按照如下步骤操作即可快速接入 IM 离线推送。
说明:
如果您想尽可能简单地接入 TUIOfflinePush 组件,您需要使用 TUICore 组件中的 TUILogin 提供的 login/logout 接口登录/登出,此时 TUIOfflinePush 组件会自动感知登录/登出事件。如果您不想使用 TUILogin 提供的接口,可参见 TUIOfflinePush 的 高级用法-自定义登录/登出

步骤1:集成 TUIOfflinePush 组件

1. TUIOfflinePush 组件支持 cocoapods 集成,您需要在 Podfile 中添加组件依赖。
# 防止 TUI 组件里的 *.xcassets 与您项目里面冲突。
install! 'cocoapods', :disable_input_output_paths => true

# TUI 组件依赖了静态库,需要屏蔽如下设置,如果报错,请参见常见问题说明。
# use_frameworks!

# 集成离线推送组件
pod 'TUIOfflinePush'
2. 执行以下命令,安装 TUIOfflinePush 组件。
pod install
如果无法安装 TUIKit 最新版本,执行以下命令更新本地的 CocoaPods 仓库列表。
pod repo update

步骤2:配置推送参数

1. 当您 上传证书到 IM 控制台 后,IM 控制台会为您分配一个证书 ID 。
2. 您需要在 AppDelegate 中,调用宏 TUIOfflinePushCertificateIDForAPNS 设置下证书 ID 即可。
@implementation AppDelegate

#ifdef DEBUG
// 配置开发环境证书
TUIOfflinePushCertificateIDForAPNS(31287)
#else
// 配置生产环境证书
TUIOfflinePushCertificateIDForAPNS(31288)
#endif

@end
说明:
TUIOfflinePushCertificateIDForAPNS 是组件内置的宏定义,您只需要在 AppDelegate@implementation 中的任意位置中调用即可。

步骤3:点击离线推送后自定义跳转

1. 点击通知栏的离线推送后,TUIOfflinePush 组件已支持推送内容的解析。
2. 如果要实现跳转到聊天列表,您只需要在 AppDelegate 中实现 -navigateToTUIChatViewController:groupID: 跳转方法即可。
说明:
TUIOfflinePush 组件默认已经从离线推送中解析出当前推送的 userID 和 groupID。
如果 groupID 不为空,说明当前点击的是群聊离线消息。
如果 groupID 为空且 userID 不为空,说明当前点击的是单聊离线消息。
您需要在 AppDelegate 的 @implementation 中实现 - navigateToTUIChatViewController:groupID: 方法。
以下是示例代码,当点击离线推送后先获取当前的会话页面,然后通过会话页面 push 到聊天页面。您可以按需实现自己的跳转逻辑。
// 统一点击跳转
// 您可以直接拷贝当前的方法名到您的 AppDelegate 中
- (void)navigateToTUIChatViewController:(NSString *)userID groupID:(NSString *)groupID
{
// 示例: 点击推送通知后,首先跳转到会话列表页面,然后再会话列表页跳转到聊天页面
// 1. 获取当前 app 的 tabBarController
// 2. 获取 tabBarController 的 firstObject,也即 ConversationController
// 3. 执行 pushToViewController: 跳转到 ChatViewController
// 跳转到聊天页面后,支持点击左上角的返回按钮回退到主页面

UITabBarController *tab = [self getMainController];
if (![tab isKindOfClass: UITabBarController.class]) {
// 正在登录中
return;
}
if (tab.selectedIndex != 0) {
[tab setSelectedIndex:0];
}
self.window.rootViewController = tab;
UINavigationController *nav = (UINavigationController *)tab.selectedViewController;
if (![nav isKindOfClass:UINavigationController.class]) {
return;
}

UIViewController *vc = nav.viewControllers.firstObject;
if (![vc isKindOfClass:NSClassFromString(@"ConversationController")]) {
return;
}
if ([vc respondsToSelector:NSSelectorFromString(@"pushToChatViewController:userID:")]) {
[vc performSelector:NSSelectorFromString(@"pushToChatViewController:userID:") withObject:groupID withObject:userID];
}
}

高级用法

1. 自定义登录/登出

TUIOfflinePush 默认使用了 TUICore 组件中的 TUILogin 提供的 login/logout 接口。如果您想自己实现 App/IM 的登录,不依赖 TUILogin,您需要在完成登录/登出操作后,手动调用 registerServiceunregisterService 接口。
说明:
如果您使用了 TUILogin 的登录/登出,无需再调用上述两个接口。
// 您登录完成后的回调
- (void)onLoginSuccess
{
// 调用 TUIOfflinePush 组件的登录
[TUIOfflinePushManager.shareManager registerService];
}

// 您登出成功后的回调
- (void)onLogoutSuccess
{
// 调用 TUIOfflinePush 的登出
[TUIOfflinePushManager.shareManager unregisterService];
}

2. 自定义离线内容解析

TUIOfflinePush 默认参与解析了离线推送的内容,并通过 - navigateToTUIChatViewController:groupID: 接口回调给业务层自定义跳转。
如果您想自定义解析离线推送的内容,或者查看收到的离线推送,可以在您的 AppDelegate 中实现 - processTUIOfflinePushNotification: 方法。
说明:
关于方法的返回值
如果返回 YES,那么组件将不再执行默认解析逻辑,完全交由业务层自行处理。
如果返回 NO,组件会继续执行默认解析逻辑,继续回调 - navigateToTUIChatViewController:groupID: 方法。
// 统一收到离线推送
- (BOOL)processTUIOfflinePushNotification:(NSDictionary *)userInfo
{
// 自定义解析收到的 userInfo
NSLog(@">>> 您可以在此处自定义解析, %@", userInfo);

// 如果您不想执行 TUIOfflinePush 默认的解析逻辑,直接返回 YES
// 如果您只是想查看推送的内容,依然依赖 TUIOfflinePush 的默认解析及统一跳转逻辑,直接返回 NO
return NO;
}

常见问题

普通消息为什么收不到离线推送?

首先,请检查下 App 的运行环境和证书的环境是否一致,如果不一致,收不到离线推送。
其次,检查下 App 和证书的环境是否为生产环境。如果是开发环境,向苹果申请 deviceToken 可能会失败,生产环境暂时没有发现这个问题,请切换到生产环境测试。

自定义消息为什么收不到离线推送?

自定义消息的离线推送和普通消息不太一样,自定义消息的内容我们无法解析,不能确定推送的内容,所以默认不推送,如果您有推送需求,需要您在 sendMessage 的时候设置 offlinePushInfodesc字段,推送的时候会默认展示 desc 信息。

如何关闭离线推送消息的接收?

如果您想关闭离线推送消息的接收,可以通过设置 setAPNS 接口的 config 参数为 nil 来实现。该功能从5.6.1200 版本开始支持。

收不到推送,且后台报错 bad devicetoken。

Apple 的 deviceToken 与当前编译环境有关。如果 登录 IMSDK 后上传 deviceToken 到腾讯云 所使用的证书 ID 和 token 不一致,就会报错。
如果使用的是 Release 环境编译,则 - application:didRegisterForRemoteNotificationsWithDeviceToken: 回调返回的是发布环境的 token,此时 businessID 需要设置生产环境的 证书 ID
如果使用的是 Debug 环境编译,则- application:didRegisterForRemoteNotificationsWithDeviceToken: 回调返回的是开发环境的 token,此时 businessID 需要设置开发环境的 证书 ID
V2TIMAPNSConfig *confg = [[V2TIMAPNSConfig alloc] init];
/* 用户自己到苹果注册开发者证书,在开发者帐号中下载并生成证书(p12 文件),将生成的 p12 文件传到腾讯证书管理控制台,控制台会自动生成一个证书 ID,将证书 ID 传入以下 busiId 参数中。*/
//推送证书 ID
confg.businessID = sdkBusiId;
confg.token = self.deviceToken;
[[V2TIMManager sharedInstance] setAPNS:confg succ:^{
NSLog(@"%s, succ, %@", __func__, supportTPNS ? @"TPNS": @"APNS");
} fail:^(int code, NSString *msg) {
NSLog(@"%s, fail, %d, %@", __func__, code, msg);
}];

iOS 开发环境下,注册偶现不返回 deviceToken 或提示 APNs 请求 token 失败?

此问题现象是由于 APNs 服务不稳定导致的,可尝试通过以下方式解决:
1. 给手机插入 SIM 卡后使用4G网络测试。
2. 卸载重装、重启 App、关机重启后测试。
3. 打生产环境的包测试。
4. 更换其它 iOS 系统的手机测试。