Skip to content
AI 文档索引 ↗

iOS 基础 API ​

swift
let sdk = try TrackBusinessSDK.create(endpoint: endpoint, projectKey: projectKey)
sdk.track("checkout_started")
try sdk.identify(userId: "user_1001")
sdk.screen("Checkout")

iOS SDK 使用 Swift,支持类型化事件属性。

TIP

获取预编译 XCFramework 离线集成包(.xcframework.zip),请前往 iOS SDK 下载页。快速接入指南请参考 iOS 快速起步。


常用接口 ​

模块类别核心入口 (Swift)典型说明
初始化TrackBusinessSDK.create(...)可能抛出错误;在应用启动入口创建并复用实例
业务埋点sdk.track(...) / sdk.group(...)支持原生 Dictionary 或类型化 JSONValue 字典
位置快照import TrackBusinessLocation 后调用 trackWithLocation / grantLocationConsent / revokeLocationConsent可选模块,核心包不含该隐私声明;完整语义
手动 Flushtry await sdk.flush()手动触发持久化队列出队上报
身份管理identify / clearUser / switchUser / signOut首次关联、清除用户、切换账号、安全退出
身份快照let id = sdk.identity读取不可变的当前身份状态
用户属性sdk.setUserProperties(...) / profileIncrement提交 Profile 操作事件;接收端语义由部署实现决定
健康诊断sdk.getHealthSnapshot() / exportDiagnostics()查看运行状态与诊断报告;分享前检查宿主自定义内容

track() 返回不代表事件已写入队列或被接收端确认。仅含字符串的属性可使用 [String: String];其他类型使用 [String: JSONValue]。SDK 会合批发送,通常无需逐条调用 flush()。


1. SDK 初始化与生命周期 ​

基础初始化 (TrackBusinessSDK.create) ​

以下示例使用下载包中的二进制模块 TrackBusinessSDKKit。

在 AppDelegate 或 @main App 中创建并持有 SDK 实例:

swift
import Foundation
import TrackBusinessSDKKit

let sdk = try TrackBusinessSDK.create(
    endpoint: URL(string: "https://collector.example.com/v1/events/batch")!,
    projectKey: "ios-public-project-key"
)
sdk.track("checkout_started", properties: ["checkoutId": "chk_01"])

常用初始化参数 ​

参数类型默认值说明
endpointURL必填事件接收地址,支持 HTTP 与 HTTPS
projectKeyString必填绑定项目环境的公开凭据
initialConsentConsentDecision.granted(level: "MINIMAL").pending、.granted(level:) 或 .denied
privacyPolicyPrivacyPolicyPrivacyPolicy()默认敏感字段保护及项目字段规则
flushScheduleFlushSchedule.default默认 20 条、前台 30 秒与退后台调度
beforeSendEventInterceptor?nil入队前处理事件属性或拒绝事件
policySDKPolicySDKPolicy()插件开关、白名单及其他受控本地策略
enrichmentPlugins[any EventEnrichmentPlugin][]标准 create 与 createWithRemotePolicy 的命名属性增强器
identityServiceString"com.trackbusiness.sdk"Keychain Service 基名,按项目配置派生隔离空间
identitySyncTransport(any IdentitySyncTransport)?nil可选身份同步适配器,需配套服务
backgroundSessionIdentifierString?nil选配系统后台会话;宿主保障可信最终地址无跳转,需转发回调;SDK 无法限制系统后台重定向
integrationWarningHandlerSDKIntegrationWarningHandler?nil接收事件拒绝原因等元数据,不含属性、身份值或凭据

配置在创建实例时确定;完整参数见当前包的 create 声明。授权与敏感字段规则见授权与隐私。

命名业务属性增强 ​

支持 event_enrichment 的 SDK 包可向标准工厂传入实现 EventEnrichmentPlugin 的 Swift 类型;通过 EventEnrichmentResult(context:properties:) 返回补丁,返回 nil 表示不修改,不删除事件。增强在最终隐私和事件计划检查前执行,不替换现有 beforeSend 的事件过滤职责。

列表按配置顺序处理,同名增强器保留第一个,同名业务属性由后者覆盖。身份、Session、eventId 和保留的 SDK Context 快照不可改写。默认补丁预算 16,384 bytes;异常或超限时保留原事件。回调是同步短任务,不能执行网络/磁盘等待,也不提供 Web 式异步强制超时。

注册不会提升 Consent;PENDING 或授权等级不足时不执行增强。完整注册、授权、触发及关闭配方见客户端插件与能力接入,旧包须先核对实际 API 和随包能力声明。

启动前异步远程策略 (createWithRemotePolicy) ​

当服务端已经部署并验证 GET /v1/sdk/policy,且宿主主动选择远程控制面时,可使用异步工厂加载插件策略或 Tracking Plan。它不是基础初始化或上传的前置条件;没有该接口时使用普通 create 和可选的本地 Tracking Plan。iOS 只在 SDK 创建前加载远程策略,运行期间不会热刷新;策略变化需由宿主重新创建 SDK 后生效。

swift
let resolver = RemotePolicyResolver(
    provider: URLSessionRemotePolicyProvider(
        endpoint: endpoint,
        projectKey: "ios-public-project-key"
    ),
    cache: hostKeychainPolicyCache,
    verifier: policyVerifier,
    requireSignature: true // 生产环境开启验签
)

let sdk = try await TrackBusinessSDK.createWithRemotePolicy(
    endpoint: endpoint,
    projectKey: "ios-public-project-key",
    remotePolicyResolver: resolver,
    initialConsent: .granted(level: "STANDARD")
)

WARNING

远程策略请求涉及前置网络交互,宿主必须确认处于权威的 GRANTED 状态;若处于 pending,将直接以 CONSENT_REQUIRED_BEFORE_REMOTE_POLICY 抛错终止。

实例释放 (close) ​

swift
sdk.close()

停止定时发送、网络恢复监听与生命周期观察。重复调用安全;已保存的身份和待发送事件仍会保留。


2. 身份识别与生命周期 (Identity) ​

核心身份流转 API ​

swift
// 登录后绑定业务身份
try sdk.identify(userId: "user_9527")

// 退出登录
sdk.signOut()

// 只清除当前用户,保留匿名身份
sdk.clearUser()

// 切换账号
try sdk.switchUser(userId: "user_9528")

// 绑定其他业务标识
sdk.bindIdentity(namespace: "crm_id", value: "crm_1001")

// 重置身份和会话
sdk.reset()

// 获取当前身份
let identity = sdk.identity

reset() 会重置用户与会话身份;安装身份按 SDK 语义保留。切换账号请使用 switchUser()。


3. 事件采集 ​

SDK 在事件属性进入隐私过滤前限制嵌套层数、值数量和文本总量。采集属性或事件拦截器返回的属性超过处理预算时,SDK 丢弃该事件,诊断记录 invalid_event(event_input_budget)。后续事件继续处理。

业务事件与群组 (track / group) ​

swift
// 1. 自定义业务埋点
sdk.track("checkout_started", properties: [
    "checkoutId": .string("chk_10086"),
    "estimatedTotal": .number(299.0),
    "isPromotion": .boolean(false)
])

// 2. B2B 群组关联
sdk.group("org_alibaba", traits: [
    "role": .string("admin"),
    "tier": .string("enterprise")
])

事件拦截回调的调用限制 ​

设置全局属性或会话属性时,若更新后的总量超出 SDK 处理预算,本次更新会被拒绝,已有属性保持原值。健康快照记录 event_input_budget。

beforeSend、事件增强和动态公共属性回调在发起采集的线程上同步执行。回调应只做短小的内存操作,避免网络请求、磁盘读取和跨线程等待。回调执行期间,SDK 会拒收其他线程提交的事件,诊断记录 before_send_busy。回调结束后恢复正常准入。

beforeSend 中可以撤回授权、暂停采集、清理本地数据或关闭实例;这些操作会取消尚未提交的当前事件。close() 保留已经入队的事件。不要在该回调中等待队列写入、同步读取或确认队列:waitForPendingEventWrites() 等同步操作应在回调外调用。

4. 属性管理 (Super & User Properties) ​

Session Properties ​

仅保存在当前会话内存中,退出登录或会话超时后自动清理:

swift
sdk.setSessionProperties(["campaign": .string("winter_sale")])
sdk.removeSessionProperties(["campaign"])

Dynamic Super Properties ​

每次事件构建时同步求值,禁止在闭包中执行耗时锁或 I/O:

swift
sdk.registerDynamicSuperProperties {
    [
        "currentTab": .string(getCurrentTabName()),
        "unreadCount": .number(Double(getUnreadCount()))
    ]
}
sdk.clearDynamicSuperProperties()

用户 Profile 操作 ​

Profile API 提交操作事件,本地缓存与服务端画像可能不同步。服务端是否执行操作及如何保证并发更新,由部署实现决定。

  • setUserPropertyOnce 由服务端判断目标属性是否已存在;profileAppend 不保证数组去重或并发原子性。
  • unsetUserProperties 可指定未出现在本地缓存中的属性名。clearUserProperties 按本地已知键产生 $profile_unset,针对本地已知属性,不表示删除所有服务端数据。
  • 未授权、opt-out 或关闭实例后不执行 Profile 修改,也不在恢复授权后补发。禁止持久存储时仅维护内存缓存。
  • 缓存读写问题通过诊断查看;操作返回不代表服务端更新完成。隐私擦除使用 denyConsent 或 clearLocalData,检查完成结果,清理失败时不能恢复采集。
swift
// 1. 设置用户属性
sdk.setUserProperties(["plan": .string("Pro"), "level": .number(3)])

// 2. 仅首次有效设置
sdk.setUserPropertyOnce(["firstChannel": .string("app_store")])

// 3. 数值累加
sdk.profileIncrement(["loginCount": .number(1)])

// 4. 数组追加(单次最多 100 项,每个字符串最多 256 字符)
sdk.profileAppend(["badges": .array([.string("vip"), .string("early_adopter")])])

// 5. 删除指定属性,或清理本地已知的画像属性
sdk.unsetUserProperties(["legacy_tag"])
sdk.clearUserProperties()

// 6. 获取本地 Profile 字典快照
let profile = sdk.getUserProperties()

业务 Item 维度属性 ​

swift
sdk.itemSet("product", itemId: "sku_998", properties: ["price": .number(99.0)])
sdk.itemDelete("product", itemId: "sku_998")

5. 存储与发送 ​

手动刷新与调度感知 (flush) ​

swift
// 手动调度出队落盘与上报
try await sdk.flush()

// 前台定时发送默认已启用,仅在需要自行控制时调用
sdk.startForegroundFlush()
sdk.stopForegroundFlush()

内存模式与事件调试接口 ​

关闭持久存储时,事件仅保存在当前进程的内存队列。flush() 仍可尝试发送,进程结束后未发送事件无法恢复。

queuedEvents() 和 acknowledgeQueuedEvents(_:) 用于读取或确认本地调试队列;drainEvents() 仅排空 SDK 保留的内存事件快照,不能代替持久队列的 flush()。这些方法不撤回已发出的请求,也不表示 Collector 已接收事件。waitForPendingEventWrites() 是测试与诊断同步点;正常投递由 flush() 等待 SDK 的写入队列。

6. 调试、诊断与监控 (Diagnostics) ​

swift
// 1. 获取健康度快照
let health = sdk.getHealthSnapshot()

// 2. 导出诊断报告(不含事件 properties;分享前仍应检查其中的元数据)
let bundle = sdk.exportDiagnostics()

// 3. 读取近期诊断时间线
let timeline = sdk.getDiagnosticsTimeline(limit: 50)

// 4. 读取最近入队的事件列表元数据
let recent = sdk.getRecentEvents()

// 5. 开发构建可在 create 时注册接入提示
let debugSDK = try TrackBusinessSDK.create(
    endpoint: endpoint,
    projectKey: projectKey,
    integrationWarningHandler: { warning in
        logger.warning("SDK event rejected: \(warning.code)")
    }
)

诊断报告可能包含事件 ID、事件名、插件名和错误定位信息。分享前请检查其中的业务自定义名称和文本。


7. 屏幕浏览与原生交互 ​

页面停留与加载性能 ​

swift
// 1. 显式记录页面访问 ($screen)
sdk.screen("CheckoutViewController")

// 2. 页面加载完成耗时 ($page_load)
sdk.recordPageLoad(
    pageViewId: "pv_checkout_01",
    durationMs: 420,
    screenName: "CheckoutViewController"
)

控件点击与语义交互 (recordAppClick) ​

swift
sdk.recordAppClick(
    action: "checkout_submit",
    targetTag: "button",
    trackId: "btn-checkout-submit",
    captureKey: "ios:uikit:checkout-submit",
    elementType: "UIButton",
    screenName: "CheckoutViewController"
)

8. 平台原生能力与生命周期 ​

应用生命周期自动采集 (TrackBusinessApplicationLifecycle) ​

如需自动采集 $app_start 与 $app_end,宿主可显式持有 UIKit 扩展的生命周期监听器:

swift
import TrackBusinessSDKUIKit

let appLifecycle = TrackBusinessApplicationLifecycle(sdk: sdk)
appLifecycle.start()

渠道来源快照 ​

swift
sdk.recordAttribution(campaign: ["utm_source": "newsletter", "utm_campaign": "spring"])
sdk.track("order_created", properties: ["orderType": "subscription"])

仅需要已有首次/最近 UTM 来源时使用 recordAttribution。需要当前入口、会话入口和排除返回域时,见下方 recordAcquisition。

入口与营销触点(recordAcquisition,可选) ​

需要区分当前入口、会话入口和最近有效触点时使用。复用已初始化的 SDK,在宿主确认 STANDARD 授权及新入口事实之后调用:

swift
sdk.recordAcquisition(AcquisitionInput(
    parameters: ["utm_source": "newsletter"],
    landingUrl: "https://shop.example.com/landing"
))
sdk.track("entry_viewed", properties: ["surface": "campaign_landing"])

新输入被本地接受后,后续事件可携带 context.campaign.entry、sessionEntry 和 latestTouch。它们表示入口事实,不计算订单归因。原接口 recordAttribution 继续维护已有 UTM 视图,不要为同一入口重复调用两个接口。

通过 TrackBusinessDeepLinkIntegration 统一处理 SceneDelegate、AppDelegate 与 SwiftUI 场景:

swift
import TrackBusinessSDKUIKit

let deepLinks = TrackBusinessDeepLinkIntegration(
    sdk: sdk,
    options: TrackBusinessDeepLinkOptions(
        allowedHTTPSHosts: ["links.example.com"],
        allowedCustomSchemes: ["myapp"]
    )
)

// SceneDelegate 接收链接
func scene(_ scene: UIScene, openURLContexts contexts: Set<UIOpenURLContext>) {
    contexts.forEach { deepLinks.capture(url: $0.url) }
}

func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
    deepLinks.capture(userActivity: userActivity)
}

应用安装与更新 ​

swift
// 1. 显式记录应用安装(内部持久化去重)
sdk.recordApplicationInstall(properties: ["channel": .string("AppStore")])

// 2. 显式记录版本升级
sdk.recordApplicationUpdate(currentVersion: "2.1.0", properties: ["build": .string("20260901")])

诊断信号转发 ​

swift
// 1. 业务非致命异常
sdk.recordNonFatalError(message: "OrderCheckoutFailure")

// 2. 启动与网络性能上报
sdk.recordStartupPerformance(durationMs: 320)
sdk.recordNetworkPerformance(durationMs: 180, resourceCount: 4, transferBytes: 12000, failedCount: 0)

9. 元素曝光 (Exposure) ​

手动曝光 API (recordExposure) ​

swift
sdk.recordExposure(
    "feed_banner_01",
    durationMs: 800,
    visibleRatio: 0.75
)

曝光事件支持附加业务属性;自定义属性名不要以 $ 开头,并遵循隐私过滤及事件大小限制。请确认接收服务支持对应事件契约。

UIKit View 与 CollectionView 绑定 ​

监听器应作为视图控制器的属性持有,视图退出或复用时释放绑定。只调用 attach() 或 bind() 而不保留返回对象,会使监听器立即释放。

swift
import TrackBusinessSDKUIKit

// 视图控制器属性
private var bannerExposure: ViewExposureTracker?
private var collectionExposure: UICollectionViewExposureBinder?

func bindExposure() {
    unbindExposure()
    bannerExposure = ViewExposureTracker.attach(
        to: bannerImageView,
        sdk: sdk,
        exposureId: "home_hero_banner",
        minDurationMs: 500,
        minVisibleRatio: 0.5
    )
    collectionExposure = UICollectionViewExposureBinder(sdk: sdk) { indexPath in
        "goods_item_\(indexPath.item)"
    }.bind(collectionView)
}

func unbindExposure() {
    bannerExposure?.detach()
    bannerExposure = nil
    collectionExposure?.unbind()
    collectionExposure = nil
}

10. 外部设备标识读取 (IDFA / IDFV) ​

ATT 授权检测与 IDFA 读取器 ​

广告用途的 IDFA 读取由宿主管理 ATT 授权,读取器应检查授权状态:

swift
import AdSupport
import AppTrackingTransparency

struct HostIDFAReader: DeviceIdentifierReader {
    let name = "host-idfa-reader"

    func read(purpose: DeviceIdentifierPurpose, consent: ConsentState) async -> DeviceIdentifierReading? {
        guard purpose == .advertising else { return nil }
        guard #available(iOS 14, *), ATTrackingManager.trackingAuthorizationStatus == .authorized else {
            return DeviceIdentifierReading(
                kind: .idfa,
                status: .limited,
                purpose: .advertising,
                source: .host,
                isUserResettable: true,
                errorCode: "att_not_authorized"
            )
        }

        return DeviceIdentifierReading(
            kind: .idfa,
            status: .available,
            purpose: .advertising,
            source: .host,
            value: ASIdentifierManager.shared().advertisingIdentifier.uuidString,
            isUserResettable: true
        )
    }
}

IDFV 读取 ​

若仅需应用供应商测量,可直接注入 TrackBusinessSDKUIKit 提供的 AppleVendorIdentifierReader:

swift
import TrackBusinessSDKUIKit

let sdk = try TrackBusinessSDK.create(
    endpoint: endpoint,
    projectKey: projectKey,
    deviceIdentifierReader: AppleVendorIdentifierReader()
)

let reading = await sdk.readDeviceIdentifier(.measurement)

11. 预置上下文规范 (NativeContextCollector) ​

获准采集的事件可包含以下环境上下文。具体字段取决于设备和系统是否提供相关信息:

字段命名空间包含属性规范说明
context.devicemanufacturer, model, osVersion, screenWidth, screenHeight, screenScale, orientation, locale, timezone, typemanufacturer 固定为 "Apple";屏幕宽高以 points 为单位;type 基于 UIUserInterfaceIdiom 判定为 phone / tablet / tv 等。
context.appid, name, version, build自动提取宿主主 Bundle 的 Bundle ID、CFBundleDisplayName、CFBundleShortVersionString 与 CFBundleVersion。
context.networkonline, connectionType网络类型(wifi / cellular / ethernet / none / other);SDK 不读取运营商名称或 WiFi SSID。

应用版本与构建观测 ​

swift
sdk.recordApplicationUpdate(currentVersion: "2.1.0", properties: ["currentBuild": .string("210")])

SDK 会读取应用版本及构建号。显式调用时可通过 currentBuild 提供构建号。事件上的 $previous_version、$current_version、$previous_build、$current_build 和 $change_type 由 SDK 确定,不能通过自定义属性覆盖。

首次观测只建立基线。后续版本变化生成 version_changed,同版本的已知构建号变化生成 build_changed。SDK 不通过版本字符串排序判断升级或降级;缺失构建号不填零。内存模式下的基线不保证跨进程保留。

工厂作用域 ​

同一 endpoint、projectKey 和 identityService 作用域只能持有一个活动 Runtime。重复创建会抛出 TrackBusinessSDKError.scopeAlreadyOwned(SDK_SCOPE_ALREADY_OWNED);宿主应复用已有实例,或等待 close() 完成后再创建。不同作用域可独立使用。自定义 storageDirectory 时,宿主负责确保目录归属正确。

SDK 计时器 startTimer/endTimer 的输出字段为 properties.$timer_duration_ms(非负毫秒数),宿主 durationMs 属性不被覆盖。计时事件的严格 Tracking Plan 需声明该测量字段。

有界投递验证 ​

swift
sdk.track("integration_check")
let report = await sdk.verifyDelivery(timeoutMs: 3000)
if report.status == "acknowledged" {
    // 本次非空固定范围全部收到有效整批 ACK。
}

verifyDelivery 不抛出网络/存储错误;通过 status/reason 表达本次观察结果。取消 Swift Task 只结束观察,已开始的上传仍保留原有所有权。未知计数为 nil,empty 不表示发送成功;该结果也不承诺后台实时发送、强杀后执行或报表可见。详见可靠投递。

Swift Callback 重载 verifyDelivery(timeoutMs:completion:) 会同步登记范围,并在主线程回调一次;RN/Flutter 原生桥使用此重载,避免先派发 Task 再确定目标的竞争。原生准入锁被其他事件占用时返回 busy,可稍后重试,不阻塞接入方线程。