App Tracking Transparency (ATT)
ATT is Apple's privacy framework that requires apps to request user permission before accessing the IDFA. This is required for iOS 14.5+.
Prerequisites
- iOS 14.5 or later
- Apptrove .NET MAUI SDK installed
Implementation
Step 1: Add Permission Description
Add to Platforms/iOS/Info.plist:
<key>NSUserTrackingUsageDescription</key>
<string>This identifier will be used to deliver personalized ads to you.</string>
Step 2: Create ATT Helper (Platforms/iOS/AppTrackingHelper.cs)
using AppTrackingTransparency;
using AdSupport;
using Foundation;
using UIKit;
namespace YourApp.Platforms.iOS;
public static class AppTrackingHelper
{
public static async Task<string?> RequestTrackingAuthorizationAsync()
{
var status = ATTrackingManager.TrackingAuthorizationStatus;
if (status == ATTrackingManagerAuthorizationStatus.Authorized)
{
return GetIdfa();
}
if (status == ATTrackingManagerAuthorizationStatus.NotDetermined)
{
// Wait for app to be active
await WaitForAppToBeActiveAsync();
var tcs = new TaskCompletionSource<ATTrackingManagerAuthorizationStatus>();
ATTrackingManager.RequestTrackingAuthorization((newStatus) =>
{
tcs.TrySetResult(newStatus);
});
var result = await tcs.Task;
if (result == ATTrackingManagerAuthorizationStatus.Authorized)
{
return GetIdfa();
}
}
return null;
}
private static async Task WaitForAppToBeActiveAsync()
{
var app = UIApplication.SharedApplication;
int attempts = 0;
while (app.ApplicationState != UIApplicationState.Active && attempts < 30)
{
await Task.Delay(100);
attempts++;
}
}
public static string? GetIdfa()
{
if (ATTrackingManager.TrackingAuthorizationStatus == ATTrackingManagerAuthorizationStatus.Authorized)
{
var idfa = ASIdentifierManager.SharedManager.AdvertisingIdentifier.AsString();
if (!string.IsNullOrEmpty(idfa) && idfa != "00000000-0000-0000-0000-000000000000")
{
return idfa;
}
}
return null;
}
public static string GetTrackingStatusString()
{
return ATTrackingManager.TrackingAuthorizationStatus switch
{
ATTrackingManagerAuthorizationStatus.NotDetermined => "Not Determined",
ATTrackingManagerAuthorizationStatus.Restricted => "Restricted",
ATTrackingManagerAuthorizationStatus.Denied => "Denied",
ATTrackingManagerAuthorizationStatus.Authorized => "Authorized",
_ => "Unknown"
};
}
}
Step 3: Initialize SDK with ATT (App.xaml.cs)
Important
Call WaitForATTUserAuthorization() before Initialize() to ensure the SDK waits for the user's ATT decision.
using AppTroveSDK.Maui;
public partial class App : Application
{
public static string? Idfa { get; private set; }
public static async Task InitializeSDKAsync()
{
// On iOS, request ATT first
if (OperatingSystem.IsIOS())
{
Idfa = await RequestATTAsync();
Console.WriteLine($"ATT complete, IDFA = {Idfa ?? "null"}");
}
var config = new AppTroveSDKConfig("your-sdk-key", AppTroveEnvironment.Production);
// Wait for ATT user authorization (30 seconds timeout)
AppTroveSDK.WaitForATTUserAuthorization(30);
// Initialize SDK
AppTroveSDK.Initialize(config);
}
private static Task<string?> RequestATTAsync()
{
#if IOS
return Platforms.iOS.AppTrackingHelper.RequestTrackingAuthorizationAsync();
#else
return Task.FromResult<string?>(null);
#endif
}
}
Visual Examples
ATT Permission Dialog:
IDFA Console Logs:

IDFA Panel in Apptrove Dashboard:

ATT Status Values
| Status | Description | SDK Behavior |
|---|---|---|
Authorized | User granted permission | Full tracking enabled, IDFA available |
Denied | User denied permission | Limited tracking, no IDFA |
Restricted | System restricted | Limited tracking, no IDFA |
NotDetermined | Not requested yet | Wait for user decision |
Troubleshooting
| Issue | Solution |
|---|---|
| ATT dialog not showing | Ensure NSUserTrackingUsageDescription is in Info.plist |
| IDFA is null | Check user granted permission and device supports IDFA |
| IDFA shows all zeros | User denied permission or running on simulator |
| SDK issues | Call WaitForATTUserAuthorization before Initialize |
For support, contact support@trackier.com.