Skip to main content

App Tracking Transparency (ATT)

SDK Version Selection

Choose your SDK version below:

  • Apptrove SDK → Recommended for all projects (Latest: v2.0.0)
  • Trackier SDK → Will be deprecated in May 2026 (v1.x.xx)

Use the tabs below to view App Tracking Transparency code for your chosen SDK.

This guide provides comprehensive instructions for implementing Apple's App Tracking Transparency (ATT) framework with the Apptrove Flutter SDK. ATT is required for iOS 14.5+ apps that want to access the Identifier for Advertisers (IDFA) for accurate attribution and tracking.

Overview

App Tracking Transparency (ATT) is Apple's privacy framework that requires apps to request user permission before accessing the IDFA. This is crucial for accurate attribution and tracking on iOS 14.5 and later.

Prerequisites

  • iOS 14.5 or later
  • Flutter 2.0 or later
  • Apptrove Flutter SDK installed
  • Access to your app's Info.plist file

Implementation

Step 1: Add Permission Description

Add the following key-value pair to your Info.plist to describe why your app needs tracking permission:

ios/Runner/Info.plist
<key>NSUserTrackingUsageDescription</key>
<string>We use tracking to personalize your experience and improve our services.</string>

Step 2: Install ATT Package

Install the app_tracking_transparency package:

flutter pub add app_tracking_transparency

Step 3: Implement ATT in Your App

note

Important: Always include the SDK's waitForATTUserAuthorization(20) (e.g. Trackierfluttersdk.waitForATTUserAuthorization(20) or Apptrove equivalent) before initializing the SDK. This ensures the SDK waits for the user's ATT decision before starting tracking, which is required for accurate attribution on iOS 14.5+. You can adjust the timeout value (20 seconds) according to your use case or logic.

Update your main app file to integrate ATT with the Apptrove SDK:

main.dart
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:app_tracking_transparency/app_tracking_transparency.dart';
import 'package:apptrove_sdk_flutter/apptrove_sdk_flutter.dart';

void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}

class MyApp extends StatefulWidget {

_MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {

void initState() {
super.initState();
_initializeApp();
}

Future<void> _initializeApp() async {
await _initializeApptroveSDK();
}

/// Request ATT permission (iOS only)
Future<void> _requestATTPermission() async {
if (!Platform.isIOS) {
print('ATT is only required on iOS platform');
return;
}
try {
print('Requesting App Tracking Transparency permission...');
final TrackingStatus status = await AppTrackingTransparency.trackingAuthorizationStatus;
print('Current ATT status: $status');
if (status == TrackingStatus.notDetermined) {
print('Requesting ATT permission from user...');
final TrackingStatus result = await AppTrackingTransparency.requestTrackingAuthorization();
print('ATT permission result: $result');
_handleATTPermissionResult(result);
} else {
print('ATT permission already determined: $status');
_handleATTPermissionResult(status);
}
} catch (error) {
print('Error requesting ATT permission: $error');
}
}

/// Handle ATT permission result
void _handleATTPermissionResult(TrackingStatus status) {
switch (status) {
case TrackingStatus.authorized:
print('App Tracking Permission: Authorized');
_getIDFA();
break;
case TrackingStatus.denied:
print('App Tracking Permission: Denied');
break;
case TrackingStatus.restricted:
print('App Tracking Permission: Restricted');
break;
case TrackingStatus.notDetermined:
print('App Tracking Permission: Not Determined');
break;
}
}

/// Get IDFA if authorized (optional - for logging purposes)
Future<void> _getIDFA() async {
try {
final String? idfa = await AppTrackingTransparency.getAdvertisingIdentifier();
if (idfa != null) {
print('IDFA: $idfa');
} else {
print('IDFA not available');
}
} catch (error) {
print('Error getting IDFA: $error');
}
}

/// Initialize Apptrove SDK with ATT support
Future<void> _initializeApptroveSDK() async {
try {
AppTroveSDKConfig config = AppTroveSDKConfig("YOUR_SDK_KEY", "production");
if (Platform.isIOS) {
print('iOS detected, handling ATT...');
await _requestATTPermission();
print('Waiting for ATT user authorization (20 seconds timeout)...');
AppTroveFlutterSdk.waitForATTUserAuthorization(20);
print('ATT wait completed');
}
AppTroveFlutterSdk.initializeSDK(config);
print('Apptrove SDK initialized successfully');
} catch (error) {
print('Error initializing Apptrove SDK: $error');
}
}


Widget build(BuildContext context) {
return MaterialApp(
title: 'Your App',
home: Scaffold(
appBar: AppBar(title: Text('Apptrove SDK with ATT')),
body: Center(child: Text('Apptrove SDK Initialized')),
),
);
}
}

Key Features

  • waitForATTUserAuthorization(20): This method waits for the user to respond to the ATT prompt before initializing the SDK. The timeout can be adjusted based on your app's needs.
  • Automatic Status Handling: The SDK automatically handles all ATT status responses.
  • IDFA Access: Provides easy access to IDFA when permission is granted.
  • Platform Check: Seamlessly handles iOS-specific code with Platform.isIOS checks.
warning

Always replace "YOUR_SDK_KEY" with your actual SDK token from the Apptrove Panel. Using an incorrect token will cause attribution issues.

Visual Examples

ATT Permission Dialog:

ATT Permission Dialog

IDFA Console Logs:


IDFA Access


IDFA Panel in Apptrove Dashboard: When tracking is properly configured with ATT, you can see the IDFA details in the Apptrove panel for installs and events:

IDFA Panel Details

This panel shows the IDFA information when users grant tracking permission, allowing for accurate attribution and detailed tracking analytics.

ATT Status Values

StatusDescriptionSDK Behavior
.authorizedUser granted permissionFull tracking enabled, IDFA available
.deniedUser denied permissionLimited tracking, no IDFA
.restrictedSystem restricted (parental controls)Limited tracking, no IDFA
.notDeterminedPermission not requested yetWait for user decision

Best Practices

  • Timing: Request permission after app launch (1-2 second delay)
  • User Experience: Explain why tracking is needed before requesting permission
  • Timeout Configuration: Adjust timeout based on your app's flow (default 20 seconds)
  • Status Monitoring: Monitor tracking status changes and update UI accordingly
  • Check Status First: Always check if permission has already been determined before requesting
  • Handle All Cases: Implement proper handling for all ATT status values
  • iOS Version Check: Always check platform before using ATT

Troubleshooting

Common Issues

ATT Permission Not Requested

  • Ensure NSUserTrackingUsageDescription is added to Info.plist
  • Check that the app targets iOS 14.5 or later
  • Verify the permission request is called after app launch

IDFA Not Available

  • Check if user granted permission (status should be .authorized)
  • Ensure the device supports IDFA (not available on simulator)
  • Verify the app is not restricted by parental controls

SDK Initialization Issues

  • Make sure waitForATTUserAuthorization is called before SDK initialization
  • Check that the timeout value is appropriate for your app flow
  • Verify the SDK token is correct

Debug Tips

// Check current tracking status
final status = await AppTrackingTransparency.trackingAuthorizationStatus;
print('Current ATT Status: $status');

// Check if IDFA is available
final idfa = await AppTrackingTransparency.getAdvertisingIdentifier();
if (idfa != null) {
print('IDFA: $idfa');
} else {
print('IDFA not available');
}

Support

For technical support and questions:


This guide provides comprehensive implementation of App Tracking Transparency with the Apptrove Flutter SDK, ensuring compliance with Apple's privacy requirements while maintaining accurate attribution and tracking capabilities.