如何优化应用App Tracking Transparency (ATT) 的授权转化率? 改善 ATT 权限体验包括提供清晰的预授权上下文、编写准确的 NSUserTrackingUsageDescription,以及在用户旅程中选择合适的时机来呈现系统授权请求。对授权率的影响应当通过实证进行评估。
广告商标识符 (IDFA) 是苹果公司提供的一个可重置设备标识符,用于 iOS 平台上的广告归因和效果衡量。在 App Tracking Transparency (ATT) 框架下,应用程序只有在通过
ATTrackingManager授权提示收到用户的明确许可后,才能访问 IDFA。
| 术语 | 定义 |
|---|---|
| IDFA | 受 App Tracking Transparency 管制的苹果平台级广告标识符。 |
| App Tracking Transparency | 苹果的框架,要求在跨应用和网站追踪用户之前获得用户授权。 |
| ATTrackingManager | 用于请求追踪授权的原生 AppTrackingTransparency API。 |
| Pre-Permission Primer | 在系统提示之前展示的自定义应用内屏幕,用于解释为什么请求授权。 |
IDFA 访问的经济学与 ATT 同意优化
授权 IDFA 访问在现代 iOS 测量中的作用
当应用程序对追踪数据的使用符合 ATT 及其广告与测量合作伙伴的要求时,获授权的 IDFA 访问可以支持用户级别的广告归因和衡量工作流。当无法获得追踪授权时,仍需要广告测量的团队可以使用独立允许的平台中介方法,例如 AdAttributionKit 或现有的 SKAdNetwork 集成。
获得授权后,IDFA 为受支持的广告网络集成提供了一个确定的连接键,从而无需统计建模即可实现广告系列级别的转化核对。当拒绝授权时,应用程序会将测量转换为平台原生框架。
立即进行冷启动弹窗的问题
在冷启动时立即提示用户进行追踪授权在技术上是苹果所允许的,但这可能为做出明智的授权决定提供较少的上下文,因为用户尚未体验应用的功能:
- 缺乏产品信任:首次使用的用户尚未对应用的功能、品牌价值或数据安全实践建立信心。
- 上下文不清:原生系统对话框在没有事先解释的情况下出现,导致风险厌恶型用户默认选择“要求 App 不要在跨应用追踪”。
- 权限疲劳:在初始应用启动期间堆叠多个系统权限对话框(例如推送通知、ATT 和定位)会产生摩擦并增加注册流失率。苹果 API 文档指出,如果现有的权限警报已经处于挂起状态,随后的授权请求将不会出现。

通过实证评估授权表现
可达到的同意率因应用类别、受众信任度、弹窗时机和文案清晰度而异。团队应使用受控的 A/B 测试来评估优化变体,而不是假设固定的行业基准。
另请参阅:IDFA ──> 移动归因模型
AppTrackingTransparency 框架的技术机制
四种授权状态
对 IDFA 的访问严格由 ATTrackingManager.AuthorizationStatus 枚举控制:
notDetermined(0):尚未向用户展示授权对话框。restricted(1):设备受到家长控制、教育配置文件或企业 MDM 的限制;无法授予追踪权限且设置开关已被禁用。denied(2):在用户拒绝请求后,应用没有权限访问与追踪相关的数据。authorized(3):用户授权追踪。在支持的 iOS 和 iPadOS 设备上,这通常允许访问非零的广告标识符。

一次性系统提示规则
iOS 操作系统对 ATTrackingManager.requestTrackingAuthorization 强制执行单次展示规则。一旦用户通过选择“允许”或“要求 App 不要在跨应用追踪”与原生模态框进行交互,系统就会记住确定的状态,并且在应用安装期间不会再次显示该提示。
虽然系统提示不会再次出现,但用户可以随时在 iOS 设置中手动调整其追踪授权。
当拒绝追踪授权时,iOS 如何强制执行标识符归零
在 iOS 14.5 及更高版本中,当未授予追踪授权时,广告标识符通常返回全零(00000000-0000-0000-0000-000000000000)。开发者应按需验证 ATT 授权状态和返回的标识符值,而不是假设缓存的字符串在应用启动期间保持有效。
高转化率 UX 架构:预授权引导策略
有效上下文引导页的剖析:苹果 HIG 约束
根据苹果关于隐私的人机界面指南,如果需要额外的上下文,应用程序可以在系统权限提示之前展示自定义的预提示屏幕。然而,苹果对这些预授权引导页施加了严格的设计规则:
- 单一操作按钮:预授权屏幕必须仅提供一个按钮(例如“继续”或“下一步”),该按钮直接通往系统提示。它绝不能提供规避或延迟系统警报的“取消”、“关闭”或“以后再说”按钮。
- 无 UI 模仿:引导页绝不能在视觉上模仿原生 iOS 系统警报框或显示标记为“允许”的模拟按钮。
- 无视觉胁迫:界面不得使用旨在操纵或引导用户在随后的系统提示上选择“允许”的图形、箭头或高对比度样式。
遵守 App Store 审核指南 5.1.2
根据苹果 App Store 审核指南第 5.1.2 节,苹果对权限请求强制执行了明确的界限:
- 禁止激励追踪:严格禁止应用以提供金泽奖励、虚拟货币、高级内容或折扣作为获得 ATT 同意回报的行为。这样做违反了指南 5.1.2,并可能导致应用被拒。
- 核心功能无门槛:如果用户拒绝追踪授权,应用程序不得阻止核心功能、阻止账户创建或降低应用性能。
- 用户选择优先:用户的实际追踪选择必须始终在苹果原生系统提示上做出。

Stage 1: User Onboarding / Core Value Realization
│
▼
Stage 2: Contextual Pre-Permission Primer Screen
(Single "Continue" Action ── Explains Purpose)
│
▼
Stage 3: Native iOS ATTrackingManager System Modal
(User Selects "Allow" or "Ask App not to Track")
│
┌────────────┴────────────┐
▼ ▼
[.authorized] [.denied]
ATT Authorized Graceful Fallback
(IDFA normally to Platform APIs
available) (AdAttributionKit / SKAN)
优化 NSUserTrackingUsageDescription 字符串
配置 Info.plist 以实现目的透明度
Info.plist 中的 NSUserTrackingUsageDescription 键定义了直接显示在苹果原生 ATT 系统警报中的说明字符串。根据苹果文档,该字符串必须简明、具体,并准确解释如何使用追踪数据。
在不改变底层披露的情况下测试清晰的用途字符串变体
在测试字符串变体时,测试的每个变体都必须准确且完整地描述应用程序的实际追踪实践:
- 个性化焦点:解释如何使用数据来定制内容推荐和产品建议。
- 广告相关性焦点:解释如何使用数据来提供相关的促销活动并避免重复的广告。
- 营销效果衡量焦点:解释如何使用数据来衡量广告合作伙伴的表现。
下面的配置说明了一个示例 NSUserTrackingUsageDescription。仅当措辞准确描述了应用的实际追踪实践时才使用它:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSUserTrackingUsageDescription</key>
<string>Your data will be used to deliver personalized product recommendations, relevant promotional offers, and measure advertising campaign performance.</string>
</dict>
</plist>
在用户生命周期中设计最佳弹窗时机触发器
跨用户旅程的时机考量
虽然在首次启动时请求 ATT 授权在技术上是允许的,但在用户体验到初始产品效用之后再展示提示,可以为授权请求提供更相关的上下文:
- 注册后触发器:在用户完成账户设置并配置初始偏好后展示提示。
- 上下文里程碑触发器:在完成核心用户操作后展示提示(例如,在游戏中完成新手引导教程、在购物应用中保存心仪商品,或在内容应用中收藏文章)。
在 Swift 中进行 ATTrackingManager 的工程实现
处理应用活动状态与线程安全
根据苹果 API 文档,ATTrackingManager.requestTrackingAuthorization 仅当应用状态为 .active 时才显示模态对话框。如果另一个权限提示处于活动状态或挂起状态,系统警报将不会出现,并且操作系统不会对并发请求进行排队。

下面的实现将授权状态管理与说明性的自定义预授权视图分离:
import UIKit
import AppTrackingTransparency
import AdSupport
final class ATTManager {
static let shared = ATTManager()
private init() {}
/// Evaluates current authorization status
var currentStatus: ATTrackingManager.AuthorizationStatus {
return ATTrackingManager.trackingAuthorizationStatus
}
/// Determines if tracking authorization can be presented
var canRequestAuthorization: Bool {
return currentStatus == .notDetermined
}
/// Requests tracking authorization with application active state verification
/// - Parameter completion: Closure returning the resolved authorization status
func requestAuthorization(completion: @escaping (ATTrackingManager.AuthorizationStatus) -> Void) {
guard canRequestAuthorization else {
completion(currentStatus)
return
}
// Verify application is active before invoking requestTrackingAuthorization
guard UIApplication.shared.applicationState == .active else {
print("ATT request skipped: application is not active. Call again once active if status remains notDetermined.")
completion(currentStatus)
return
}
DispatchQueue.main.async {
ATTrackingManager.requestTrackingAuthorization { status in
DispatchQueue.main.async {
switch status {
case .authorized:
// Read the current advertising identifier on demand; do not persist a cached IDFA.
let idfa = ASIdentifierManager.shared().advertisingIdentifier.uuidString
print("ATT Authorized - IDFA available: \(idfa)")
case .denied:
print("ATT Denied - Advertising identifier returns zeroes")
case .restricted:
print("ATT Restricted by system profile")
case .notDetermined:
print("ATT State unresolved")
@unknown default:
print("ATT Unknown state encountered")
}
completion(status)
}
}
}
}
}
/// Illustrative custom UIViewController for a HIG-aligned pre-permission primer
final class ATTPrimerViewController: UIViewController {
private let continueButton = UIButton(type: .system)
private let titleLabel = UILabel()
private let descriptionLabel = UILabel()
var onContinueTapped: (() -> Void)?
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
private func setupUI() {
view.backgroundColor = .systemBackground
// Prevent interactive swipe-to-dismiss when presented modally to ensure single-path navigation
isModalInPresentation = true
titleLabel.text = "Help Us Personalize Your Experience"
titleLabel.font = .boldSystemFont(ofSize: 20)
titleLabel.textAlignment = .center
titleLabel.numberOfLines = 0
descriptionLabel.text = "We use data to tailor product recommendations and deliver relevant promotional offers. On the next screen, you will see Apple's standard permission prompt to confirm your choice."
descriptionLabel.font = .systemFont(ofSize: 15)
descriptionLabel.textAlignment = .center
descriptionLabel.textColor = .secondaryLabel
descriptionLabel.numberOfLines = 0
// Apple HIG guidance calls for a single Continue or Next action
continueButton.setTitle("Continue", for: .normal)
continueButton.titleLabel?.font = .boldSystemFont(ofSize: 17)
continueButton.addTarget(self, action: #selector(handleContinue), for: .touchUpInside)
let stack = UIStackView(arrangedSubviews: [titleLabel, descriptionLabel, continueButton])
stack.axis = .vertical
stack.spacing = 20
stack.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stack)
NSLayoutConstraint.activate([
stack.centerXAnchor.constraint(equalTo: view.centerXAnchor),
stack.centerYAnchor.constraint(equalTo: view.centerYAnchor),
stack.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 32),
stack.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -32)
])
}
@objc private func handleContinue() {
// Presentation and dismissal mechanics are illustrative and should be adapted to the app's navigation architecture.
dismiss(animated: true) { [weak self] in
self?.onContinueTapped?()
}
}
}
拒绝后恢复:在不违反政策的情况下引导用户前往系统设置
何时引入辅助设置工作流
当用户选择“要求 App 不要在跨应用追踪”时,授权状态解析为 .denied。随后的 requestTrackingAuthorization 调用将返回 .denied 而不显示对话框。然而,如果用户随后启用了显式受益于追踪的功能(例如在其账户资料中请求个性化广告设置),应用可以提供一条通往 iOS 设置的非强迫性路径。
使用 UIApplication.openSettingsURLString
要引导用户前往应用的设置面板,请使用 UIApplication.openSettingsURLString:
if let settingsURL = URL(string: UIApplication.openSettingsURLString),
UIApplication.shared.canOpenURL(settingsURL) {
UIApplication.shared.open(settingsURL, options: [:], completionHandler: nil)
}
请注意,此 API 打开的是应用程序专用的设置页面;它不提供通往全局 设置 > 隐私与安全性 > 追踪 屏幕的直接深度链接。
App Store 政策边界
- 严禁持续唠叨:不要显示提示用户在设置中启用追踪的循环横幅。
- 严禁功能锁死:切勿因为追踪在设置中保持禁用状态而禁用核心功能。
对比决策矩阵:预授权策略与冷启动弹窗
| 维度 | 默认冷启动弹窗 | 强制门槛权限模态框 | 符合 HIG 的预授权引导页 |
|---|---|---|---|
| 提示前的用户上下文 | 低(无产品参与度) | 不固定(阻止访问) | 上下文导向(在相关交互后展示) |
| 苹果 UI/权限指南 | 在请求和数据使用合规时允许 | 当访问或补偿取决于追踪时不允许 | 在遵循 HIG 预警约束和追踪规则时允许 |
| 苹果预警 UI 约束 | 不适用 | 不适用 | 单个“继续/下一步”操作;无伪造警报 |
| 权限中断时机 | 启动时立即触发 | 阻塞式 | 上下文里程碑 |
| 观察到的授权率 | 必须通过实证测量 | 不允许 | 必须通过实证测量 |
常见问题解答 (FAQ)
应用能否提供应用内货币或折扣来换取授予 ATT 权限?
如果用户最初选择“要求 App 不要在跨应用追踪”,应用能否显示第二个 ATT 提示?
预授权引导页是否违反苹果的 App Store 指南?
总结与决策框架
改善 ATT 权限体验需要清晰的用途披露和符合上下文的时机把握。当需要额外的解释时,符合 HIG 的预授权引导页可以在系统提示之前提供上下文,同时使权限体验与苹果发布的追踪和界面指南保持一致。
如果 ATT 拒绝后仍需要归因或注册上下文,应用程序可以使用独立允许的机制,例如 AdAttributionKit、现有的 SKAdNetwork 集成,或保持在苹果追踪规则内的真正第一方上下文路由。
有关特定于产品的上下文路由和归因行为,请查阅 OpoInstall 文档并对照适用的苹果追踪要求评估实现方案。
相关材料
-
概念:App Tracking Transparency、IDFA 优化、预授权引导页、同意转化漏斗
-
技术:StoreKit 框架、AppTrackingTransparency 框架、OpoInstall 移动 SDK
-
标准:苹果关于隐私的人机界面指南、App Store 审核指南第 5.1.2 节
-
API:
ATTrackingManager.requestTrackingAuthorization、UIApplication.openSettingsURLString、ASIdentifierManager
官方文档
Share this article



