Реализация App Tracking Transparency (ATT) для iOS
С iOS 14.5 приложения обязаны запрашивать разрешение пользователя через ATTrackingManager перед доступом к IDFA. Без разрешения ASIdentifierManager.shared().advertisingIdentifier возвращает нули (00000000-0000-0000-0000-000000000000). Приложение без корректной ATT-реализации получает reject на ревью по гайдлайну 5.1.2, а рекламные сети — нулевые данные атрибуции.
Что конкретно нужно сделать
Info.plist: ключ NSUserTrackingUsageDescription обязателен. Без него приложение падает с исключением при вызове requestTrackingAuthorization. Текст должен быть конкретным — Apple возвращает на ревью с формулировками вроде «для улучшения опыта». Работающий пример: «Мы используем данные устройства для показа релевантной рекламы и измерения эффективности рекламных кампаний».
Момент показа. requestTrackingAuthorization можно вызвать только один раз — повторный вызов не показывает диалог, а сразу возвращает кешированный статус. Поэтому момент важен: показывайте после онбординга, не в момент cold start. Пользователь, не понимающий зачем нужен доступ, нажимает «Запретить».
import AppTrackingTransparency
import AdSupport
func requestTrackingPermission() async {
// Ждём, пока приложение станет активным — иначе диалог не появится
await MainActor.run {
guard UIApplication.shared.applicationState == .active else { return }
}
let status = await ATTrackingManager.requestTrackingAuthorization()
switch status {
case .authorized:
let idfa = ASIdentifierManager.shared().advertisingIdentifier.uuidString
// Передаём IDFA в SDK рекламной сети
configureAdSDKs(withIDFA: idfa)
case .denied, .restricted:
// Инициализируем SDK в режиме без трекинга
configureAdSDKs(withIDFA: nil)
case .notDetermined:
break
@unknown default:
break
}
}
Частая ошибка — вызов requestTrackingAuthorization до того, как приложение стало .active. Диалог просто не появляется, статус остаётся .notDetermined, и приложение никогда не спросит снова. Это сложно воспроизвести в симуляторе, но легко поймать на реальном устройстве при первом запуске.
Интеграция с рекламными SDK
Facebook (Meta) Audience Network, Google AdMob, AppsFlyer, Adjust — все эти SDK нужно инициализировать после получения статуса ATT, иначе они стартуют без IDFA и кешируют этот факт.
// AppDelegate или SceneDelegate
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, ...) {
Task {
await requestTrackingPermission()
// Только после этого
initializeAnalyticsSDKs()
initializeAdSDKs()
}
}
SKAdNetwork: для атрибуции без IDFA. Список SKAdNetworkItems в Info.plist нужно обновлять при каждом добавлении новой рекламной сети. Meta, Google, Unity и другие сети публикуют свои SKAdNetwork-идентификаторы. Инструмент для генерации актуального списка — SKAdNetwork IDs от MMP-провайдеров.
AppsFlyer требует отдельной настройки режима работы без IDFA:
AppsFlyerLib.shared().start()
// При denied/restricted
AppsFlyerLib.shared().anonymizeUser = true
Тестирование
Симулятор не показывает ATT-диалог. Тестировать только на реальном устройстве. Сбросить статус разрешения для конкретного приложения: Настройки → Конфиденциальность → Отслеживание или полная переустановка приложения.
Для автоматизированного тестирования — ATTrackingManager можно подменить через протокол в тестах, не обращаясь к реальному API.
Процесс работы
Аудит текущей реализации ATT и SDK-инициализации, проверка Info.plist.
Реализация корректного флоу: момент показа, обработка всех статусов, передача IDFA в SDK.
Интеграция с конкретными рекламными SDK (Meta, AdMob, AppsFlyer/Adjust).
Тестирование на устройстве, проверка поведения при denied-статусе.
Ориентиры по срокам
Реализация ATT с одним-двумя SDK — 1 день. При наличии сложной матрицы рекламных сетей и необходимости настройки SKAdNetwork — до 3 дней.







