- Published on
- ยท12 min read
Applying AdMob GDPR in React Native (Android)
I apologize in advance for any awkward expressions in English.
English is not my native language, and I have relied on ChatGPT's assistance to proceed with the translation.
Overview
AdMob keeps warning to display GDPR messages.
Since creating GDPR messages in AdSense was straightforward, I assumed it would be similar for AdMob. However, AdMob required additional code in the application to directly check for consent. ๐ฑ
I'll share the details of what I implemented in the Simple Vocab Buddy app.
For the implementation on Android, you can refer to Applying AdMob GDPR on Android.
๐ Caution
โ I do not assume legal responsibility. โ
I implemented this based on Google documentation, sample codes, and searches. However, there may be incorrect interpretations regarding policies or implementations. Please use this as a reference only.
Although I confirmed normal operation in test mode on a test device, I did not verify the actual behavior on devices in the EEA region. Additionally, I have not verified its operation on iOS.
GDPR Message Setup
Go to Google AdMob > GDPR > Click on [Create Message]
button.
Configure the settings according to your app. Here are my settings.
- My apps: I selected the app to apply the message to.
- Language: I specified 'English' as the language for displaying the message.
- Not consenting:
Enabled
- Close (Not consenting):
Disabled
After creating and publishing the message, you need to implement the code for the configured GDPR message to be displayed.
Result Screen
This is the screen observed during testing.
On the left is the message displayed when the app is launched.
On the right, a Change GDPR Settings
button has been added to the settings to allow changing GDPR settings. Clicking this button displays a message screen similar to the left image.
Implementation
1. Modify proguard-rules.pro
Following the guidance from React Native Google Mobile Ads - European User Consent, add the following content to the android/app/proguard-rules.pro
file.
-keep class com.google.android.gms.internal.consent_sdk.** { *; }
2. Delaying app measurement
Set the delay_app_measurement_init
value to true
in the react-native-google-mobile-ads
configuration in the app.json
file.
{
"react-native-google-mobile-ads": {
"android_app_id": "ca-app-pub-xxxxxxxx~xxxxxxxx",
"ios_app_id": "ca-app-pub-xxxxxxxx~xxxxxxxx",
"delay_app_measurement_init": true
}
}
3. Code
I've created a common function file, gdprAdsConsent.ts, for loading GDPR consent messages.
gdprAdsConsent.ts
// https://docs.page/invertase/react-native-google-mobile-ads/european-user-consent
import mobileAds, {AdsConsent, AdsConsentDebugGeography} from 'react-native-google-mobile-ads';
const debugParams = {
debugGeography: AdsConsentDebugGeography.EEA,
testDeviceIdentifiers: ['Your-Test-Device-Hashed-Id'],
}
// Initialize AdsMob
function initializeMobileAdsSdk() {
mobileAds()
.initialize()
.then(adapterStatuses => {
console.log("mobileAds initialize:", adapterStatuses);
});
}
// Check consent status
async function checkIsNotAgreement() {
const {
activelyScanDeviceCharacteristicsForIdentification,
applyMarketResearchToGenerateAudienceInsights,
createAPersonalisedAdsProfile,
createAPersonalisedContentProfile,
developAndImproveProducts,
measureAdPerformance,
measureContentPerformance,
selectBasicAds,
selectPersonalisedAds,
selectPersonalisedContent,
storeAndAccessInformationOnDevice,
usePreciseGeolocationData,
} = await AdsConsent.getUserChoices();
return (
applyMarketResearchToGenerateAudienceInsights === false ||
createAPersonalisedAdsProfile === false ||
createAPersonalisedContentProfile === false ||
developAndImproveProducts === false ||
measureAdPerformance === false ||
measureContentPerformance === false ||
selectBasicAds === false ||
selectPersonalisedAds === false ||
selectPersonalisedContent === false ||
storeAndAccessInformationOnDevice === false ||
usePreciseGeolocationData === false
)
}
async function requestConsent() {
return await AdsConsent.requestInfoUpdate();
//return await AdsConsent.requestInfoUpdate(debugParams); // Add debugParams during testing
}
// Load GDPR consent form and check consent status
export async function loadGdprAdsConsent(
) {
try{
//await AdsConsent.reset(); // Uncomment for testing
const data = await requestConsent();
if(data.isConsentFormAvailable){
const resultForm = await AdsConsent.loadAndShowConsentFormIfRequired();
// If the user has already consented or refused consent, check the consent status and request consent if needed
if(data.status === 'OBTAINED'){
const isNotAgreement = await checkIsNotAgreement();
if(isNotAgreement) {
await AdsConsent.showForm();
}
}
if(resultForm.canRequestAds === true) {
initializeMobileAdsSdk()
}
} else {
initializeMobileAdsSdk()
}
}
catch(error) {
console.log('loadGdprAdsConsent > error: ', error);
initializeMobileAdsSdk();
}
}
// Check if Consent is Available (EEA region)
export async function checkIsConsentAvailable() {
try{
const data = await requestConsent();
//console.log('checkConsentAvailable > data: ', data);
return data.isConsentFormAvailable;
}catch (error) {
console.log('checkConsentAvailable > error: ', error);
return false;
}
}
// Show consent form on button click or screen navigation, and check consent status afterward
export async function showAdsConsentForm(
consentCallback?: () => void,
notConsentCallback?: () => void,
) {
try{
const data = await requestConsent();
if(data.isConsentFormAvailable){
const isNotAgreement = await checkIsNotAgreement();
if(isNotAgreement) {
await AdsConsent.showForm();
// Re-check consent status
const isNotAgreementResult = await checkIsNotAgreement();
if(isNotAgreementResult) {
// Handle non-consent
notConsentCallback && notConsentCallback();
} else {
// Handle consent
consentCallback && consentCallback();
}
} else {
// Handle consent
consentCallback && consentCallback();
}
}
else {
// Handle case where no consent form is available
consentCallback && consentCallback();
}
}catch (error) {
console.log('showAdsConsentForm > error: ', error);
}
}
// Show privacy options form in settings
export async function showPrivacyOptionsForm() {
try{
const data = await requestConsent();
if(data.isConsentFormAvailable){
await AdsConsent.showPrivacyOptionsForm()
}
}catch (error) {
console.log('showPrivacyOptionsForm > error: ', error);
}
}
- The verification of consent status (
checkIsNotAgreement
) can be conditional based on the specific consents required.- Since it may not be clear under which conditions the ads are displayed correctly, I chose to examine the property set to
true
when the "Consent" button is clicked and used that to determine the consent status.
- Since it may not be clear under which conditions the ads are displayed correctly, I chose to examine the property set to
4. Test Configuration
Change 'Your-Test-Device-Hashed-ID'
- Enter the following command in the terminal to check the device's hashed ID.
- Look for the log entry:
Use RequestConfiguration.Builder().setTestDeviceIds(Arrays.asList("Your-Test-Device-Hashed-ID")) to get test ads on this device.
adb shell "logcat -d | grep Ads"
- Apply test configuration using
debugParams
- โ Use this only for testing purposes, and remove
debugParams
during deployment. โ
async function requestConsent() {
//return await AdsConsent.requestInfoUpdate();
return await AdsConsent.requestInfoUpdate(debugParams); // Add debugParams during testing
}
5. Implementation
- Load GDPR on app launch and request consent if not given.
// App.tsx
import React, { useEffect } from 'react';
import AppNavigator from './navigation/AppNavigator';
import { loadGdprAdsConsent } from './api/gdprAdsConsent';
export default function App() {
useEffect(() => {
loadGdprAdsConsent().then(() => {
console.log("loadGdprAdsConsent completed");
}).catch((err) => {
console.log("loadGdprAdsConsent err: ", err);
});
}, []);
return (
<AppNavigator />
);
}
- If consent is not given, request consent when the user clicks the "New Group" or "Create Group" button.
// When the user clicks the "New Group" button
const onClickNewGroup = () => {
showAdsConsentForm(() => {
GlobalModal.showPopup(<GroupCreateModal refresh={refresh} />, t('create_group'))
}).then(() => {})
}
// When navigating to the settings
const goToSetting = () => {
showAdsConsentForm(() => {
navigation.navigate('Setting');
}).then(() => {})
}
- Added a button in the settings screen to allow the user to change their consent status.
export const SettingScreen = () => {
const [isConsentAvailable, setIsConsentAvailable] = useState(false);
useEffect(() => {
checkIsConsentAvailable().then((isAvailable) => {
setIsConsentAvailable(isAvailable);
})
}, []);
return (
<View style={globalStyles.container}>
{isConsentAvailable &&
<TouchableOpacity style={styles.listContainer} onPress={() => showPrivacyOptionsForm()}>
<AppText style={styles.listTitle}>Change GDPR Settings</AppText>
</TouchableOpacity>}
</View>
);
}
If the GDPR message is not loading:
- Check on the Google AdMob website whether GDPR has been published for the app.
- Confirm that the 'Your-Test-Device-Hashed-ID' is correct.
When testing in test mode โ non-test mode, and the previous settings persist, causing testing issues:
- Test mode: Use
AdsConsent.requestInfoUpdate(debugParams);
, uncommentawait AdsConsent.reset();
to reset the settings, then comment it back after resetting. - Non-test mode: Use
AdsConsent.requestInfoUpdate();
, uncommentawait AdsConsent.reset();
to reset the settings, then comment it back after resetting.
- Test mode: Use
If the loading behavior is incorrect (e.g., loading when it shouldn't or not loading when it should):
- The selected information is stored in the Local Storage. Deleting the app's storage and relaunching resolved the issue.
- If the above step doesn't work, as a last resort, uninstall the app and reinstall it to ensure proper functionality.