本文将介绍如何在 iOS 工程中集成 TIMPush。
前提条件
资源 | 获取位置 | 用途 |
SDKAppID | 腾讯云控制台 > 即时通信 IM > 推送服务 Push > 概览 | 调用 registerPush。创建 Push 应用后,腾讯云控制台会自动创建相同 SDKAppID 的 Chat 应用。 |
Push Key | 腾讯云控制台 > 即时通信 IM > 推送服务 Push > 概览 | 独立 Push 场景作为 registerPush 的 appKey 参数;Chat 场景 appKey 传 nil。控制台展示名 AppKey。 |
Chat Key | 腾讯云控制台 > 即时通信 IM > Chat 应用详情 | 仅 Chat 登录使用。不要把 Chat Key 当作 registerPush 的 appKey 传入。 |
证书 ID | 腾讯云控制台上传 APNs 证书后生成 | 证书 ID 即 businessID,在 AppDelegate 中返回给 TIMPush。 |
App Group ID | Apple Developer Center / Xcode Capabilities | 仅在需要推送触达统计时使用。 |
Push 服务开通状态 | 腾讯云控制台 > 即时通信 IM > 推送服务 Push > 概览 | 创建 Push 应用不等于已开通 Push 服务,请确认 Push 服务已开通。 |
TIMPush iOS VERSION | VERSION 是 Podfile 中 TIMPush 依赖的版本号占位符,例如 8.9.7537、8.8.7357、8.7.7201 等,请以实际接入版本为准。 |
本文示例中的
VERSION、SDKAppID、AppKey、businessID、App Group ID 均为占位符,请勿在代码仓库中提交真实密钥。集成 TIMPush
请按顺序完成本文配置。
主 App target 集成
TIMPush 通过 CocoaPods 集成。请在
Podfile 中为主 App target 添加 TIMPush 依赖。Podfile 示例:target 'YourAppName' douse_frameworks!use_modular_headers!pod 'TIMPush', 'VERSION'end
如果您的工程已显式依赖 IMSDK,请保持
TIMPush 与 TXIMSDK_Plus_iOS_XCFramework 版本一致,否则执行 pod install 时可能出现依赖冲突:target 'YourAppName' douse_frameworks!use_modular_headers!pod 'TXIMSDK_Plus_iOS_XCFramework', 'VERSION'pod 'TIMPush', 'VERSION'end
保存
Podfile 后,在工程目录执行:pod install
如果无法安装 TIMPush 最新版本,可先执行
pod repo update 更新本地 CocoaPods 仓库列表后再 pod install。仓库更新可能耗时较长且影响依赖解析结果,建议在确认版本后执行。验证:
pod install 输出 Installing TIMPush (VERSION),工程内可以 import TIMPush / #import <TIMPush/TIMPushManager.h> 且编译通过。配置推送参数
配置 businessID
businessID 是腾讯云控制台上传 APNs 证书后生成的证书 ID。TIMPush 通过该 ID 识别当前 App 使用控制台中的哪一份 iOS 推送证书。请在 AppDelegate 中实现
businessID 方法,并返回控制台生成的证书 ID。注意,Swift 端需要添加 @objc 标注。// 在 AppDelegate.swift 中添加import TIMPush@objc func businessID() -> Int32 {// TODO: Replace <#YOUR_BUSINESS_ID#> with the certificate ID generated in the Tencent Cloud console.return <#YOUR_BUSINESS_ID#>}
// 在 AppDelegate.m 中添加#import <TIMPush/TIMPushManager.h>- (int)businessID {// TODO: Replace <#YOUR_BUSINESS_ID#> with the certificate ID generated in the Tencent Cloud console.return <#YOUR_BUSINESS_ID#>;}
如果
businessID 填写错误,部分 APNs 配置链路日志仍可能显示成功,因此还需要继续检查 token 上传和测试消息结果。验证:在
businessID 方法内打 log,启动 App 后观察该方法是否被调用,且返回值与腾讯云控制台显示的证书 ID 一致。配置 applicationGroupID(可选)
如果您需要统计推送触达率,需要配置 App Group ID,该 ID 应与您在厂商配置阶段准备的 App Group ID 保持一致。如果不需要推送触达统计,可以跳过本节。
// 在 AppDelegate.swift 中添加import TIMPush@objc func applicationGroupID() -> String {// TODO: Replace <#YOUR_APP_GROUP_ID#> with the App Group ID configured in Apple Developer Center / Xcode.return "group.<#YOUR_APP_GROUP_ID#>"}
// 在 AppDelegate.m 中添加#import <TIMPush/TIMPushManager.h>- (NSString *)applicationGroupID {// TODO: Replace <#YOUR_APP_GROUP_ID#> with the App Group ID configured in Apple Developer Center / Xcode.return @"group.<#YOUR_APP_GROUP_ID#>";}
验证:在 Apple Developer Center 和 Xcode 中确认主 App 与 Notification Service Extension 已绑定到同一个 App Group ID,且 AppDelegate 返回的
group.<#YOUR_APP_GROUP_ID#> 与之一致。注册推送
registerPush 用于向 TIMPush 注册推送服务,并触发 APNs 设备 token 上传。每次 App 冷启动后,需要在合适时机调用一次。接入场景与调用时机
TIMPush 支持三种接入场景,场景决定了推送标识、
registerPush 调用时机以及 appKey 取值。接入场景 | 适用说明 | 推送标识 | 调用时机 | appKey 取值 |
Push | 仅使用推送通知能力(如营销推送、活动通知),不接入 Chat。 | registrationID:调用 registerPush 时由 SDK 生成的设备标识,卸载重装可能变化。 | App 完成必要初始化、用户同意隐私政策并获得通知授权后调用 registerPush。 | Push Key |
IM SDK + Push | 同时集成 IM SDK + TIMPush。 | userID:Chat 登录后 SDK 自动将推送标识切换为当前登录的 userID。 | IM SDK 登录成功回调中调用 registerPush。 | nil |
TUIKit + Push | 同时集成 TUIKit + TIMPush。 | userID:Chat 登录后 SDK 自动将推送标识切换为当前登录的 userID。 | 建议在 TUILogin.login 成功回调中调用。 | nil |
警告:
Chat / TUIKit 场景下
registerPush 的 appKey 必须传 nil,由 Chat 登录态接管推送标识。不要把 Push Key 或 Chat Key 当作 appKey 传入;同样不要在 Chat 登录前调用 registerPush,否则 token 可能上传失败,控制台排查工具显示「没有上传成功 token」。调用 registerPush
请在用户同意隐私政策后再调用
registerPush。建议放在 AppDelegate 的初始化流程或业务侧统一的登录后回调中。按接入场景选择对应示例:App 完成初始化、获得用户通知授权并具备
SDKAppID 和 Push Key 后调用 registerPush,appKey 填 Push Key。import TIMPushfunc registerTIMPush() {// TODO: Replace 0 with your SDKAppID.let sdkAppID: Int32 = 0// TODO: Replace "<#YOUR_PUSH_KEY#>" with your Push Key.let appKey = "<#YOUR_PUSH_KEY#>"TIMPushManager.registerPush(sdkAppID, appKey: appKey, succ: { deviceToken inprint(">>>>> TIMPush register success")}, fail: { code, desc inprint(">>>>> TIMPush register failed, code:\\(code), desc:\\(desc)")})}
#import <TIMPush/TIMPushManager.h>- (void)registerTIMPush {// TODO: Replace 0 with your SDKAppID.int sdkAppID = 0;// TODO: Replace @"<#YOUR_PUSH_KEY#>" with your Push Key.NSString *appKey = @"<#YOUR_PUSH_KEY#>";[TIMPushManager registerPush:sdkAppIDappKey:appKeysucc:^(NSData * _Nonnull deviceToken) {NSLog(@">>>>> TIMPush register success");} fail:^(int code, NSString * _Nonnull desc) {NSLog(@">>>>> TIMPush register failed, code:%d, desc:%@", code, desc);}];}
必须在 Chat 登录成功回调(或
TUILogin.login 成功回调)中调用 registerPush,且 appKey 必须传 nil。import TIMPushimport ImSDK_Plusfunc loginIMAndRegisterPush() {// TODO: Replace 0 with your SDKAppID.let sdkAppID: Int32 = 0let userID = "<#YOUR_USER_ID#>"let userSig = "<#YOUR_USER_SIG#>"V2TIMManager.sharedInstance().login(userID, userSig: userSig) {// Chat login succeeded. Register TIMPush right after.TIMPushManager.registerPush(sdkAppID, appKey: nil, succ: { deviceToken inprint(">>>>> TIMPush register success")}, fail: { code, desc inprint(">>>>> TIMPush register failed, code:\\(code), desc:\\(desc)")})} fail: { code, msg inprint(">>>>> Chat login failed, code:\\(code), msg:\\(msg ?? "")")}}
#import <TIMPush/TIMPushManager.h>#import <ImSDK_Plus/ImSDK_Plus.h>- (void)loginIMAndRegisterPush {// TODO: Replace 0 with your SDKAppID.int sdkAppID = 0;NSString *userID = @"<#YOUR_USER_ID#>";NSString *userSig = @"<#YOUR_USER_SIG#>";[[V2TIMManager sharedInstance] login:userID userSig:userSig succ:^{// Chat login succeeded. Register TIMPush right after.[TIMPushManager registerPush:sdkAppIDappKey:nilsucc:^(NSData * _Nonnull deviceToken) {NSLog(@">>>>> TIMPush register success");} fail:^(int code, NSString * _Nonnull desc) {NSLog(@">>>>> TIMPush register failed, code:%d, desc:%@", code, desc);}];} fail:^(int code, NSString *msg) {NSLog(@">>>>> Chat login failed, code:%d, msg:%@", code, msg);}];}
验证:
1.
registerPush 的 succ 回调被触发;登录腾讯云控制台 > 即时通信 IM > 推送服务 Push 排查工具,输入 registrationID 或 userID,确认 token 已上传2. 如触发
fail,可按 错误码 查询 code 含义。registrationID 与 userID 说明(可选读)
名称 | 含义 | 使用场景 |
registrationID | TIMPush 默认在调用 registerPush 时生成的设备推送标识,卸载重装后可能变化。 | 独立 Push 场景。控制台或服务端 REST API 可指定 registrationID 下发推送。 |
userID | Chat 登录时使用的业务用户 ID。 | Chat / TUIKit 离线推送场景。Chat 登录成功后调用 registerPush(_, appKey: nil, ...),推送标识切换到当前 userID。 |
getRegistrationID: | + (void)getRegistrationID:(TIMPushValueCallback)callback,回调返回当前注册成功的推送 ID。 | Chat 场景下回调返回 Chat 登录的 userID;独立 Push 场景下返回 SDK 生成的设备标识。控制台或服务端发送测试消息时,使用该值定位设备。请在 registerPush 成功回调之后再调用 getRegistrationID:,否则可能拿不到值。 |
setRegistrationID:callback: | + (void)setRegistrationID:(NSString *)registrationID callback:(TIMPushCallback)callback,自定义推送 ID 的接口,必须在 registerPush 之前调用。 | 混用场景中如需自定义 ID,自定义值必须与 Chat 登录使用的 userID 完全一致,否则可能产生账号互踢,导致推送不稳定。 |
配置消息触达统计(可选)
仅当您需要统计推送触达率时,才需要完成本节配置;如果只需要接收普通离线推送,可跳过本节。
完整流程包含:
1. 在 Xcode 中创建并配置 Notification Service Extension target
2. 确保 APNs payload 开启
mutable-content3. 在 Extension 中调用 TIMPush 处理通知。
创建并配置 Notification Service Extension target
1. 在 Xcode 中选择 File > New > Target。
2. 选择 Notification Service Extension。
3. 输入 Extension 名称。
4. 创建完成后,确认工程中出现新的 Extension target。
5. 在 Extension target 的 Signing & Capabilities 中配置与主 App 相同的 App Groups。
6. 在
Podfile 中为 Extension target 添加 TIMPush 依赖:target 'YourNotificationServiceExtensionTarget' douse_frameworks!use_modular_headers!pod 'TIMPush', 'VERSION'end
7. 执行
pod install。注意:
Extension target 是独立 target,不能复用主 App target 的 Pod 依赖。若未在 Extension target 中添加
TIMPush,在 NotificationService 中 import TIMPush 或 #import <TIMPush/TIMPushManager.h> 会失败。开启 mutable-content
mutable-content 是 APNs payload 字段,由发送方在推送内容中设置。只有 payload 中开启 mutable-content,iOS 10 及以上系统才会在送达前调用 Notification Service Extension,触达统计才能生效。请确保通过以下任一方式开启
mutable-content:腾讯云控制台推送:在控制台推送测试或发送页面,勾选
mutable-content 相关选项。服务端 REST API:在 APNs payload 中设置
"mutable-content": 1。Chat SDK 发送:以 Chat SDK 离线推送配置文档为准。
在 Extension 中处理通知
请将
group.<#YOUR_APP_GROUP_ID#> 替换为您在 Apple Developer Center 和 Xcode 中配置的 App Group ID。主 App 和 Extension 必须使用同一个 App Group ID。以下 Swift 写法基于 OC 接口自动桥接,参数命名可能随 SDK 版本调整,请以 SDK 头文件 TIMPushManager.h 为准。// 在 NotificationService.swift 中添加import UserNotificationsimport TIMPushclass NotificationService: UNNotificationServiceExtension {var contentHandler: ((UNNotificationContent) -> Void)?var bestAttemptContent: UNMutableNotificationContent?override func didReceive(_ request: UNNotificationRequest,withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {self.contentHandler = contentHandlerself.bestAttemptContent = request.content.mutableCopy() as? UNMutableNotificationContentlet appGroupID = "group.<#YOUR_APP_GROUP_ID#>"TIMPushManager.handleNotificationServiceRequest(request: request, appGroupID: appGroupID) { [weak self] content inguard let self = self else {contentHandler(content)return}self.bestAttemptContent = content.mutableCopy() as? UNMutableNotificationContentcontentHandler(self.bestAttemptContent ?? content)}}override func serviceExtensionTimeWillExpire() {if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {contentHandler(bestAttemptContent)}}}
// 在 NotificationService.m 中添加#import "NotificationService.h"#import <TIMPush/TIMPushManager.h>@implementation NotificationService- (void)didReceiveNotificationRequest:(UNNotificationRequest *)requestwithContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {NSString *appGroupID = @"group.<#YOUR_APP_GROUP_ID#>";[TIMPushManager handleNotificationServiceRequest:requestappGroupID:appGroupIDcallback:^(UNNotificationContent *content) {contentHandler(content);}];}@end
验证:发送一条带
mutable-content 的测试推送,在 didReceive 内打断点或 log 确认 Extension 被调用;后续在腾讯云控制台触达统计页面应能看到上报数据。测试推送
完成上述集成步骤后,需要通过发送测试消息验证整体链路是否打通。发送消息前请确认:
1. App 已获得系统通知权限;
2. Xcode 主 App target 已开启 Push Notifications,AppDelegate 已正确返回
businessID;3. App 已置于后台或杀掉进程(前台时离线推送可能不触发)。
发送测试消息可以采用下面几种方法:
仅集成 TIMPush 的用户,建议优先使用腾讯云控制台接入测试能力验证离线推送。
操作路径:腾讯云控制台 > 即时通信 IM > 推送服务 Push > 接入测试。在接入测试页面,可以指定
registrationID 或 userID 发送离线推送测试。验证:App 置于后台后发送测试消息,设备能收到离线推送通知。如果收不到,请参见下文「收不到推送排障流程」逐步排查。
如果您的项目已接入 IMSDK,可在调用
sendMessage 发送消息时,通过 V2TIMOfflinePushInfo 设置离线推送参数,再交由 V2TIMManager 发送消息。具体方法签名以您接入的 IMSDK 版本头文件为准。import ImSDK_Pluslet pushInfo = V2TIMOfflinePushInfo()pushInfo.title = "推送标题"pushInfo.desc = "推送内容"pushInfo.ext = "{\\"action\\":\\"open_chat\\",\\"conversationID\\":\\"c2c_userA\\"}"let message = V2TIMManager.sharedInstance().createTextMessage("Hello TIMPush")V2TIMManager.sharedInstance().send(message,receiver: "<#TARGET_USER_ID#>",groupID: nil,priority: V2TIM_PRIORITY_DEFAULT,onlineUserOnly: false,offlinePushInfo: pushInfo,progress: nil,succ: { msg inprint(">>>>> sendMessage success, msgID = \\(msg?.msgID ?? "")")},fail: { code, desc inprint(">>>>> sendMessage failed, code:\\(code), desc:\\(desc ?? "")")})
#import <ImSDK_Plus/ImSDK_Plus.h>V2TIMOfflinePushInfo *pushInfo = [[V2TIMOfflinePushInfo alloc] init];pushInfo.title = @"推送标题";pushInfo.desc = @"推送内容";pushInfo.ext = @"{\\"action\\":\\"open_chat\\",\\"conversationID\\":\\"c2c_userA\\"}";V2TIMMessage *message = [[V2TIMManager sharedInstance] createTextMessage:@"Hello TIMPush"];[[V2TIMManager sharedInstance] sendMessage:messagereceiver:@"<#TARGET_USER_ID#>"groupID:nilpriority:V2TIM_PRIORITY_DEFAULTonlineUserOnly:NOofflinePushInfo:pushInfoprogress:nilsucc:^{NSLog(@">>>>> sendMessage success");} fail:^(int code, NSString *desc) {NSLog(@">>>>> sendMessage failed, code:%d, desc:%@", code, desc);}];
sendMessage 属于 IMSDK 消息发送能力。仅集成 TIMPush 的用户不需要为了验证离线推送而额外接入完整 Chat 初始化、登录和消息发送流程。处理通知点击跳转
通知点击跳转需要三步配合完成:控制台配置点击动作、发送推送时携带跳转参数、客户端注册监听并解析参数。三步缺一则跳转不生效。
配置控制台点击动作
在腾讯云控制台配置推送证书的「点击后续动作」,勾选「打开应用内指定页面」:
操作路径:腾讯云控制台 > 即时通信 IM > 推送服务 Push > 推送设置 > 厂商配置 > iOS > 对应 APNs 证书 > 编辑 > 点击后续动作 > 打开应用内指定页面。
发送推送时携带 ext
发送离线推送时,通过
ext 字段携带跳转所需的业务信息(如目标页面、会话 ID 等)。ext 是一个字符串,建议使用 JSON 格式便于客户端解析。通过 REST API 发送推送时,在请求体的
Ext 字段中设置 JSON 字符串:{"MsgBody": [],"OfflinePushInfo": {"PushFlag": 0,"Title": "离线推送标题","Desc": "离线推送内容","Ext": "{\\"action\\":\\"open_chat\\",\\"conversationID\\":\\"c2c_userA\\"}"}}
控制台接入测试页面同样支持设置
Ext 字段,填入 JSON 字符串即可。import ImSDK_Pluslet pushInfo = V2TIMOfflinePushInfo()pushInfo.title = "推送标题"pushInfo.desc = "推送内容"pushInfo.ext = "{\\"action\\":\\"open_chat\\",\\"conversationID\\":\\"c2c_userA\\"}"
#import <ImSDK_Plus/ImSDK_Plus.h>V2TIMOfflinePushInfo *pushInfo = [[V2TIMOfflinePushInfo alloc] init];pushInfo.title = @"推送标题";pushInfo.desc = @"推送内容";pushInfo.ext = @"{\\"action\\":\\"open_chat\\",\\"conversationID\\":\\"c2c_userA\\"}";
客户端注册监听并解析 ext
请在 AppDelegate 中遵循
TIMPushListener 协议,调用 addPushListener: 注册监听器,并在 onNotificationClicked 中解析 ext。// 在 AppDelegate.swift 中遵循 TIMPushListener 并注册 listener:import UIKitimport TIMPush@mainclass AppDelegate: UIResponder, UIApplicationDelegate, TIMPushListener {func application(_ application: UIApplication,didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {TIMPushManager.addPushListener(listener: self)return true}// MARK: - TIMPushListener@objc func onNotificationClicked(_ ext: String) {print(">>>>> TIMPush notification clicked, ext:\\(ext)")// Parse ext and navigate to the target page.}@objc func onRecvPushMessage(_ message: TIMPushMessage) {print(">>>>> TIMPush received push message")}@objc func onRevokePushMessage(_ messageID: String) {print(">>>>> TIMPush revoked push message:\\(messageID)")}}
// 在 AppDelegate.h 中声明协议:#import <TIMPush/TIMPushManager.h>@interface AppDelegate : UIResponder <UIApplicationDelegate, TIMPushListener>@end// 在 AppDelegate.m 中注册 listener 并处理回调:#import <TIMPush/TIMPushManager.h>- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {[TIMPushManager addPushListener:self];return YES;}// MARK: - TIMPushListener- (void)onNotificationClicked:(NSString *)ext {NSLog(@">>>>> TIMPush notification clicked, ext:%@", ext);// Parse ext and navigate to the target page.}- (void)onRecvPushMessage:(TIMPushMessage *)message {NSLog(@">>>>> TIMPush received push message");}- (void)onRevokePushMessage:(NSString *)messageID {NSLog(@">>>>> TIMPush revoked push message:%@", messageID);}
onRecvPushMessage 回调中传入的 TIMPushMessage 对象提供以下字段,便于业务读取离线推送内容:字段 | 类型 | 含义 |
title | NSString * | 离线推送标题。 |
desc | NSString * | 离线推送内容。 |
ext | NSString * | 离线推送透传内容,业务自定义字段,常用于点击跳转。 |
messageID | NSString * | 消息唯一标识 ID,可用于去重或 onRevokePushMessage 撤回匹配。 |
收不到推送排障流程
如果发送测试消息后设备没有收到推送,请按以下流程逐步排查。每一步确认通过后再进入下一步,可以快速定位问题所在环节。不要只凭单条 APNs 配置成功日志判断接入成功;运行 App 后可过滤
TIMPush 关键字观察整体日志。步骤1:确认 APNs 权限和 Push Notifications 能力
App 是否已获得系统通知权限。
Xcode 主 App target 的 Signing & Capabilities 是否已开启 Push Notifications。
Apple Developer Center 对应 App ID 的 Capabilities 是否已勾选 Push Notifications。
通过后,进入第二步。
步骤2:确认 registerPush 是否成功
检查
registerPush 回调结果:succ 已触发:注册成功,进入第三步。fail 触发:注册失败。结合下文「错误码」表查询 code 含义,并按返回的 desc 排查 SDKAppID、appKey 取值(独立 Push 用 Push Key,Chat / TUIKit 用 nil)以及调用时机。步骤3:确认控制台排查工具能查到 token
在腾讯云控制台 即时通信 IM > 推送服务 Push 的排查工具中输入
registrationID 或 userID:能查到设备且 token 正常:进入第四步。
查不到设备 / 显示「没有上传成功 token」:常见原因是 Chat 场景未在 Chat 登录成功后调用
registerPush,或调用顺序错误。请重新核对调用时机。步骤4:确认 businessID / Bundle ID / APNs 环境匹配
控制台显示「不允许向该主题推送」或仅部分设备能收到推送时,通常是这一层出了问题:
Xcode 主 App target 的 Bundle Identifier 是否与 Apple App ID 一致。
腾讯云控制台上传的 APNs 证书是否绑定同一个 Bundle ID。
AppDelegate 返回的
businessID 是否为该 Bundle ID 对应的证书 ID。APNs 环境(开发 / 生产)是否与 App 安装包环境匹配。
p8 / p12 证书是否上传到正确应用。
步骤5:确认 Chat / TUIKit 场景登录顺序
仅 Chat / TUIKit 场景需要检查:
registerPush 是否在 Chat 登录成功回调(或 TUILogin.login 成功回调)之后调用。appKey 参数是否传 nil,而不是 Push Key 或 Chat Key。自定义
registrationID 是否与 Chat 登录使用的 userID 完全一致(不一致会产生账号互踢)。步骤6:确认测试消息发送目标和设备状态
测试消息发送给的
registrationID 或 userID 是否与 getRegistrationID: 返回值一致。App 是否处于后台或已杀进程(前台时离线推送可能不触发)。
设备是否处于可接收通知状态(勿扰模式、通知权限等)。
推送服务是否到期(试用到期后自动停止服务)。
仍无法解决