Compose 与 SwiftUI
以下代码示意如何在已安装原生 SDK 与可选 UI 模块的宿主中绑定按钮和页面事件。按钮的 action、targetTag 应使用稳定业务标识;页面生命周期绑定按需使用。完整依赖和实例注入见下文。
Button(onClick = trackBusinessAppClickAction(sdk = analytics, action = "checkout_submit", targetTag = "button", perform = { submitOrder() })) { Text("提交订单") }
CheckoutScreen(modifier = Modifier.trackBusinessScreenLifecycle(sdk = analytics, screenName = "checkout"))Button(action: trackBusinessAppClickAction(
using: sdk, action: "checkout_submit", targetTag: "button", perform: submitOrder
)) { Text("提交订单") }
CheckoutView().trackBusinessScreenLifecycle("checkout", sdk: sdk)可选关联标识
仅在宿主已有稳定逻辑键时传入 captureKey。不要读取按钮文本或页面私有内容来生成标识。
Modifier.trackBusinessSemanticInteraction(action = "open_help", targetTag = "navigation_affordance", captureKey = "android:compose:help-entry")页面、普通点击、语义交互和曝光事件支持附加业务属性;自定义属性名不要以 $ 开头,并遵循隐私过滤及事件大小限制。此能力需要接收服务支持对应事件契约;接入前请与服务提供方确认部署版本。
这些可选模块只处理显式绑定的 UI 事件,不扫描 UI 树或读取控件文本。事件的授权、队列和发送由核心 SDK 处理。
模块矩阵与依赖
可选模块必须与同版本的核心 SDK 配套使用,事件使用核心 SDK 当前可用的存储后端;降级为内存时不具备进程重启恢复能力。
| 平台 | 可选模块 | 最低系统 | 支持能力 |
|---|---|---|---|
| Android | com.trackbusiness:track-business-sdk-compose | Android API 23+ | Compose 点击、页面生命周期、元素与 Lazy 列表曝光 |
| iOS | TrackBusinessSDKSwiftUI | iOS 13+ | SwiftUI 点击、页面生命周期、元素曝光 |
| iOS UIKit | TrackBusinessSDKUIKit | iOS 13+ | UIKit 显式控件 Binder、ViewController 生命周期、列表曝光 |
1. Android Jetpack Compose
依赖配置
从客户端 SDK 下载页取得包含 Compose 扩展的离线集成包。按Android 安装步骤配置包内本地 Maven 仓库,再声明核心与 Compose 扩展依赖:
dependencies {
implementation("com.trackbusiness:track-business-sdk:1.0.0")
implementation("com.trackbusiness:track-business-sdk-compose:1.0.0")
}若组织已将包导入内部 Maven 仓库,可使用 com.trackbusiness:track-business-sdk-compose:1.0.0。该坐标不表示公共 Maven 已发布;模块版本以下载清单为准。
子树注入与按钮点击
使用 TrackBusinessSDKProvider 为页面子树提供 SDK。标准 Button、IconButton 等控件把采集包装在 onClick 中:只有控件实际执行 action 时才记录事件,触摸、键盘和无障碍激活共用同一路径。禁用或取消的操作不会执行 action。
import com.trackbusiness.sdk.compose.TrackBusinessSDKProvider
import com.trackbusiness.sdk.compose.trackBusinessAppClickAction
TrackBusinessSDKProvider(sdk = analytics) {
Button(
onClick = trackBusinessAppClickAction(
action = "checkout_submit",
targetTag = "checkout.submit",
captureKey = "android:compose:checkout-submit",
perform = { submitOrder() },
),
) {
Text("提交订单")
}
}也可以显式传入 sdk = analytics,不使用子树注入。未注入 SDK 的 Preview 中,action 包装仍执行 perform,只跳过采集。perform 是宿主业务回调,SDK 不改变其异常语义。
点击与语义交互
| 入口 | 事件 | 用途 |
|---|---|---|
trackBusinessAppClickAction | $app_click | 标准控件真实激活,作为控件的 onClick |
trackBusinessSemanticInteractionAction | $sdk_interaction | 宿主选择的语义动作,作为控件的 onClick |
Modifier.trackBusinessAppClick / trackBusinessSemanticInteraction | 对应的点击或语义事件 | 自定义非控件节点的未消费触摸;不代表 Button 的 enabled 或完整激活语义 |
Modifier 只观察未取消的触摸,不提供控件业务动作或自动的键盘/无障碍激活。已有 Button + Modifier 接入应把元数据移入 action 包装,并将原 onClick 业务回调移到 perform;移除同一按钮的观察 Modifier 和 onAccessibilityActivate 配置。
同一个交互只选择一条记录路径。不要同时使用 action 包装、交互 Modifier 和手动 recordAppClick 记录同一次点击。点击与订单提交、支付结果属于不同事实,可以分别记录;SDK 不会因为它们发生时间接近而自动删除其中一个。captureKey 是稳定目标标识,不是每次操作的唯一 ID。
标识符使用静态名称,例如 checkout-submit;不要填手机号、账号、订单详情或凭据。
页面与生命周期
- 单纯进入页面:使用
trackBusinessScreenView,在节点进入 Composition 时上报一次$screen; - 页面停留统计:使用
trackBusinessScreenLifecycle,在进入 Composition 时记录$screen,在离开 Composition 时自动配对发送$page_leave,并通过系统单调时钟计算durationMs。
CheckoutScreen(
modifier = Modifier.trackBusinessScreenLifecycle(
sdk = analytics,
screenName = "checkout",
),
)IMPORTANT
trackBusinessScreenLifecycle 已配对记录 $screen 与 $page_leave。请勿对同一 Composable 节点重复调用 trackBusinessScreenView 或手动 recordScreenLeave。
元素曝光与 Lazy 列表
// 单个组件曝光
PromoCard(
modifier = Modifier.trackBusinessExposure(
sdk = analytics,
exposureId = "home_banner_01",
minDurationMs = 500L, // 有效停留时长(默认 500ms)
minVisibleRatio = 0.5f, // 最小可见比例(默认 50%)
),
)
// LazyColumn / LazyRow 列表项曝光
FeedItem(
modifier = Modifier.trackBusinessListItemExposure(
sdk = analytics,
exposureId = "feed_item_$itemId",
),
)NOTE
声明式曝光修饰符属于自动采集源(source=auto),需要策略显式启用 android-exposure 插件;手动调用 sdk.recordExposure(...) 属于 source=manual,不受该插件开关限制。
2. iOS SwiftUI
依赖与环境注入
本地二进制 Swift Package 中添加 TrackBusinessSDKKit 与 TrackBusinessSDKSwiftUI 产品。在根视图使用 .trackBusinessSDK(_:) 注入环境值:
import SwiftUI
import TrackBusinessSDKKit
import TrackBusinessSDKSwiftUI
struct AppRootView: View {
let sdk: TrackBusinessSDK
var body: some View {
CheckoutView()
.trackBusinessSDK(sdk)
}
}点击与语义交互
下例的语义交互修饰符从 SwiftUI 环境中读取 SDK,未提供时不采集;Button action 包装器显式传入实例。页面生命周期和曝光修饰符也需要显式传入实例。
// 1. Button 通过自己的 action 路径记录标准控件点击 ($app_click)
Button(action: trackBusinessAppClickAction(
using: sdk,
action: "checkout_submit",
targetTag: "button",
captureKey: "ios:swiftui:checkout-submit",
perform: submitOrder
)) {
Text("提交订单")
}
// 2. 非 Button 的显式语义目标可使用 View Modifier ($sdk_interaction)
Image(systemName: "arrow.forward")
.trackBusinessSemanticInteraction(
action: "open_help",
targetTag: "navigation_affordance",
captureKey: "ios:swiftui:help-entry"
)Button 应通过 trackBusinessAppClickAction 或 trackBusinessSemanticInteractionAction 包装其 action 闭包。SwiftUI Button 的 action 是触摸、辅助功能和 UI 自动化共享的激活入口;通用 View 手势 Modifier 只用于非 Button 目标。包装器先尝试记录 SDK 事件,再执行宿主 action;SDK 尚未就绪时仍执行宿主 action。
页面生命周期与曝光
// 页面进入与离开闭环 ($screen + $page_leave)
CheckoutView()
.trackBusinessScreenLifecycle(
"checkout",
sdk: sdk
)
// 组件几何可见性曝光 ($sdk_exposure)
PromoCard()
.trackBusinessExposure(
"home_banner_01",
sdk: sdk,
minDurationMs: 500,
minVisibleRatio: 0.5
)SwiftUI 曝光按视图与窗口中的可见矩形估算,不识别其他视图的遮挡或任意形状遮罩。滚动列表和自定义容器请在实际界面验证。建议在授权并启用插件后绑定;授权前已可见的视图不保证自动补记曝光。
3. iOS UIKit 兼容适配 (显式 Binder)
在 UIKit 应用中,引入 TrackBusinessSDKUIKit 模块,使用 Binder 记录生命周期与交互事件。
控件点击绑定 (InteractionBinder)
由宿主持有 Binder 实例,显式监听目标 UIControl:
import TrackBusinessSDKUIKit
// 定义在 ViewController 中,确保绑定器在控件可用期间被持有。
private var payBinder: TrackBusinessUIKitInteractionBinder?
// 在控件和 SDK 都准备好后调用一次。
payBinder = TrackBusinessUIKitInteractionBinder(
control: payButton,
sdk: sdk,
action: "submit",
targetTag: "checkout_pay",
trackId: "checkout-pay",
captureKey: "ios:uikit:checkout-pay",
elementType: "UIButton",
screenName: "Checkout"
)
// 视图销毁或解绑时
payBinder?.close()
payBinder = nilViewController 页面生命周期
在 viewDidAppear 与 viewDidDisappear 中显式转发事件,由单调时钟自动配对计算时长:
class CheckoutViewController: UIViewController {
// 展示该控制器前由宿主注入已初始化的 SDK 实例。
var sdk: TrackBusinessSDK?
private var screenBinder: TrackBusinessUIKitScreenLifecycleBinder?
override func viewDidLoad() {
super.viewDidLoad()
guard let sdk else { return }
screenBinder = TrackBusinessUIKitScreenLifecycleBinder(
sdk: sdk,
screenName: "Checkout"
)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
screenBinder?.appear()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
screenBinder?.disappear(reason: "screen_change")
}
}视图与列表曝光
UIKit 可使用 ViewExposureTracker 绑定单个 View,使用 UICollectionViewExposureBinder 或 UITableViewExposureBinder 绑定列表。将监听器作为控制器属性持有,在视图退出或复用时解除绑定;完整示例见 iOS API 的元素曝光说明。
UIKit 曝光会考虑窗口和父视图的裁剪,但不识别其他视图的遮挡或任意形状遮罩。视图移动到新的父容器后请重新绑定;父视图显隐变化可调用 evaluate() 重新检查。
4. 行为准则与安全边界
action、targetTag、exposureId和页面名称使用稳定、非敏感的标识。- 同一点击、页面进入或离开只选择一种记录方式,避免显式绑定与手工调用重复。
- UI 扩展只记录已绑定的目标;授权、入队和上传仍由核心 SDK 处理。
5. 验收清单
集成后检查以下内容:
- [ ] 版本对齐:核心 SDK 与 UI 扩展库使用相同的对应平台版本;
- [ ] 隐私授权前置:在用户未授予对应 Consent 等级(如未同意隐私协议)时,修饰符保持静默,不产生事件;
- [ ] 插件开关验证:通过宿主配置禁用对应插件后,已绑定目标不再产生该插件事件;
- [ ] 身份隔离验证:调用
reset()或switchUser()后,后续产生的 UI 事件正确附着新身份上下文; - [ ] 离线可靠性:处于飞行模式或弱网环境下触发的 UI 事件,应检查实际存储后端和事件接纳结果;持久后端可用时验证恢复,网络恢复后核对有效 ACK;
- [ ] 诊断数据合规:
sdk.exportDiagnostics()导出运行元数据与处理后的诊断;分享前检查宿主自定义标识和错误摘要。
iOS 采集排查
触发一次已绑定的操作后,按顺序检查:
- 确认 SDK 实例已创建、自动采集策略允许该操作,且 Consent 已达到对应能力要求。UIKit 还需确认 Binder 已被宿主持有,且没有提前调用
close()。 - 查看
sdk.getDiagnosticsTimeline(limit: 50)中该操作的近期记录。若使用了稳定的captureKey,可用它定位;dropped的code提供准入失败原因。没有记录并不能证明已经采集:绑定可能未触发、插件可能未通过准入,或记录已超出有界时间线。曝光还可用sdk.getPluginHealth()检查ios-exposure状态及blockedReason。 - 查看
sdk.getHealthSnapshot()的queueSize、pendingWrites、lastDropReason、uploadFailures和lastError。这些是实例级状态,并非某个操作的专属结果;异步写入尚未完成时,队列大小可能尚未变化。 - 联调时调用
try await sdk.flush(),并在接收端按事件及批次核对接收结果。时间线中的本地接纳和调用flush()都不能单独证明业务事件已被接收端处理。
SwiftUI 曝光的 onExposed 表示可见性阈值已触发,并尝试向 SDK 提交;它不表示事件已入队或被接收端确认。captureKey、事件名和自定义属性应使用非敏感值;分享诊断报告前请检查其中的宿主自定义元数据。诊断接口示例见 iOS API。