iOS

最近更新时间:2026-06-22 11:46:01

我的收藏
本文将介绍如何在 iOS 工程中集成 TIMPush。

前提条件

请确认已 开通 Push 服务 并完成 iOS 厂商配置,获取到了下列信息:
资源
获取位置
用途
SDKAppID
腾讯云控制台 > 即时通信 IM > 推送服务 Push > 概览
调用 registerPush。创建 Push 应用后,腾讯云控制台会自动创建相同 SDKAppID 的 Chat 应用。
Push Key
腾讯云控制台 > 即时通信 IM > 推送服务 Push > 概览 > 客户端密钥
独立 Push 场景下,作为 registerPushappKey 参数。
Chat Key
腾讯云控制台 > 即时通信 IM > 消息服务 Chat > 概览 > 密钥
Chat 登录使用。不要把 Chat Key 当作 registerPushappKey 传入。
证书 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.75378.8.73578.7.7201 等,请以实际接入版本为准。
本文示例中的 VERSIONSDKAppIDAppKeybusinessID、App Group ID 均为占位符,请勿在代码仓库中提交真实密钥。

集成 TIMPush

请按顺序完成本文配置。

主 App target 集成

TIMPush 通过 CocoaPods 集成。请在 Podfile 中为主 App target 添加 TIMPush 依赖。Podfile 示例:
target 'YourAppName' do
use_frameworks!
use_modular_headers!

pod 'TIMPush', 'VERSION'
end
如果您的工程已显式依赖 IMSDK,请保持 TIMPushTXIMSDK_Plus_iOS_XCFramework 版本一致,否则执行 pod install 时可能出现依赖冲突:
target 'YourAppName' do
use_frameworks!
use_modular_headers!

pod 'TXIMSDK_Plus_iOS_XCFramework', 'VERSION'
pod 'TIMPush', 'VERSION'
end
保存 Podfile 后,在工程目录执行:
pod install
如果无法安装 TIMPush 最新版本,可先执行 pod repo update 更新本地 CocoaPods 仓库列表后再 pod install。仓库更新可能耗时较长且影响依赖解析结果,建议在确认版本后执行。
注意:
无论 Swift 还是 Objective-C 项目,都请集成 TXIMSDK_Plus_iOS_XCFramework。
验证pod install 输出 Installing TIMPush (VERSION),工程内可以 import TIMPush / #import <TIMPush/TIMPushManager.h> 且编译通过。

配置推送参数

配置 businessID

businessID 是腾讯云控制台上传 APNs 证书后生成的证书 ID。TIMPush 通过该 ID 识别当前 App 使用控制台中的哪一份 iOS 推送证书。
请在 AppDelegate 中实现 businessID() 方法,并返回控制台生成的证书 ID。
Swift
Objective-C
// 在 AppDelegate.swift 中添加
import TIMPush

// 注意:Swift 端需要添加 @objc 标注
@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 方法内打 log,启动 App 后观察该方法是否被调用,且返回值与腾讯云控制台显示的证书 ID 一致。

配置 applicationGroupID(可选)

如果您需要统计推送触达率,需要配置 App Group ID 并实现 applicationGroupID() 方法 ,该 ID 应与您在厂商配置阶段准备的 App Group ID 保持一致。如果不需要推送触达统计,可以跳过本节。
Swift
Objective-C
// 在 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

registerPush 用于注册当前设备的推送 token。注册成功后,后台才能根据 registrationIDuserID 向该设备下发离线推送。其中 appKey 的取值会影响注册方式:
appKey = Push Key:注册 TIMPush 独立推送能力。Push Key 为腾讯云控制台 > 即时通信 IM > 推送服务 Push > 概览中的客户端密钥。
appKey = nil:复用 IM 登录态注册推送,必须在 IM login 成功后调用。
请先根据业务场景确认调用顺序:
场景
调用顺序
后台可用推送标识
说明
仅使用 TIMPush
App 每次冷启动后调用
registerPush(appKey = Push Key)
registrationID
适用于营销 / 活动 / 通知推送,不接入 IM SDK。
IM SDK + TIMPush
先注册 Push 再登录
registerPush(appKey = Push Key)
→ IM login
登录前:registrationID
登录后:registrationID + userID
适用于希望用户未登录时也能收到营销推送的场景。
IM SDK + TIMPush
先登录再注册 Push
IM login
registerPush(appKey = null)
登录后:userID
注册后:userID + registrationID
此时 registrationID = userID
适用于希望用户登录后能收到 Chat 离线消息和营销推送的场景。
注意:
如果用户退出 IM SDK,同时集成了 IM SDK + TIMPush 的场景下已建立的 userIDregistrationID 推送关系都会失效,需要重新完成对应注册。
Chat 应用的密钥仅用于 IM 登录,不能作为 registerPushappKey
App 冷启动注册 TIMPush(appKey 传 Push Key)
IM 登录后注册推送(appKey 传 null)
App 冷启动、用户同意隐私政策后调用 registerPush(appKey = Push Key)
Swift
Objective-C
import TIMPush

func 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 in
print(">>>>> TIMPush register success")
}, fail: { code, desc in
print(">>>>> 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:sdkAppID
appKey:appKey
succ:^(NSData * _Nonnull deviceToken) {
NSLog(@">>>>> TIMPush register success");
} fail:^(int code, NSString * _Nonnull desc) {
NSLog(@">>>>> TIMPush register failed, code:%d, desc:%@", code, desc);
}];
}

请在 IM login 成功回调中调用 registerPush(appKey = nil)
Swift
Objective-C
import TIMPush
import ImSDK_Plus

func loginIMAndRegisterPush() {
// TODO: Replace 0 with your SDKAppID.
let sdkAppID: Int32 = 0
let userID = "<#YOUR_USER_ID#>"
let userSig = "<#YOUR_USER_SIG#>"

V2TIMManager.sharedInstance().login(userID: userID, userSig: userSig) {
// Swift 桥接下 appKey 类型为非可选 String,IM 登录场景传空字符串等同于 OC 接口传 nil。
TIMPushManager.registerPush(sdkAppID, appKey: "", succ: { deviceToken in
print(">>>>> TIMPush register success")
}, fail: { code, desc in
print(">>>>> TIMPush register failed, code:\\(code), desc:\\(desc)")
})
} fail: { code, msg in
print(">>>>> IM 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:^{
[TIMPushManager registerPush:sdkAppID
appKey:nil
succ:^(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(@">>>>> IM login failed, code:%d, msg:%@", code, msg);
}];
}Objective-C
验证
1. registerPushonSuccess 回调被触发。
2. 登录腾讯云控制台 > 即时通信 IM > 推送服务 Push > 推送排查,按当前场景输入 registrationIDuserID,确认 token 已上传。
3. 如触发 onError,可按 错误码 查询 code 含义。

自定义 registrationID(可选)

如需自定义推送标识(如使用业务侧用户 ID),可在 registerPush 调用 setRegistrationID
注意:
混用场景下,自定义 registrationID 必须与 IM 登录使用的 userID 完全一致,否则会产生账号互踢导致推送丢失。

配置消息触达统计(可选)

仅当您需要统计推送触达率时,才需要完成本节配置;如果只需要接收普通离线推送,可跳过本节。
完整流程包含:
1. 在 Xcode 中创建并配置 Notification Service Extension target
2. 确保 APNs payload 开启 mutable-content
3. 在 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' do
use_frameworks!
use_modular_headers!

pod 'TIMPush', 'VERSION'
end
7. 执行 pod install
注意:
Extension target 是独立 target,不能复用主 App target 的 Pod 依赖。若未在 Extension target 中添加 TIMPush,在 NotificationServiceimport 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 为准。
Swift
Objective-C
// 在 NotificationService.swift 中添加

import UserNotifications
import TIMPush

class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?

override func didReceive(
_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
) {
self.contentHandler = contentHandler
self.bestAttemptContent = request.content.mutableCopy() as? UNMutableNotificationContent

let appGroupID = "group.<#YOUR_APP_GROUP_ID#>"
TIMPushManager.handleNotificationServiceRequest(request: request, appGroupID: appGroupID) { [weak self] content in
guard let self = self else {
contentHandler(content)
return
}
self.bestAttemptContent = content.mutableCopy() as? UNMutableNotificationContent
contentHandler(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 *)request
withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
NSString *appGroupID = @"group.<#YOUR_APP_GROUP_ID#>";

[TIMPushManager handleNotificationServiceRequest:request
appGroupID:appGroupID
callback:^(UNNotificationContent *content) {
contentHandler(content);
}];
}

@end
验证:发送一条带 mutable-content 的测试推送,在 didReceive 内打断点或 log 确认 Extension 被调用;后续在腾讯云控制台触达统计页面应能看到上报数据。

测试推送

完成上述集成步骤后,需要通过发送测试消息验证整体链路是否打通。发送消息前请确认:
1. App 已获得系统通知权限;
2. Xcode 主 App target 已开启 Push Notifications,AppDelegate 已正确返回 businessID
3. App 已置于后台或杀掉进程(前台时离线推送可能不触发)。
发送测试消息可以采用下面几种方法:
控制台发送
REST API 发送
SDK API 发送
仅集成 TIMPush 的用户,建议优先使用腾讯云控制台接入测试能力验证离线推送。
操作路径:腾讯云控制台 > 即时通信 IM > 推送服务 Push > 接入测试。在接入测试页面,可以指定 registrationIDuserID 发送离线推送测试。
如果需要通过服务端发送推送,可参考 REST API 接口 - 发起全员/标签推送
如果您的项目已接入 IM SDK,可在调用 sendMessage 发送消息时,通过 V2TIMOfflinePushInfo 设置离线推送参数,再交由 V2TIMManager 发送消息。具体方法签名以您接入的 IMSDK 版本头文件为准。
Swift
Objective-C
import ImSDK_Plus

let pushInfo = V2TIMOfflinePushInfo()
pushInfo.title = "推送标题"
pushInfo.desc = "推送内容"
pushInfo.ext = "{\\"action\\":\\"open_chat\\",\\"conversationID\\":\\"c2c_userA\\"}"

let message = V2TIMManager.sharedInstance().createTextMessage("Hello TIMPush")

V2TIMManager.sharedInstance().sendMessage(
message: message,
receiver: "<#TARGET_USER_ID#>",
groupID: nil,
priority: V2TIM_PRIORITY_DEFAULT,
onlineUserOnly: false,
offlinePushInfo: pushInfo,
progress: nil,
succ: { msg in
print(">>>>> sendMessage success, msgID = \\(msg?.msgID ?? "")")
},
fail: { code, desc in
print(">>>>> 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:message
receiver:@"<#TARGET_USER_ID#>"
groupID:nil
priority:V2TIM_PRIORITY_DEFAULT
onlineUserOnly:NO
offlinePushInfo:pushInfo
progress:nil
succ:^{
NSLog(@">>>>> sendMessage success");
} fail:^(int code, NSString *desc) {
NSLog(@">>>>> sendMessage failed, code:%d, desc:%@", code, desc);
}];
sendMessage 属于 IMSDK 消息发送能力。仅集成 TIMPush 的用户不需要为了验证离线推送而额外接入完整 Chat 初始化、登录和消息发送流程。
验证:App 置于后台后发送测试消息,设备能收到离线推送通知。如果手机通知栏开启权限,通知栏会弹出离线推送消息弹框。如果收不到,请参见下文「收不到推送排障流程」逐步排查。

处理通知点击跳转

通知点击跳转需要三步配合完成:控制台配置点击动作、发送推送时携带跳转参数、客户端注册监听并解析参数。三步缺一则跳转不生效。

配置控制台点击动作

在腾讯云控制台配置推送证书的「点击后续动作」,勾选「打开应用内指定页面」:
操作路径:腾讯云控制台 > 即时通信 IM > 推送服务 Push > 推送设置 > 厂商配置 > iOS > 对应 APNs 证书 > 编辑 > 点击后续动作 > 打开应用内指定页面。

发送推送时携带 ext

发送离线推送时,通过 ext 字段携带跳转所需的业务信息(如目标页面、会话 ID 等)。ext 是一个字符串,结构由业务自定义,推荐使用 JSON 格式便于客户端解析。下文示例统一使用如下结构演示:
// conversationType 为 1 表示单聊(conversationID 填消息发送方 userID),为 2 表示群聊(conversationID 填 groupID)。
{"conversationID":"user_A","conversationType":1}
REST API 发送
SDK API 发送
通过 REST API 发送推送时,在请求体的 Ext 字段中设置 JSON 字符串:
{
"MsgBody": [],
"OfflinePushInfo": {
"PushFlag": 0,
"Title": "离线推送标题",
"Desc": "离线推送内容",
"Ext": "{\\"conversationID\\":\\"user_A\\",\\"conversationType\\":1}"
}
}
控制台接入测试页面同样支持设置 Ext 字段,填入 JSON 字符串即可。
通过 V2TIMOfflinePushInfoext 属性携带跳转参数,再随消息一起发送。
如果你接入了 TUIKit,TUIKit 内置的消息发送链路会自动用 OfflinePushExtInfo 组装 ext,无需手动设置。下方示例适用于自行调用 IM SDK 发送消息的场景。
Swift
Objective-C
import ImSDK_Plus

let pushInfo = V2TIMOfflinePushInfo()
pushInfo.title = "推送标题"
pushInfo.desc = "推送内容"
// TODO: ext 由业务自定义,按需替换为您的目标页面、会话 ID 等参数。
pushInfo.ext = "{\\"conversationID\\":\\"user_A\\",\\"conversationType\\":1}"

let message = V2TIMManager.sharedInstance().createTextMessage("Hello TIMPush")
V2TIMManager.sharedInstance().sendMessage(
message: message,
receiver: "<#TARGET_USER_ID#>",
groupID: nil,
priority: V2TIM_PRIORITY_DEFAULT,
onlineUserOnly: false,
offlinePushInfo: pushInfo,
progress: nil,
succ: { msg in
print(">>>>> sendMessage success, msgID = \\(msg?.msgID ?? "")")
},
fail: { code, desc in
print(">>>>> sendMessage failed, code:\\(code), desc:\\(desc ?? "")")
}
)
#import <ImSDK_Plus/ImSDK_Plus.h>

V2TIMOfflinePushInfo *pushInfo = [[V2TIMOfflinePushInfo alloc] init];
pushInfo.title = @"推送标题";
pushInfo.desc = @"推送内容";
// TODO: ext 由业务自定义,按需替换为您的目标页面、会话 ID 等参数。
pushInfo.ext = @"{\\"conversationID\\":\\"user_A\\",\\"conversationType\\":1}";

V2TIMMessage *message = [[V2TIMManager sharedInstance] createTextMessage:@"Hello TIMPush"];
[[V2TIMManager sharedInstance] sendMessage:message
receiver:@"<#TARGET_USER_ID#>"
groupID:nil
priority:V2TIM_PRIORITY_DEFAULT
onlineUserOnly:NO
offlinePushInfo:pushInfo
progress:nil
succ:^(V2TIMMessage *msg) {
NSLog(@">>>>> sendMessage success, msgID = %@", msg.msgID);
} fail:^(int code, NSString *desc) {
NSLog(@">>>>> sendMessage failed, code:%d, desc:%@", code, desc);
}];

客户端注册监听并解析 ext

请在 AppDelegate 中遵循 TIMPushListener 协议,在 didFinishLaunchingWithOptions 中调用 addPushListener 注册监听器,并在 onNotificationClicked 中解析 ext 后跳转到业务页面。
以下示例只演示解析 ext 的核心逻辑,跳转部分用 TODO 占位,请按自身业务补充。如果您接入了 TUIKit 并使用 OfflinePushExtInfo 组装的 ext,可改为 OfflinePushExtInfo.create(withExtString:) 解析后再跳转。
Swift
Objective-C
import UIKit
import TIMPush

@main
class AppDelegate: UIResponder, UIApplicationDelegate, TIMPushListener {
var window: UIWindow?

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)")

// 1. 解析 ext。JSON 结构由业务自定义,需与发送端约定一致。
guard let data = ext.data(using: .utf8),
let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else {
return
}
let conversationID = dict["conversationID"] as? String ?? ""
let conversationType = dict["conversationType"] as? Int ?? 0

// 2. TODO: 根据业务字段跳转到目标页面。
// 若使用了 Chat / TUIKit,建议在用户登录成功后再跳转;
// 冷启动场景可先把参数缓存起来,登录回调中再跳转。
}

// 协议要求实现,与点击跳转无关;如需感知收到 / 撤回离线推送,可按需补充。
@objc func onRecvPushMessage(_ message: TIMPushMessage) {}
@objc func onRevokePushMessage(_ messageID: String) {}
}
// AppDelegate.h
#import <UIKit/UIKit.h>
#import <TIMPush/TIMPushManager.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate, TIMPushListener>
@property (nonatomic, strong, nullable) UIWindow *window;
@end

// AppDelegate.m
#import "AppDelegate.h"
#import <TIMPush/TIMPushManager.h>

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[TIMPushManager addPushListener:self];
return YES;
}

#pragma mark - TIMPushListener

- (void)onNotificationClicked:(NSString *)ext {
NSLog(@">>>>> TIMPush notification clicked, ext:%@", ext);

// 1. 解析 ext。JSON 结构由业务自定义,需与发送端约定一致。
NSData *data = [ext dataUsingEncoding:NSUTF8StringEncoding];
if (data.length == 0) {
return;
}
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if (![dict isKindOfClass:[NSDictionary class]]) {
return;
}
NSString *conversationID = dict[@"conversationID"];
NSInteger conversationType = [dict[@"conversationType"] integerValue];

// 2. TODO: 根据业务字段跳转到目标页面。
// 若使用了 Chat / TUIKit,建议在用户登录成功后再跳转;
// 冷启动场景可先把参数缓存起来,登录回调中再跳转。
}

// 协议要求实现,与点击跳转无关;如需感知收到 / 撤回离线推送,可按需补充。
- (void)onRecvPushMessage:(TIMPushMessage *)message {}
- (void)onRevokePushMessage:(NSString *)messageID {}

@end
验证:点击通知后 onNotificationClicked 被调用,且 ext 内容与发送时设置的一致。如果回调未触发,请检查:控制台点击动作是否选择了「打开应用内指定页面」、监听器是否在 didFinishLaunchingWithOptions 中注册、发送消息时是否设置了 ext

收不到推送排障流程

如果发送测试消息后设备没有收到推送,请按以下流程逐步排查。每一步确认通过后再进入下一步,可以快速定位问题所在环节。不要只凭单条 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 排查 SDKAppIDappKey 取值(独立 Push 用 Push Key,Chat / TUIKit 用 nil)以及调用时机。

步骤3:确认控制台排查工具能查到 token

在腾讯云控制台 即时通信 IM > 推送服务 Push 的排查工具中输入 registrationIDuserID
能查到设备且 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:确认测试消息发送目标和设备状态

测试消息发送给的 registrationIDuserID 是否与 getRegistrationID: 返回值一致。
App 是否处于后台或已杀进程(前台时离线推送可能不触发)。
设备是否处于可接收通知状态(勿扰模式、通知权限等)。
推送服务是否到期(试用到期后自动停止服务)。

仍无法解决

如果按上述流程仍无法定位问题,请 联系我们 提交反馈。