iosflutteradmobapp-storeapp-store-connect

App Store Rejection Due to Guideline 5.1.2 - Issues with AdMob Consent and Tracking Permissions in Flutter App


I recently published my Flutter app in the App Store, and it was initially accepted. The app included AdMob consent forms and ad codes, but I hadn't connected AdMob to my app in the AdMob dashboard during the first review. This likely caused the GDPR consent form to not display, allowing the app to pass the initial review.

However, my app update was rejected due to Guideline 5.1.2 - Legal - Privacy - Data Use and Sharing. Here's the screenshot of the rejection notice, highlighting the issue with the consent form.

Apple Rejection Message

Consent Form

I'm unsure how to proceed and need some guidance. Specifically, I'm confused about the correct approach for handling consent forms and tracking permissions. Here are my questions and understandings:

I assume the first approach is correct. To implement this, I've added the following:

In pubspec.yaml:

permission_handler: ^11.3.0

In info.plist:

<key>NSUserTrackingUsageDescription</key>
<string>Your description for why you need the tracking permission</string>

In my Dart file:

Future<void> _requestTrackingPermission() async {
  if (Platform.isIOS) {
    var status = await Permission.appTrackingTransparency.status;
    print('Current tracking status: $status');
    if (status == PermissionStatus.denied || status == PermissionStatus.permanentlyDenied) {
      status = await Permission.appTrackingTransparency.request();
    }
    setState(() {
      _isTrackingPermissionGranted = status == PermissionStatus.granted;
    });
    _storeTrackingPermissionStatus(_isTrackingPermissionGranted);

    if (_isTrackingPermissionGranted) {
      await _requestAndShowConsentForm();
    } else {
      _storeConsentStatus(ConsentStatus.unknown);
      _loadNonPersonalizedAds();
    }
  } else if (Platform.isAndroid) {
    await _requestAndShowConsentForm();
  }
}

Future<void> _storeTrackingPermissionStatus(bool isGranted) async {
  final prefs = await SharedPreferences.getInstance();
  prefs.setBool('tracking_permission_granted', isGranted);
}

@override
void initState() {
  super.initState();
  _initializeMobileAds();
  _requestTrackingPermission();
  _loadItems();
  _loadDarkModePreference();
  _loadLocalePreference();
  WidgetsBinding.instance.addPostFrameCallback((_) async {
    await _showAppOpenAd();
  });
}

When I run this on an iOS device, I see in the console:

Current tracking status: PermissionStatus.denied or Current tracking status: PermissionStatus.permanentlyDenied

After researching, I saw that if the current tracking status is PermissionStatus.denied, the user has previously denied the permission request. The user must be guided to the app settings to manually enable tracking permissions.

I added these codes:

Future<void> _openAppSettings() async {
  bool opened = await openAppSettings();
  if (!opened) {
    print("Failed to open app settings.");
  }
}

And updated _requestTrackingPermission:

Future<void> _requestTrackingPermission() async {
  if (Platform.isIOS) {
    var status = await Permission.appTrackingTransparency.status;
    print('Current tracking status: $status');
    if (status == PermissionStatus.denied || status == PermissionStatus.permanentlyDenied) {
      bool userConfirmed = await _showPermissionDialog();
      if (userConfirmed) {
        await _openAppSettings();
      }
    } else if (status != PermissionStatus.granted) {
      status = await Permission.appTrackingTransparency.request();
    }
    setState(() {
      _isTrackingPermissionGranted = status == PermissionStatus.granted;
    });
    _storeTrackingPermissionStatus(_isTrackingPermissionGranted);

    if (_isTrackingPermissionGranted) {
      await _requestAndShowConsentForm();
    } else {
      _storeConsentStatus(ConsentStatus.unknown);
      _loadNonPersonalizedAds();
    }
  } else if (Platform.isAndroid) {
    await _requestAndShowConsentForm();
  }
}

Future<bool> _showPermissionDialog() async {
  return showDialog<bool>(
    context: context,
    builder: (BuildContext context) {
      return AlertDialog(
        title: Text('Tracking Permission Required'),
        content: Text('This app requires tracking permission to show personalized ads. Please enable it in the app settings.'),
        actions: [
          TextButton(
            onPressed: () {
              Navigator.of(context).pop(false);
            },
            child: Text('Cancel'),
          ),
          TextButton(
            onPressed: () {
              Navigator.of(context).pop(true);
            },
            child: Text('Open Settings'),
          ),
        ],
      );
    },
  ) ?? false;
}

I see the "Permission Denied - Open Settings" dialog, but when I go to settings, there is no option for tracking. I also checked Settings -> Privacy & Security -> Tracking and my app is not listed there.

Permission Denied Popup

Phone Settings

What's my mistake here? Is my approach about showing the consent form after allowing tracking correct, if so what is my mistake in codes or am I completely wrong?

Thank you for your help!


Solution

  • UPDATE

    Yes, my approach is correct. If you want to use AdMob ads with GDPR Consent Form in iOS, you need to show Tracking Transparency dialog first, then you can show consent form. And to fix showing Tracking Transparency dialog, I used permission_handler package earlier and I believe that was the problem. I changed the package to this: app_tracking_transparency and that solved my problem. Then I updated my app and it's accepted.