审核看的是你上传的那个二进制,其余都不在范围内。
Podfile 的 :configurations 只列你不会提交的构建配置,正式包里就没有它的任何代码。
万一被链进正式包,SDK 会在启动时识别分发渠道:判为 App Store 就停下并打日志;TestFlight 默认不启动。
营养标签与 PrivacyInfo.xcprivacy 描述的是你提交的那个 App。排除之后,它既不占体积也没有可声明项。
按下面的接入方式,判定与 SDK 自带的分发闸门一致。
| Debug | TestFlight | App Store | |
|---|---|---|---|
| SDK 是否链入 | 是 | 是,前提是你列了这个配置 | 否,被 :configurations 排除 |
| 是否启动 | 是 | 需显式 allowTestFlight = YES |
不启动,识别到正式包即停止 |
| 是否联网 | 是,配好工程凭证后连接托管服务 | 放行后连接 | 无任何连接 |
| 二进制体积 | 增加链入的静态代码 | 与 Debug 相同 | 无影响,没有链入任何代码 |
| 隐私清单与营养标签 | 不提交给 Apple | 会上传到 App Store Connect,按你自己构建的用法声明 | 无需为 SDK 声明 |
AppTelepath 不随包提供 PrivacyInfo.xcprivacy——它不是为 App Store 分发构建的。你自己的构建要声明什么取决于你的接入方式,最终以 Apple 当时的审核结果为准。
一行 Podfile、一道编译开关,凭证只提交占位符。
配置名因工程而异,不要写死 Debug。TestFlight、AdHoc 与企业签通常也是 Release 类配置,属于可用范围;要列的是你不会提交 App Store 的每一个配置。
只在上面这些配置里定义 TELEPATH_ENABLED。不要用 #if DEBUG——它会让 SDK 在你确实想用的 Release 类测试包里也起不来。
Info.plist 引用 build setting,真实地址与工程凭证放在 gitignored 的本地 xcconfig,正式配置留空。若某个 Release 配置同时用于 App Store archive,另建 Internal 或 Staging 配置。
# Podfile · 只列不会提交 App Store 的配置
pod 'AppTelepath', '~> 3.0',
:configurations => ['Debug', 'Staging']
// Debug.xcconfig · 每个非提交配置各加一份
SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) TELEPATH_ENABLED
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) TELEPATH_ENABLED=1
// AppDelegate.swift · 没定义这个开关时整段不编译 #if TELEPATH_ENABLED import Telepath Telepath.start() #endif
<!-- Info.plist · 只放占位符,实际值来自 gitignored xcconfig --> <key>TelepathAgentServerURL</key> <string>$(TELEPATH_AGENT_URL)</string> <key>TelepathAgentWorkspaceKey</key> <string>$(TELEPATH_AGENT_KEY)</string>
进入测试包的是 device-only 凭证:被提取者可以冒充一台设备,但访问不了控制台、MCP 或其他设备。任何情况下都不要把管理员令牌写进 App。
链接方式决定了怎么排除,也决定要不要自己加 linker flag。
:configurations 是能按构建配置排除 SDK 的接入方式。-ObjC 由 CocoaPods 自动加上——这个 pod 以静态库构建、又带 vendored 静态产物,两条都命中。
挂在 App target 上的包依赖在所有配置里都会链接。需要「正式包里完全没有」时,走 CocoaPods,或把它只挂在测试用的独立 target 上。#if TELEPATH_ENABLED 这道开关两种情况都要留。
SwiftPM 与手动拖 xcframework 都要自己在 Other Linker Flags 里加 -ObjC。缺了它,只含 category 的目标文件会被链接器丢掉,表现是运行时 unrecognized selector,而不是链接报错。SwiftPM 禁止依赖包使用 unsafeFlags,替你加不了。
证据全部来自公开手段:编译期模拟器标记、bundle 里有没有 embedded.mobileprovision、App Store 收据文件是否真的存在。
bundle 里还有描述文件,说明这个包没经过商店分发,SDK 正常启动,并把判定出的渠道一并上报——控制台上能看出「现在看的是哪个包」。
没有描述文件、收据名为 sandboxReceipt 即判为 TestFlight,需显式设置 config.allowTestFlight = YES 或 Info.plist 的 TelepathAgentAllowTestFlight。它面向真实外部测试者,这一步必须是你主动做的决定。
没有描述文件、收据文件存在且不是 sandbox,判为正式包。Telepath 在连接前停止并打出原因,提示回去核对 Podfile 的配置列表。
既无描述文件也无收据文件时不做推定,照常启动但持续告警。失败方向是刻意选的:这道闸防的是配置失误,而调试工具「静默连不上」比「连上了一直告警」更难排查。
闸门拦的是 Telepath.start,不是链接器。SDK 一旦被链入,代码就在二进制里,个别探针还会在 +load 阶段读本地开关。所以正解永远是第一步——根本不让它进提交用的构建,闸门只是兜底。
Apple 的隐私清单与营养标签要求,针对的是你提交的 App 以及其中包含的 SDK。AppTelepath 被排除在正式包之外,就没有需要为它声明的项,也不可能从 App Store 用户那里采集任何数据。
AppTelepath 不随包提供 PrivacyInfo.xcprivacy:它不是为 App Store 分发构建的,自然不为一次自己不参与的提交携带清单。反过来,把它留在正式包里,缺声明与 SDK 拒绝启动会同时发生。
它确实用到 Apple 归入 Required Reason 的几类 API——文件时间戳、系统启动时间与 UserDefaults,分别来自读沙盒、采启动耗时与查看已存值。这是它只该出现在你不会提交的构建里的又一个理由。
在你启动它的测试构建里,SDK 采集的是调试所需的那些数据:截图与录屏、日志与崩溃、网络请求、UI 树、沙盒文件、数据库查询与性能采样,发往 AppTelepath 托管服务,供你的 workspace 与 agent 读取。数据来自跑这个包的那台设备——所以放行 TestFlight 必须是一次明确的决定,而不是默认值。
你自己的 App 要声明什么、要不要告知测试者,取决于你的接入方式与构建里的其余部分,最终以 Apple 当时的审核结果为准。我们只能说清自己的 SDK 做了什么,替你的二进制回答不了。
SDK 本身不构成拒绝理由。审核看的是你上传的那个二进制,调试 SDK 只有随包提交才与审核相关。AppTelepath 的做法是在构建期就把它排除出 App Store 配置,这样排除之后它完全不参与审核。具体某个包能否通过,以 Apple 当时的审核结果为准。
可以,但要你自己决定。TestFlight 通常是 Release 类配置,把该配置列进 Podfile,并设置 config.allowTestFlight = YES 或 Info.plist 的 TelepathAgentAllowTestFlight。默认不启动,是因为 TestFlight 面向真实外部测试者,而包里的凭证可被提取;该凭证是 device-only 的,提取者能冒充一台设备,但访问不了控制台、MCP 或其他设备。
排除之后不需要——这项要求覆盖的是你提交的 App 里包含的 SDK。AppTelepath 自身不随包提供 PrivacyInfo.xcprivacy,因为它不为 App Store 分发构建。你自己的 App 要声明什么,取决于你的接入方式与构建里的其余部分。
有几项能力用到:滑动的触摸合成、录屏的帧捕获,以及模拟推送时构造通知响应。它们只在 agent 调用对应命令时执行,不在启动路径上;有公开替代的会降级并在响应里说明这次走的是哪条,没有公开替代的会如实回报「这个系统版本上做不到」,而不是假装做过了。这也是 SDK 只限用于不提交的构建的原因之一;TestFlight 上传的检查结果同样以 Apple 为准。
SDK 会识别出 App Store 渠道并在连接前停止,同时打出原因。但代码仍然在二进制里,所以要去修 Podfile 的配置列表与编译开关,再重新 archive。运行时闸门只是配置失误的兜底,不能替代排除。