TUIKit 是基于 IM SDK 的一款 UI 组件库,可通过 UI 组件快速实现聊天、会话、搜索、关系链、群组等功能。本文介绍如何快速集成 TUIKit 并实现核心功能。
集成 TUIKit
TUIKit 采用数据驱动的响应式架构与原生 iOS UIKit 体系(非声明式 UI ),以源码方式开放集成。它提供会话列表、聊天、联系人、搜索、群组等完整 UI 能力。
前提条件
Xcode:16.0 或以上版本(推荐 Xcode 16.x 系列)。
iOS:14.0 及以上真机(暂时不支持模拟器)。
CocoaPods:1.12.0 及以上版本(推荐 1.16.x)。如尚未安装,请参考 CocoaPods Getting Started 进行安装。
一个有效的腾讯云账号及 Chat 应用。可参考 开通服务 从控制台获取以下信息:
SDKAppID:App 在控制台获取的 Chat 应用的 ID,为应用的唯一标识。
SDKSecretKey:应用的密钥。
集成并引入组件
说明:
下载源码
# 从 CNB 中克隆代码git clone https://cnb.cool/tencent/cloud/trtc/TUIKit_iOS.git# 从 GitHub 中克隆代码git clone https://github.com/Tencent-RTC/TUIKit_iOS.git
将仓库根目录下的
chat、call 文件夹整体复制到您的工程根目录下。复制完成后的目录结构如下:YourApplication/├── source/ # 您的工程代码├── call/ # 音视频通话组件│ └── TUICallKit_Swift.podspec│ └── TUICallKit_Swift/├── chat/ # Chat 源码│ ├── demo/ # 示例工程│ └── uikit/ # TUIChatKit 组件库├── Podfile # 依赖配置文件├── YourApplication.xcworkspace # 您的项目文件
说明:
TUIKit_iOS 是包含多个产品的开源仓库,Chat 的 UI 源码位于
chat/ 目录下(chat/uikit 为 UIKit 组件库,chat/demo 为示例工程),其依赖的音视频通话组件 call/TUICallKit_Swift 位于仓库根目录。模块目录可放在工程任意位置,只需在 Podfile 中设置正确的相对路径即可。集成组件
1. 在 Podfile 中引入对应模块(请按源码实际存放位置调整相对路径):
# 请使用您的真实项目名称替换 your_project_nametarget 'your_project_name' do# 补充如下内容 增加 TUIChatKit 和 TUICallKit_Swift 这两个依赖# 注意 path 的相对路径pod 'TUIChatKit', :path => 'chat/uikit/TUIChatKit.podspec'pod 'TUICallKit_Swift', :path => 'call/TUICallKit_Swift.podspec'end
2. Podfile 修改完毕后,执行以下命令,安装 TUIKit 组件。
pod install# 如果无法安装 TUIKit 最新版本,执行以下命令更新本地的 CocoaPods 仓库列表。# pod repo update# pod update
接入步骤
完成上述集成后,参考以下步骤,您仅需几行代码即可在项目中快速搭建会话列表、聊天、联系人等核心界面。
步骤1:配置用户鉴权

步骤2:用户登录
登录鉴权后才能正常使用组件的功能。调用
LoginStore 的 login 接口,传入上文获取的 sdkAppID、userID、userSig 进行登录鉴权:import AtomicXCoreimport TUIChatKitimport UIKitlet yourSdkAppID: Int32 = 10_000_000_00 // 填写你的实际sdkAppIDlet testUserID = "testUserID" // 你的测试用户IDlet userSig = "xxxxxxx" // 你的测试用户ID对应的 userSig (从IM 控制台获取,看上面的截图)LoginStore.shared.login(sdkAppID: yourSdkAppID, userID: testUserID, userSig: userSig) { [weak self] result inDispatchQueue.main.async {switch result {case .success:// 登录成功后 显示会话列表// self?.showConversationList()case .failure(let error):print("login failed: \\(error.code) \\(error.message)")}}}
警告:
步骤3:构建会话列表界面
import AtomicXCoreimport TUIChatKitimport UIKitfunc showConversationList() {let conversationsPage = ConversationsPage(onConversationClick: { [weak self] info in// 点击会话列表中的某条会话后,进入对应的聊天页// self?.showChat(info.conversation)})navigationController.pushViewController(conversationsPage, animated: true)}
ConversationsPage 会自动从本地数据库加载最近会话。当用户点击某条会话时,ConversationsPage 会通过 onConversationClick 回调,把所选会话的信息传递给上层,您可以在该回调中创建并进入聊天页(见 步骤 4)。步骤4:构建聊天界面
聊天页由 ChatPage 承载,负责消息的展示与收发。构造 ChatPage 时必须传入一个
ConversationInfo,用于指定要进入的会话。其中 conversationID 与 type 是必填的关键字段,title 用于聊天页导航栏标题的初始展示。import AtomicXCoreimport TUIChatKitimport UIKit// 进入单聊let userID = "test_user"var conversation = ConversationInfo(conversationID: ChatUtil.getC2CConversationID(userID))conversation.type = .c2cconversation.title = "与 \\(userID) 聊天"showChat(conversation)// 进入群聊let groupID = "@TGS#xxxxxx"var groupConversation = ConversationInfo(conversationID: ChatUtil.getGroupConversationID(groupID))groupConversation.type = .groupgroupConversation.title = "测试群"showChat(groupConversation)func showChat(_ info: ConversationInfo) {let chatPage = ChatPage(conversation: info, onBack: { [weak self] inself?.navigationController.popViewController(animated: true)})navigationController.pushViewController(chatPage, animated: true)}
注意:
在聊天界面使用 相册/录像/视频通话等需要声明对应的权限,请在 App 的 Info.plist 中声明如下权限:
<key>NSCameraUsageDescription</key><string>需要访问您的相机权限,开启后才能发送图片或视频</string><key>NSMicrophoneUsageDescription</key><string>需要访问您的麦克风权限,开启后才能发送语音</string><key>NSPhotoLibraryAddUsageDescription</key><string>需要访问您的相册权限,开启后保存图片或视频</string><key>NSPhotoLibraryUsageDescription</key><string>需要访问您的相册权限,开启后才能发送图片或视频</string>
步骤5:构建联系人界面
联系人页由 ContactsPage 承载,展示当前用户的好友列表(按字母索引分组)与群列表入口 等。页面自带导航栏,右上角的 +按钮内置了添加好友和加入群聊两个功能入口。
import AtomicXCoreimport TUIChatKitimport UIKitfunc showContacts() {let contactsPage = ContactsPage(onContactClick: { [weak self] contact in// 点击好友列表中的某个联系人回调(ContactInfo),可在该回调中进入单聊// self?.showChat(with: contact.userID, title: contact.nickname ?? contact.userID)},onGroupClick: { [weak self] group in// 点击群列表中的某个群的回调(GroupInfo),可在该回调中进入群聊// self?.showGroupChat(with: group.groupID, title: group.groupName)})navigationController.pushViewController(contactsPage, animated: true)}
步骤 6:音视频通话
音视频通话能力由 TUICallKit_Swift 组件提供(Podfile 中需添加 pod 'TUICallKit_Swift')。
1. 开通音视频通话服务,请参考 开通音视频服务。
2. 登录成功后初始化通话引擎。
LoginStore 登录只建立了消息通道,在登录成功后,通话引擎需单独初始化:import RTCRoomEngineimport TUICallKit_Swiftfunc initCallEngine() {let youSdkAppID: Int32 = 10_000_000_00 // 填写你的实际sdkAppIDlet testUserID = "testUserID" // 你的测试用户IDlet userSig = "xxxxxxx" // 你的测试用户ID对应的 userSig (从IM 控制台获取,看上面的截图)TUICallEngine.createInstance().`init`(youSdkAppID, userId: testUserID, userSig: userSig) {// enableIncomingBanner(true) 用于开启被叫时的来电横幅通知TUICallKit.createInstance().enableIncomingBanner(enable: true)} fail: { code, message inprint("initCallEngine failed: \\(code), \\(message ?? "")")}}
3. 发起通话。
func startVideoCall() {TUICallKit.createInstance().calls(userIdList: ["liu100"], // 被叫用户 ID 列表。单人通话传一个元素;传多个则为群组通话mediaType: .video, // 通话类型:.video 视频通话、.audio 语音通话params: nil,completion: nil)}
调用后 TUICallKit 会自动弹出全屏通话界面(含呼叫等待、接通、挂断全流程 UI),无需自行实现。
4. 如何移除音视频通话功能?
func showChat(_ info: ConversationInfo) {// 在输入配置中关闭输入面板中的视频通话和音频通话功能let inputConfig = ChatMessageInputConfig(isShowVideoCall: false, isShowAudioCall: false)let chatPage = ChatPage(conversation: info, messageInputConfig: inputConfig, onBack: { [weak self] inself?.navigationController.popViewController(animated: true)})navigationController.pushViewController(chatPage, animated: true)}
AI 助手:知识咨询与代码集成
在接入 IM SDK 过程中,您可以通过 MCP 使用 AI 助手,快速完成知识咨询、报错排查和 UIKit 集成代码生成。支持 Web/Android/iOS/Flutter/uni-app 等平台,答案基于官方文档。适用于查询 SDK API、UI 组件用法、服务端 API 与 IM 产品配置等场景,立即体验,提出您的第一个问题。
常见问题
音视频常见问题
TUICallKit 和自己集成的音视频库冲突了?
腾讯云的音视频库不能同时集成,可能存在符号冲突,可以按照下面的场景处理。
1. 如果您使用了
TXLiteAVSDK_TRTC 库,不会发生符号冲突。可直接在 Podfile 文件中添加依赖,pod 'TUICallKit_Swift'
2. 如果您使用了
TXLiteAVSDK_Professional 库,会产生符号冲突。您可在 Podfile 文件中添加依赖,pod 'TUICallKit_Swift/Professional'
3. 如果您使用了
TXLiteAVSDK_Enterprise 库,会产生符号冲突。建议升级到 TXLiteAVSDK_Professional 后使用 TUICallKit_Swift/Professional。通话邀请的超时时间默认是多久?
通话邀请的默认超时时间是 30 秒。
在邀请超时时间内,被邀请者如果离线再上线,能否立即收到邀请?
如果是单聊通话邀请,被邀请者离线再上线可以收到通话邀请,TUIKit 内部会自动唤起通话邀请界面。
如果是群聊通话邀请,被邀请者离线再上线后会自动拉取最近 30 秒内的邀请,TUIKit 会自动唤起群通话界面。
如何移除音视频通话功能?
func showChat(_ info: ConversationInfo) {// 在输入配置中关闭输入面板中的视频通话和音频通话功能let inputConfig = ChatMessageInputConfig(isShowVideoCall: false, isShowAudioCall: false)let chatPage = ChatPage(conversation: info, messageInputConfig: inputConfig, onBack: { [weak self] inself?.navigationController.popViewController(animated: true)})navigationController.pushViewController(chatPage, animated: true)}
上架常见问题
上架 App Store 时打包失败,提示 Unsupported Architectures?
问题现象如下图,打包时提示 ImSDK_Plus.framework 中包含了 App Store 不支持的 x86_64 模拟器版本。该问题是由于 IMSDK 为了方便开发者调试,发布时会默认带上模拟器版本。


您可以按照下面的步骤,在打包时去掉模拟器版本:
1. 选中您工程的 Target,并点击 Build Phases 选项,在当前面板中添加 Run Script;

2. 在新增的 Run Script 中,添加如下脚本:
#!/bin/sh# Strip invalid architecturesstrip_invalid_archs() {binary="$1"echo "current binary ${binary}"# Get architectures for current filearchs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)"stripped=""for arch in $archs; doif ! [[ "${ARCHS}" == *"$arch"* ]]; thenif [ -f "$binary" ]; then# Strip non-valid architectures in-placelipo -remove "$arch" -output "$binary" "$binary" || exit 1stripped="$stripped $arch"fifidoneif [[ "$stripped" ]]; thenecho "Stripped $binary of architectures:$stripped"fi}APP_PATH="${TARGET_BUILD_DIR}/${WRAPPER_NAME}"# This script loops through the frameworks embedded in the application and# removes unused architectures.find "$APP_PATH" -name '*.framework' -type d | while read -r FRAMEWORKdoFRAMEWORK_EXECUTABLE_NAME=$(defaults read "$FRAMEWORK/Info.plist" CFBundleExecutable)FRAMEWORK_EXECUTABLE_PATH="$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME"echo "Executable is $FRAMEWORK_EXECUTABLE_PATH"strip_invalid_archs "$FRAMEWORK_EXECUTABLE_PATH"done

Xcode 集成常见问题
[Xcodeproj] Unknown object version (60). (RuntimeError)

使用 Xcode 15 创建新工程来集成 TUIKit 时,输入 pod install 后,可能会遇到此问题,原因是使用了较旧版本的 CocoaPods ,此时有两种解决办法:
解决方式一: 修改 Xcode 工程的 Project Format 版本。

解决方式二: 升级本地的 CocoaPods 版本,升级方式本文不再赘述。
您可以在终端输入 pod --version 查看当前的 Pods 版本。
-ld64链接器问题?
Assertion failed: (false && "compact unwind compressed function offset doesn't fit in 24 bits"), function operator(), file Layout.cpp,

或是使用 Xcode 15 集成 TUIRoom 时,因最新链接器导致 TUIRoomEngine 的符号冲突,都属于该问题。

解决方式是:修改链接器配置
在 Build Settings 中的 Other Linker Flags 中添加"-ld64",即可解决。 参考资料: https://developer.apple.com/forums/thread/735426。

Rosetta 模拟器问题?
使用苹果芯片(M1\\M2等系列芯片)时会遇到, 原因是包括 SDWebImage 在内的三方库,并未支持 xcframework,不过苹果依旧给出了适配办法,就是在模拟器上开启 Rosetta 设置, 一般情况下编译时会自动弹出 Rosetta 选项。

Xcode 15 开发者沙盒选项问题?
Sandbox: bash(xxx) deny(1) file-write-create

当您使用 Xcode 15 创建一个新工程时, 可能会因为此选项导致编译运行失败,建议您关闭此选项。

Xcode 16 不支持 Framework 开启 bitcode 问题?
解决方案1:升级 SDK
如果您使用的是包含 Bitcode 的旧版本 SDK(例如 TXIMSDK_iOS),建议您按照本文档的指引,将 SDK 升级至 TXIMSDK_Plus_iOS_XCFramework。
解决方式2: 修改 Podfile 配置
在您的 Podfile 末尾新增如下配置,重新 Pod install。
post_install do |installer|bitcode_strip_path = 'xcrun --find bitcode_strip'.chop!def strip_bitcode_from_framework(bitcode_strip_path, framework_relative_path)framework_path = File.join(Dir.pwd, framework_relative_path)command = "#{bitcode_strip_path} #{framework_path} -r -o #{framework_path}"puts "Stripping bitcode: #{command}"system(command)endframework_paths = ["/Pods/TXIMSDK_iOS/ImSDK.framework/ImSDK",]framework_paths.each do |framework_relative_path|strip_bitcode_from_framework(bitcode_strip_path, framework_relative_path)endend
CocoaPods 集成常见问题
若您执行 pod install,出现 Podfile.lock 和 插件依赖的版本不一致时,
此时请删除 Podfile.lock 文件, 并使用 pod repo update 更新本地代码仓库, 之后使用 pod update 重新更新即可。
其他常见问题
表情包的使用
为了尊重版权,IM Demo/TUIKit 工程中默认不包含大表情元素切图。正式上线商用前请您替换为自己设计或拥有版权的其他表情包。下图所示默认的小黄脸表情包版权归腾讯云所有,可有偿授权使用,如需获得授权,您可以通过升级至 IM 企业版套餐 免费使用该表情包。

