### Example: Requesting Tracking Authorization Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/ios-frameworks.md Demonstrates how to request tracking authorization from the user and handle the response in a completion handler. This example shows enabling or disabling tracking based on the user's choice. ```swift ATTrackingManager.requestTrackingAuthorization { status in switch status { case .authorized: print("User granted tracking") enableTracking() case .denied: print("User denied tracking") disableTracking() case .restricted: print("Tracking restricted") case .notDetermined: print("Dialog not shown") @unknown default: print("Unknown") } } ``` -------------------------------- ### Install and Run on Real iOS Device Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/quick-reference.md Use `flutter run --release` to install and run the application on a real iOS device. Specify a device ID if needed. A clean install is often required for accurate testing. ```bash # Install on real iOS 14+ device flutter run --release # Or with specific device flutter run --release -d # Fresh install (required for testing) flutter clean rm -rf ios/Pods ios/Podfile.lock flutter pub get flutter run --release ``` -------------------------------- ### Integration Test Example (Native Swift) Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/api-reference/Method-Channel-Protocol.md Shows how to test the method channel invocation for getting the tracking authorization status from native iOS code using Swift. It asserts that the returned status is within the valid range. ```swift // In iOS test code let channel = FlutterMethodChannel( name: "app_tracking_transparency", binaryMessenger: testBinaryMessenger ) channel.invokeMethod("getTrackingAuthorizationStatus", arguments: nil) { result in if let status = result as? Int { XCTAssertTrue(status >= 0 && status <= 4) } } ``` -------------------------------- ### Example: Get Advertising Identifier and Check Status Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/api-reference/AppTrackingTransparency.md Demonstrates how to request tracking authorization and then retrieve the advertising identifier. It checks if the IDFA is available and prints a message indicating its status. ```dart // Get advertising identifier after requesting authorization final uuid = await AppTrackingTransparency.getAdvertisingIdentifier(); if (uuid != '00000000-0000-0000-0000-000000000000' && uuid.isNotEmpty) { // IDFA is available, use for tracking print('Advertising ID: $uuid'); } else { // IDFA unavailable, use SKAdNetwork for conversions print('IDFA not available'); } ``` -------------------------------- ### Example: Reading Tracking Authorization Status Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/ios-frameworks.md Demonstrates how to read the current tracking authorization status and handle different scenarios using a switch statement. This example is useful for understanding user consent levels. ```swift let status = ATTrackingManager.trackingAuthorizationStatus switch status { case .notDetermined: print("Dialog can be shown") case .denied: print("User denied tracking") case .authorized: print("User authorized tracking") case .restricted: print("Tracking restricted by device policy") @unknown default: print("Unknown status") } ``` -------------------------------- ### Plugin File Organization Structure Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/README.md Illustrates the directory structure for the App Tracking Transparency plugin, showing the location of README, reference guides, API definitions, and implementation details. ```plaintext output/ ├── README.md (this file) ├── quick-reference.md ├── setup-and-integration.md ├── types.md ├── ios-frameworks.md ├── implementation-details.md └── api-reference/ ├── AppTrackingTransparency.md ├── Native-iOS-Implementation.md └── Method-Channel-Protocol.md ``` -------------------------------- ### iOS 17.4 Inconsistent State Example Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/implementation-details.md This example illustrates an inconsistent state scenario on iOS 17.4 where the reported status and the result callback from requesting authorization do not match. The plugin detects this and retries using an observer. ```swift ATTrackingManager.trackingAuthorizationStatus = .notDetermined ATTrackingManager.requestTrackingAuthorization callback = .denied // These values contradict each other // Plugin detects and retries via observer ``` -------------------------------- ### Unit Test Example (Dart) Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/api-reference/Method-Channel-Protocol.md Demonstrates how to unit test the method channel communication for retrieving the tracking authorization status in Dart. It mocks the MethodChannel and asserts the expected status and method calls. ```dart test('Method channel returns int status', () async { // Mock the method channel const MethodChannel channel = MethodChannel('app_tracking_transparency'); final List log = []; channel.setMockMethodCallHandler((MethodCall methodCall) async { log.add(methodCall); if (methodCall.method == 'getTrackingAuthorizationStatus') { return 0; // notDetermined } throw MissingPluginException(); }); final status = await AppTrackingTransparency.trackingAuthorizationStatus; expect(status, TrackingStatus.notDetermined); expect(log, [ isMethodCall('getTrackingAuthorizationStatus', arguments: null), ]); }); ``` -------------------------------- ### Get Tracking Authorization Status Example Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/api-reference/AppTrackingTransparency.md Checks the current tracking authorization status without displaying a dialog. Useful for conditionally showing UI elements or determining if the system dialog can be presented. ```dart // Check tracking authorization status final status = await AppTrackingTransparency.trackingAuthorizationStatus; if (status == TrackingStatus.notDetermined) { // Show custom explainer dialog before system dialog await showCustomTrackingDialog(context); await Future.delayed(const Duration(milliseconds: 200)); // Request system authorization await AppTrackingTransparency.requestTrackingAuthorization(); } ``` -------------------------------- ### Example: Ad Network Integration with Advertising Identifier Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/api-reference/AppTrackingTransparency.md Shows how to integrate the advertising identifier with Google Mobile Ads. It conditionally sets the mobile ads ID based on whether the IDFA is available. ```dart // Google Mobile Ads integration example final uuid = await AppTrackingTransparency.getAdvertisingIdentifier(); final mobileAdsId = uuid != '00000000-0000-0000-0000-000000000000' ? uuid : null; // Configure mobile ads with or without IDFA ``` -------------------------------- ### Request Tracking Authorization Example Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/api-reference/AppTrackingTransparency.md Displays the iOS system App Tracking Transparency authorization dialog. Subsequent calls return the user's previous choice without showing the dialog again. Requires NSUserTrackingUsageDescription in Info.plist. ```dart final status = await AppTrackingTransparency.requestTrackingAuthorization(); switch (status) { case TrackingStatus.authorized: print('User granted tracking permission'); // Proceed with tracking break; case TrackingStatus.denied: print('User denied tracking permission'); // Disable tracking features break; case TrackingStatus.restricted: print('Tracking is restricted by device policy'); break; case TrackingStatus.notDetermined: print('Dialog not shown or error occurred'); break; case TrackingStatus.notSupported: print('Not on iOS 14+ or platform unsupported'); break; } ``` -------------------------------- ### Dart Error Handling for Platform Exceptions Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/api-reference/Method-Channel-Protocol.md Example of how to wrap method channel calls in Dart with a try-catch block to handle potential PlatformExceptions and other errors. ```dart try { final status = await AppTrackingTransparency.trackingAuthorizationStatus; } on PlatformException catch (e) { print('Platform error: ${e.code} ${e.message}'); } catch (e) { print('Unexpected error: $e'); } ``` -------------------------------- ### ASIdentifierManager Class Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/ios-frameworks.md Singleton providing access to advertising identifiers (IDFA). Use this to get the device's Advertising Identifier. ```swift class ASIdentifierManager: NSObject { static let shared: ASIdentifierManager var advertisingIdentifier: UUID { get } var isAdvertisingTrackingEnabled: Bool { get } } ``` -------------------------------- ### Get Advertising Identifier (Swift) Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/ios-frameworks.md Retrieves the device's advertising identifier as a UUID string. This method relies on the AdSupport framework. ```swift private func getAdvertisingIdentifier(result: @escaping FlutterResult) { result(String(ASIdentifierManager.shared().advertisingIdentifier.uuidString)) } ``` -------------------------------- ### iOS Info.plist: SKAdNetwork Items Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/setup-and-integration.md Add SKAdNetwork identifiers to `ios/Runner/Info.plist` to enable ad network attribution for app installs when IDFA is unavailable. Include identifiers from your ad partners. ```xml SKAdNetworkItems SKAdNetworkIdentifier cstr6suwn9.skadnetwork ``` -------------------------------- ### Handle All Authorization States Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/types.md This snippet demonstrates how to handle all possible outcomes after requesting tracking authorization. It covers scenarios for authorized, denied, restricted, not determined, and not supported statuses, guiding the appropriate action for each. ```dart final status = await AppTrackingTransparency.requestTrackingAuthorization(); if (status == TrackingStatus.authorized) { // IDFA available, proceed with ad tracking final uuid = await AppTrackingTransparency.getAdvertisingIdentifier(); enableTracking(uuid); } else if (status == TrackingStatus.denied) { // User denied, fall back to SKAdNetwork disableTracking(); } else if (status == TrackingStatus.restricted) { // Device restricted, cannot enable tracking showRestrictedMessage(); } else if (status == TrackingStatus.notDetermined) { // Dialog not shown or error retryLater(); } else if (status == TrackingStatus.notSupported) { // Not iOS 14+ useAndroidAlternative(); } ``` -------------------------------- ### State Transitions for Tracking Authorization Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/types.md Illustrates the possible state transitions for tracking authorization, starting from 'notDetermined' and showing how users can move to 'authorized', 'denied', or 'restricted' states. Note that 'notSupported' is a permanent state due to platform limitations. ```dart Initial State: notDetermined ↓ User responds to dialog / | \ Authorized Denied Restricted (3) (2) (1) ``` -------------------------------- ### Get Advertising Identifier Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/api-reference/Native-iOS-Implementation.md Retrieves the device's Advertising Identifier (IDFA). Returns a UUID string. Handles simulator and pre-authorization cases by returning all zeros. ```swift private func getAdvertisingIdentifier(result: @escaping FlutterResult) ``` ```swift result(String(ASIdentifierManager.shared().advertisingIdentifier.uuidString)) ``` -------------------------------- ### Request Tracking Authorization in Flutter App Source: https://github.com/deniza/app_tracking_transparency/blob/master/README.md Import the package and call requestTrackingAuthorization within your app's initialization to display the consent dialog. This example shows how to initiate the request after the widget binding is ensured. ```dart // Import package import 'package:app_tracking_tracking_transparency/app_tracking_transparency.dart'; void main() { runApp(MyApp()); } class MyApp extends StatefulWidget { @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State { @override void initState() { super.initState(); // Show tracking authorization dialog and ask for permission WidgetsFlutterBinding.ensureInitialized().addPostFrameCallback((_) async { final status = await AppTrackingTransparency.requestTrackingAuthorization(); }); } @override Widget build(BuildContext context) { ... } } ``` -------------------------------- ### Simulator Behavior for Advertising Identifier Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/implementation-details.md On the iOS simulator, `ASIdentifierManager.shared().advertisingIdentifier` always returns a string of zeros, regardless of authorization status, iOS version, or installed apps. Testing IDFA logic should always be done on a physical device. ```swift let uuid = ASIdentifierManager.shared().advertisingIdentifier // Always returns: 00000000-0000-0000-0000-000000000000 // Regardless of: // - Authorization status // - iOS version // - Installed apps // - Simulator state ``` -------------------------------- ### Show Explainer Dialog Before Request Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/quick-reference.md Implement Apple's recommended pattern by showing an explainer dialog before requesting tracking authorization. This helps users understand why you need their data. ```dart final status = await AppTrackingTransparency.trackingAuthorizationStatus; if (status == TrackingStatus.notDetermined) { // Show explainer await showDialog( context: context, barrierDismissible: false, builder: (context) => AlertDialog( title: Text('Data Privacy'), content: Text('We use your data for personalized ads.'), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text('Continue'), ), ], ), ); await Future.delayed(Duration(milliseconds: 200)); // Request authorization await AppTrackingTransparency.requestTrackingAuthorization(); } ``` -------------------------------- ### Configure Info.plist for Tracking Usage Description Source: https://github.com/deniza/app_tracking_transparency/blob/master/README.md Add the NSUserTrackingUsageDescription key to your Info.plist file with a string explaining why you need user tracking permission. ```xml NSUserTrackingUsageDescription This identifier will be used to deliver personalized ads to you. ``` -------------------------------- ### Framework Imports for iOS Implementation Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/api-reference/Native-iOS-Implementation.md Lists the necessary framework imports for both iOS implementations of the App Tracking Transparency plugin. These include Flutter, UIKit, AppTrackingTransparency, and AdSupport. ```swift import Flutter // Flutter framework import UIKit // iOS UI framework import AppTrackingTransparency // iOS 14+ ATT framework import AdSupport // IDFA and advertising identifier framework ``` -------------------------------- ### Get IDFA After Authorization Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/quick-reference.md Retrieve the Advertising Identifier (IDFA) only after the user has granted tracking authorization. This ensures you have permission to access the identifier. ```dart final status = await AppTrackingTransparency.trackingAuthorizationStatus; if (status == TrackingStatus.authorized) { final uuid = await AppTrackingTransparency.getAdvertisingIdentifier(); print('IDFA: $uuid'); } ``` -------------------------------- ### Get Advertising Identifier Source: https://github.com/deniza/app_tracking_transparency/blob/master/README.md Retrieve the device's advertising identifier after authorization has been granted. The identifier will be all zeros if authorization is denied or on simulators. ```dart final uuid = await AppTrackingTransparency.getAdvertisingIdentifier(); ``` -------------------------------- ### Flutter Plugin Registration in Swift Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/api-reference/Method-Channel-Protocol.md The standard Swift code for registering a Flutter plugin and setting up a method channel. ```swift public static func register(with registrar: FlutterPluginRegistrar) { let channel = FlutterMethodChannel(...) let instance = SwiftAppTrackingTransparencyPlugin() registrar.addMethodCallDelegate(instance, channel: channel) } ``` -------------------------------- ### Dart Call to Get Advertising Identifier Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/api-reference/Method-Channel-Protocol.md This snippet shows how to invoke the 'getAdvertisingIdentifier' method from Dart to retrieve the device's Advertising Identifier (IDFA). ```dart final String uuid = (await _channel.invokeMethod('getAdvertisingIdentifier'))!; ``` -------------------------------- ### iOS Podfile: Minimum Platform Version Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/setup-and-integration.md Set the minimum iOS platform version to 14.0 in your `ios/Podfile`. This is a requirement for the App Tracking Transparency plugin. ```ruby platform :ios, '14.0' ``` -------------------------------- ### iOS Frameworks Linking in Podfile Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/ios-frameworks.md Shows how to import AppTrackingTransparency and AdSupport frameworks in a Flutter project's ios/Podfile. These frameworks are automatically linked by the iOS SDK and do not require CocoaPods. ```ruby # iOS frameworks automatically linked in Swift import Flutter import UIKit import AppTrackingTransparency # iOS 14+ only import AdSupport # iOS 6+ ``` -------------------------------- ### Initialize Facebook Ads with ATT Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/setup-and-integration.md Initializes Facebook App Events after requesting tracking authorization and potentially retrieving the IDFA. Passes the IDFA to the Facebook SDK if available. ```dart import 'package:facebook_app_events/facebook_app_events.dart'; Future initializeFacebookAds() async { // Request tracking first final status = await AppTrackingTransparency.requestTrackingAuthorization(); if (status == TrackingStatus.authorized) { final uuid = await AppTrackingTransparency.getAdvertisingIdentifier(); // Pass IDFA to Facebook SDK if available } // Initialize Facebook App Events FacebookAppEvents.instance.logEvent( name: 'app_initialized', ); } ``` -------------------------------- ### Check Status and Get IDFA Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/setup-and-integration.md Query the current tracking authorization status and retrieve the advertising identifier if authorized. Handles cases where the IDFA is not available, falling back to SKAdNetwork. ```dart Future getAdvertisingData() async { final status = await AppTrackingTransparency.trackingAuthorizationStatus; String advertisingId = ''; if (status == TrackingStatus.authorized) { advertisingId = await AppTrackingTransparency.getAdvertisingIdentifier(); } final isTracked = advertisingId.isNotEmpty && advertisingId != '00000000-0000-0000-0000-000000000000'; if (isTracked) { print('IDFA available: $advertisingId'); } else { print('Using SKAdNetwork for conversion tracking'); } } ``` -------------------------------- ### Initialize Google Mobile Ads with ATT Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/setup-and-integration.md Initializes the Google Mobile Ads SDK after requesting tracking authorization. The SDK version 7.64.0+ automatically handles IDFA and SKAdNetwork integration. ```dart import 'package:google_mobile_ads/google_mobile_ads.dart'; Future initializeGoogleMobileAds() async { // Request tracking first await AppTrackingTransparency.requestTrackingAuthorization(); // Initialize Google Mobile Ads // Google Mobile Ads SDK 7.64.0+ automatically handles IDFA/SKAdNetwork await MobileAds.instance.initialize(); } ``` -------------------------------- ### Native Handler for Get Tracking Authorization Status Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/api-reference/Method-Channel-Protocol.md Swift code snippet for handling the 'getTrackingAuthorizationStatus' method call from Dart. It delegates the actual status retrieval to a platform-specific function. ```swift if (call.method == "getTrackingAuthorizationStatus") { getTrackingAuthorizationStatus(result: result) } ``` -------------------------------- ### Get Advertising Identifier (IDFA) Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/ios-frameworks.md Retrieves the device's Advertising Identifier (IDFA) as a UUID string. Handles different behaviors based on iOS version and authorization status. ```swift var advertisingIdentifier: UUID ``` ```swift let advertisingId = ASIdentifierManager.shared().advertisingIdentifier let idString = advertisingId.uuidString // "6D92078A-8246-4BA4-AE5B-76104861E7DC" ``` ```swift private func getAdvertisingIdentifier(result: @escaping FlutterResult) { result(String(ASIdentifierManager.shared().advertisingIdentifier.uuidString)) } ``` ```swift let uuid = ASIdentifierManager.shared().advertisingIdentifier if uuid.uuidString != "00000000-0000-0000-0000-000000000000" { print("IDFA available: \(uuid)") sendToAdNetwork(uuid) } else { print("IDFA not available, using SKAdNetwork") } ``` -------------------------------- ### Request Tracking with Custom Explainer Dialog (iOS) Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/setup-and-integration.md Implement a recommended pattern for requesting tracking authorization on iOS that first displays a custom explainer dialog before showing the system's tracking permission dialog. This follows Apple's guidelines for user privacy. ```dart Future requestTrackingWithExplainer(BuildContext context) async { final status = await AppTrackingTransparency.trackingAuthorizationStatus; if (status == TrackingStatus.notDetermined) { // Show custom explainer dialog await showDialog( context: context, barrierDismissible: false, builder: (context) => AlertDialog( title: const Text('Data Privacy'), content: const Text( 'We use your advertising identifier to show personalized ads. ' 'This helps us keep the app free.' ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Continue'), ), ], ), ); // Wait for dialog animation await Future.delayed(const Duration(milliseconds: 200)); // Show system dialog await AppTrackingTransparency.requestTrackingAuthorization(); } } ``` -------------------------------- ### Import Statement Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/quick-reference.md Import the App Tracking Transparency plugin to use its functionalities. ```dart import 'package:app_tracking_transparency/app_tracking_transparency.dart'; ``` -------------------------------- ### ATTrackingManager.trackingAuthorizationStatus Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/ios-frameworks.md Gets the current authorization status for tracking without displaying a dialog. This is a static read-only property that returns the cached device state and must be accessed from the main thread. ```APIDOC ## ATTrackingManager.trackingAuthorizationStatus ### Description Gets the current authorization status for tracking without displaying a dialog. This is a static read-only property that returns the cached device state and must be accessed from the main thread. ### Property `static var trackingAuthorizationStatus: ATTrackingManager.AuthorizationStatus` ### Type `ATTrackingManager.AuthorizationStatus` enum ### Access Static read-only property ### Availability iOS 14.0+ ### Thread Safety Main thread only ### Caching Returns cached device state ### Possible Values - `.notDetermined` (0) — User has not yet received authorization request - `.restricted` (1) — Device-level tracking disabled (parental controls, MDM, etc.) - `.denied` (2) — User explicitly denied authorization - `.authorized` (3) — User authorized tracking ### Example Usage ```swift let status = ATTrackingManager.trackingAuthorizationStatus switch status { case .notDetermined: print("Dialog can be shown") case .denied: print("User denied tracking") case .authorized: print("User authorized tracking") case .restricted: print("Tracking restricted by device policy") @unknown default: print("Unknown status") } ``` ``` -------------------------------- ### Request Tracking Authorization Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/api-reference/Native-iOS-Implementation.md Requests tracking authorization and displays the system dialog. This is typically called once per app install. Handles iOS 17.4 workaround by observing app activation. ```swift private func requestTrackingAuthorization(result: @escaping FlutterResult) ``` ```swift ATTrackingManager.requestTrackingAuthorization { [weak self] status in if status == .denied, ATTrackingManager.trackingAuthorizationStatus == .notDetermined { // Bug detected: status reports .denied but state is .notDetermined self?.addObserver(result: result) return } result(Int(status.rawValue)) } ``` -------------------------------- ### Show Explainer Dialog Before System Consent Source: https://github.com/deniza/app_tracking_transparency/blob/master/README.md If the system hasn't determined tracking status, display a custom explainer dialog before requesting the system's tracking authorization. Ensure the explainer dialog only has a 'Continue' button. ```dart // If the system can show an authorization request dialog if (await AppTrackingTransparency.trackingAuthorizationStatus == TrackingStatus.notDetermined) { // Show a custom explainer dialog before the system dialog await showCustomTrackingDialog(context); // Wait for dialog popping animation await Future.delayed(const Duration(milliseconds: 200)); // Request system's tracking authorization dialog await AppTrackingTransparency.requestTrackingAuthorization(); } ``` -------------------------------- ### Get Tracking Authorization Status in Plugin Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/ios-frameworks.md Checks if the current iOS version is 14 or later and retrieves the raw integer value of the tracking authorization status for use within the plugin. ```swift if #available(iOS 14, *) { result(Int(ATTrackingManager.trackingAuthorizationStatus.rawValue)) } ``` -------------------------------- ### Info.plist Configuration for SKAdNetwork Items Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/api-reference/AppTrackingTransparency.md This XML snippet demonstrates how to configure the `SKAdNetworkItems` key in the `Info.plist` file for iOS applications. It is recommended by Google Mobile Ads for ad network integration and requires a list of SKAdNetwork identifiers. ```xml SKAdNetworkItems ``` -------------------------------- ### PlatformException Handling in Dart Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/implementation-details.md Shows how to handle potential `PlatformException` errors when calling native methods from Dart. Includes a general catch block for unexpected errors. ```dart try { final status = await AppTrackingTransparency.trackingAuthorizationStatus; } on PlatformException catch (e) { print('Platform error: ${e.code}'); // Handle platform-specific errors } catch (e) { print('Unexpected error: $e'); // Fallback error handling } ``` -------------------------------- ### Info.plist NSUserTrackingUsageDescription Key Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/setup-and-integration.md This mandatory key in Info.plist provides the description shown to the user in the authorization dialog. Ensure this string is descriptive and explains why your app needs tracking access. ```xml NSUserTrackingUsageDescription Description shown in authorization dialog ``` -------------------------------- ### Get Tracking Authorization Status (iOS 14+) Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/api-reference/Native-iOS-Implementation.md Queries the current App Tracking Transparency authorization status without displaying a dialog. For iOS versions below 14, it returns a 'notSupported' status. ```swift private func getTrackingAuthorizationStatus(result: @escaping FlutterResult) ``` -------------------------------- ### Run Flutter App in Release Mode Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/setup-and-integration.md Command to run the Flutter application in release mode on a physical device. This is crucial for accurate testing of ATT features, as simulators have limitations. ```bash flutter run --release ``` -------------------------------- ### Dart Call to Get Tracking Authorization Status Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/api-reference/Method-Channel-Protocol.md Invokes the 'getTrackingAuthorizationStatus' method on the channel to query the current tracking authorization status without displaying a dialog. Assumes the method returns an integer. ```dart final int status = (await _channel.invokeMethod('getTrackingAuthorizationStatus'))!; ``` -------------------------------- ### Flutter App with ATT Request Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/quick-reference.md This snippet shows a complete Flutter application that requests tracking authorization upon startup. It handles the initial status check and displays the user's tracking status. Ensure `WidgetsFlutterBinding.ensureInitialized()` is called before `runApp()`. ```dart void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: HomePage(), ); } } class HomePage extends StatefulWidget { @override _HomePageState createState() => _HomePageState(); } class _HomePageState extends State { TrackingStatus? _status; @override void initState() { super.initState(); WidgetsFlutterBinding.ensureInitialized() .addPostFrameCallback((_) async { await _requestTracking(); }); } Future _requestTracking() async { final status = await AppTrackingTransparency.trackingAuthorizationStatus; if (status == TrackingStatus.notDetermined) { final uuid = await AppTrackingTransparency.getAdvertisingIdentifier(); if (uuid != '00000000-0000-0000-0000-000000000000') { print('IDFA: $uuid'); } } setState(() => _status = status); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('ATT Example')), body: Center( child: Text('Status: ${_status ?? "Loading"}') ), ); } } ``` -------------------------------- ### Get Tracking Authorization Status Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/ios-frameworks.md Retrieves the current app tracking authorization status using the AppTrackingTransparency framework. Returns a raw value integer or 4 if the OS is older than iOS 14. ```swift private func getTrackingAuthorizationStatus(result: @escaping FlutterResult) { if #available(iOS 14, *) { result(Int(ATTrackingManager.trackingAuthorizationStatus.rawValue)) } else { result(Int(4)) // notSupported } } ``` -------------------------------- ### Simple One-Shot Request Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/quick-reference.md Directly request tracking authorization from the user. This is useful when you need to show the permission dialog immediately. ```dart final status = await AppTrackingTransparency.requestTrackingAuthorization(); print('Status: $status'); ``` -------------------------------- ### Get Advertising Identifier Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/api-reference/AppTrackingTransparency.md Retrieves the device's Advertising Identifier (IDFA). Returns an empty string on Android. On iOS, it returns the actual IDFA if tracking authorization is granted, otherwise it returns all zeros. ```dart static Future getAdvertisingIdentifier() ``` -------------------------------- ### Initialize Tracking Safely with addPostFrameCallback Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/setup-and-integration.md Use `WidgetsFlutterBinding.ensureInitialized().addPostFrameCallback` to safely call native methods like `trackingAuthorizationStatus` after the widget tree has been built. This prevents errors that can occur when calling native code directly from `initState`. ```dart @override void initState() { super.initState(); // Use addPostFrameCallback to call native code after widget initialization WidgetsFlutterBinding.ensureInitialized() .addPostFrameCallback((_) => initializeTracking()); } Future initializeTracking() async { // Safe to call native methods here final status = await AppTrackingTransparency.trackingAuthorizationStatus; // Handle status... } ``` -------------------------------- ### Handling Unknown Methods in Swift Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/api-reference/Method-Channel-Protocol.md This Swift code snippet demonstrates how to handle method calls that are not explicitly implemented, returning FlutterMethodNotImplemented. ```swift else { result(FlutterMethodNotImplemented) } ``` -------------------------------- ### addObserver Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/api-reference/Native-iOS-Implementation.md Adds an observer for app activation notifications. This is used as a workaround for an iOS 17.4 bug to retry the tracking authorization request when the app becomes active. ```APIDOC ## addObserver(result:) ### Description Adds an observer for app activation to retry authorization, specifically for the iOS 17.4 bug workaround. ### Method ```swift private func addObserver(result: @escaping FlutterResult) ``` ### Parameters #### Path Parameters - **result** (FlutterResult) - Required - Callback to retry with when the app becomes active. ### Implementation Details Registers an observer for `UIApplication.didBecomeActiveNotification` on the main queue. When the app becomes active, it retries the `requestTrackingAuthorization` using the captured result callback. It uses weak self to prevent retain cycles. ### Behavior - Removes any existing observer via `removeObserver()` before adding a new one. - Observes on the main queue. - Captures the result callback in the closure. - When the app becomes active, it retries `requestTrackingAuthorization`. - Uses `weak self` to prevent retain cycles. ### Use Case - Only called when the iOS 17.4 bug is detected. - Retries authorization when the user returns to the app from Settings or other screens. - Clears the observer when a new request is made or the observer is manually removed. ``` -------------------------------- ### AppTrackingTransparency Dart API Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/README.md Provides static methods to interact with the App Tracking Transparency framework on iOS. Use `trackingAuthorizationStatus` to get the current status without a dialog, and `requestTrackingAuthorization` to prompt the user. `getAdvertisingIdentifier` retrieves the IDFA after authorization. ```dart class AppTrackingTransparency { // Get current status without dialog static Future get trackingAuthorizationStatus // Request authorization (shows dialog first time only) static Future requestTrackingAuthorization() // Get IDFA after authorization static Future getAdvertisingIdentifier() } ``` -------------------------------- ### Conditional Availability Check for iOS Versions Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/ios-frameworks.md Demonstrates how to use conditional compilation to support older iOS versions when using newer framework APIs like AppTrackingTransparency. ```swift if #available(iOS 14, *) { // Use AppTrackingTransparency APIs let status = ATTrackingManager.trackingAuthorizationStatus ATTrackingManager.requestTrackingAuthorization { ... } } else { // Return notSupported for older iOS result(Int(4)) } ``` ```swift // Always safe: AdSupport on iOS 6+ let uuid = ASIdentifierManager.shared().advertisingIdentifier // Requires check: AppTrackingTransparency on iOS 14+ if #available(iOS 14, *) { ATTrackingManager.requestTrackingAuthorization { ... } } ``` -------------------------------- ### Request Format for getAdvertisingIdentifier Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/api-reference/Method-Channel-Protocol.md The expected JSON format for requesting the advertising identifier from the native side. ```json { method: "getAdvertisingIdentifier", arguments: null } ``` -------------------------------- ### Custom Logging for Request Tracking Authorization Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/implementation-details.md The current plugin version does not include built-in logging. Custom logging can be added to native code, for example, to print debug messages when `requestTrackingAuthorization` is called and when a status is received, including detection for potential iOS 17.4 bugs. ```swift // In native plugin private func requestTrackingAuthorization(result: @escaping FlutterResult) { print("DEBUG: requestTrackingAuthorization called") if #available(iOS 14, *) { ATTrackingManager.requestTrackingAuthorization { [weak self] status in print("DEBUG: Got status \(status.rawValue)") if status == .denied, ATTrackingManager.trackingAuthorizationStatus == .notDetermined { print("DEBUG: iOS 17.4 bug detected") self?.addObserver(result: result) return } result(Int(status.rawValue)) } } } ``` -------------------------------- ### Plugin Registration in pubspec.yaml Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/implementation-details.md Declare the iOS platform for the plugin in the pubspec.yaml file. Flutter uses this to automatically register the plugin class. ```yaml flutter: plugin: platforms: ios: pluginClass: AppTrackingTransparencyPlugin ``` -------------------------------- ### Deferred Authorization Request Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/setup-and-integration.md Demonstrates how to defer the tracking authorization request until the second or later app launch using SharedPreferences to track launch count. Includes a note about informing App Store reviewers. ```dart final prefs = await SharedPreferences.getInstance(); int launchCount = prefs.getInt('launch_count') ?? 0; if (launchCount > 1) { // Request tracking on second+ launch await AppTrackingTransparency.requestTrackingAuthorization(); } await prefs.setInt('launch_count', launchCount + 1); ``` -------------------------------- ### Full State Handling with Switch Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/quick-reference.md Handle all possible tracking authorization statuses using a switch statement. This provides a comprehensive way to manage user consent and device states. ```dart final status = await AppTrackingTransparency.requestTrackingAuthorization(); switch (status) { case TrackingStatus.authorized: print('Allowed'); break; case TrackingStatus.denied: print('Denied'); break; case TrackingStatus.restricted: print('Restricted'); break; case TrackingStatus.notDetermined: print('Dialog not shown'); break; case TrackingStatus.notSupported: print('Not iOS 14+'); break; } ``` -------------------------------- ### Method Channel Protocol Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/README.md Defines the methods available through the platform's method channel for interacting with native iOS functionalities. ```APIDOC ## Method Channel Protocol ### Description Defines the methods available through the platform's method channel for interacting with native iOS functionalities. ### Methods | Method | Returns | Purpose | |---|---|---| | `getTrackingAuthorizationStatus` | Int (0-4) | Query the current tracking authorization status. | | `requestTrackingAuthorization` | Int (0-4) | Request tracking authorization from the user, showing the system dialog if necessary. | | `getAdvertisingIdentifier` | String UUID | Retrieve the Advertising Identifier (IDFA) after authorization has been granted. | ``` -------------------------------- ### Method Channel Request Format Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/api-reference/Native-iOS-Implementation.md Illustrates the format for method calls sent from Dart to the native iOS implementation. It specifies the method name and that arguments are typically null. ```dart FlutterMethodCall { method: "methodName" // One of: getTrackingAuthorizationStatus, requestTrackingAuthorization, getAdvertisingIdentifier arguments: null // No arguments passed } ``` -------------------------------- ### Check if Authorization Dialog Can Be Shown Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/types.md Use this pattern to check if the user has already made a choice or if the authorization dialog can be presented. If the status is 'notDetermined', you can proceed to show an explainer dialog and then request authorization. ```dart final status = await AppTrackingTransparency.trackingAuthorizationStatus; if (status == TrackingStatus.notDetermined) { // Show explainer dialog, then request authorization await showExplainerDialog(); await AppTrackingTransparency.requestTrackingAuthorization(); } ``` -------------------------------- ### Requesting Tracking Authorization in Swift Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/implementation-details.md This Swift code demonstrates requesting tracking authorization. It uses a weak self capture to prevent retain cycles and ensures the completion handler is executed on the main thread. ```swift ATTrackingManager.requestTrackingAuthorization { [weak self] status in // Executed on main thread // weak self prevents retain cycles // Completion handler called exactly once self?.handle(status, result: result) } ``` -------------------------------- ### Method Channel Handling Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/implementation-details.md Handles incoming method calls from the Flutter platform, routing them to the appropriate native implementation based on the method name. Unimplemented methods return `FlutterMethodNotImplemented`. ```swift public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { if (call.method == "getTrackingAuthorizationStatus") { getTrackingAuthorizationStatus(result: result) } else if (call.method == "requestTrackingAuthorization") { requestTrackingAuthorization(result: result) } else if (call.method == "getAdvertisingIdentifier") { getAdvertisingIdentifier(result: result) } else { result(FlutterMethodNotImplemented) // Only explicit error } } ``` -------------------------------- ### Handle Tracking Authorization Request Errors Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/quick-reference.md Use a try-catch block to handle potential platform exceptions or unexpected errors during the tracking authorization request. This ensures graceful failure and provides informative logs. ```dart try { final status = await AppTrackingTransparency.requestTrackingAuthorization(); if (status == TrackingStatus.authorized) { // Success } else if (status == TrackingStatus.denied) { // User denied } else if (status == TrackingStatus.restricted) { // Device restricted } else if (status == TrackingStatus.notSupported) { // Wrong platform/version } } on PlatformException catch (e) { print('Platform error: ${e.code} - ${e.message}'); } catch (e) { print('Unexpected error: $e'); } ``` -------------------------------- ### Weak Self Usage in Closure Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/implementation-details.md Demonstrates the use of `[weak self]` within a closure to prevent retain cycles, ensuring the plugin instance can be deallocated if necessary. Safe access to self is done via optional chaining. ```swift ATTrackingManager.requestTrackingAuthorization { [weak self] status in self?.handle(status) } ``` -------------------------------- ### ATTrackingManager.requestTrackingAuthorization Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/ios-frameworks.md Displays the system authorization dialog to the user and captures their response asynchronously. Subsequent calls return the cached state without showing the dialog. Requires the `NSUserTrackingUsageDescription` key in Info.plist. ```APIDOC ## ATTrackingManager.requestTrackingAuthorization ### Description Displays the system authorization dialog to the user and captures their response asynchronously. Subsequent calls return the cached state without showing the dialog. Requires the `NSUserTrackingUsageDescription` key in Info.plist. ### Method `class func requestTrackingAuthorization( completionHandler: @escaping (ATTrackingManager.AuthorizationStatus) -> Void )` ### Parameters #### Completion Handler - `completionHandler` (@escaping (AuthorizationStatus) -> Void) - Async callback with user's choice ### Behavior - First call: Shows system authorization dialog - Subsequent calls: Returns cached authorization state without dialog - Dialog contains text from `NSUserTrackingDescription` in Info.plist ### Dialog Requirements - Info.plist must contain `NSUserTrackingUsageDescription` key - If key missing, nothing happens (no error, no dialog) - User sees predefined iOS system dialog (cannot customize) ### Thread Requirements - Must be called from main thread - Completion handler called on main thread ### Completion Handler Timing - Called immediately when user responds in dialog - Not called if dialog fails to show - Called exactly once per request ### iOS 17.4 Bug Impact On iOS 17.4, the completion handler may report `.denied` status while `trackingAuthorizationStatus` remains `.notDetermined`. This indicates the user's gesture was not properly recorded. A workaround is implemented in the plugin. ### Example Usage ```swift ATTrackingManager.requestTrackingAuthorization { status in switch status { case .authorized: print("User granted tracking") enableTracking() case .denied: print("User denied tracking") disableTracking() case .restricted: print("Tracking restricted") case .notDetermined: print("Dialog not shown") @unknown default: print("Unknown") } } ``` ``` -------------------------------- ### Simulator IDFA Limitation Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/setup-and-integration.md Illustrates that on a simulator, the advertising identifier (IDFA) will always return a string of all zeros, regardless of the actual authorization status. ```dart // On simulator, IDFA always returns all zeros final uuid = await AppTrackingTransparency.getAdvertisingIdentifier(); print(uuid); // Always: 00000000-0000-0000-0000-000000000000 ``` -------------------------------- ### SwiftAppTrackingTransparencyPlugin Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/api-reference/Native-iOS-Implementation.md The main native plugin implementation that handles communication between Flutter method channels and native iOS APIs for App Tracking Transparency and AdSupport. ```APIDOC ## SwiftAppTrackingTransparencyPlugin Main native plugin implementation supporting Flutter method channel communication. **Source:** `ios/Classes/SwiftAppTrackingTransparencyPlugin.swift` ### Class Definition ```swift public class SwiftAppTrackingTransparencyPlugin: NSObject, FlutterPlugin { private var observer: NSObjectProtocol? public static func register(with registrar: FlutterPluginRegistrar) public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) } ``` ### Static Methods #### register(with:) Registers the plugin with the Flutter engine and initializes the method channel. ```swift public static func register(with registrar: FlutterPluginRegistrar) ``` | Parameter | Type | Description | |-----------|------|-------------| | registrar | FlutterPluginRegistrar | Flutter plugin registry for channel registration | **Implementation Details:** - Creates `FlutterMethodChannel` named `app_tracking_transparency` - Instantiates `SwiftAppTrackingTransparencyPlugin()` - Registers instance as method call delegate on the channel - Called automatically by Flutter during plugin initialization --- ### Instance Methods #### handle(_:result:) Dispatches incoming method calls from Flutter to appropriate handlers. ```swift public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) ``` | Parameter | Type | Description | |-----------|------|-------------| | call | FlutterMethodCall | Method call containing method name and arguments | | result | FlutterResult | Callback to return result to Dart | **Supported Methods:** - `getTrackingAuthorizationStatus` → calls `getTrackingAuthorizationStatus(result:)` - `requestTrackingAuthorization` → calls `requestTrackingAuthorization(result:)` - `getAdvertisingIdentifier` → calls `getAdvertisingIdentifier(result:)` - Other methods → returns `FlutterMethodNotImplemented` **Return Type:** Void (result callback invoked asynchronously) **Implementation Details:** - Routes method calls via string matching - Each method invokes corresponding handler function - Result callback is passed to handlers for async completion - Unknown methods return `FlutterMethodNotImplemented` --- #### getTrackingAuthorizationStatus(result:) Queries current tracking authorization status without showing a dialog. ```swift private func getTrackingAuthorizationStatus(result: @escaping FlutterResult) ``` | Parameter | Type | Description | |-----------|------|-------------| | result | FlutterResult | Callback to return status as Int | **Return Value:** Integer (0-4) representing `TrackingStatus`: - `0` → notDetermined - `1` → restricted - `2` → denied - `3` → authorized - `4` → notSupported **iOS Availability:** - iOS 14+: Returns `ATTrackingManager.trackingAuthorizationStatus.rawValue` as Int - iOS <14: Returns `4` (notSupported) **Implementation Details:** - Checks iOS 14+ availability with `#available(iOS 14, *)` - Accesses `ATTrackingManager.trackingAuthorizationStatus` - Converts enum raw value to Int and passes to result callback - Non-blocking query of cached authorization state ``` -------------------------------- ### Safe Initialization in Widget Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/quick-reference.md Safely initialize and check tracking status within a Flutter widget's lifecycle using `addPostFrameCallback`. This ensures the widget is built before checking the status. ```dart @override void initState() { super.initState(); WidgetsFlutterBinding.ensureInitialized() .addPostFrameCallback((_) async { final status = await AppTrackingTransparency.trackingAuthorizationStatus; setState(() => _status = status); }); } ``` -------------------------------- ### requestTrackingAuthorization Source: https://github.com/deniza/app_tracking_transparency/blob/master/_autodocs/api-reference/AppTrackingTransparency.md Displays the iOS system App Tracking Transparency authorization dialog and requests user permission to track. This method should be called after presenting a custom explainer dialog. ```APIDOC ## requestTrackingAuthorization() ### Description Displays the iOS system App Tracking Transparency authorization dialog and requests user permission to track. The dialog is displayed only once; subsequent calls return the user's previous choice without showing the dialog again. ### Method POST ### Endpoint AppTrackingTransparency.requestTrackingAuthorization() ### Parameters #### Path Parameters No parameters #### Query Parameters No parameters ### Response #### Success Response - **requestTrackingAuthorization** (Future) - The user's authorization choice as a `TrackingStatus`. Possible values: `notDetermined`, `restricted`, `denied`, `authorized`, or `notSupported`. ### Response Example { "requestTrackingAuthorization": "authorized" } ### Platform Support: - iOS 14+: Displays system dialog and returns authorization status - iOS <14 or Android: Returns `TrackingStatus.notSupported` ### Dialog Behavior: - Only displays once after first call (iOS system behavior) - After user makes a choice, subsequent calls return the same status without showing dialog - Must have `NSUserTrackingUsageDescription` key in `Info.plist` or dialog will not appear ### Important Notes: - Apple requires an explainer dialog shown before this system dialog - Cannot be called while another system dialog is already displayed (will be forcefully closed) - Should be called from main thread context - Implements workaround for iOS 17.4 authorization state bug via observer pattern ```