invertase / react-native-google-mobile-ads

React Native Google Mobile Ads enables you to monetize your app with AdMob.
https://docs.page/invertase/react-native-google-mobile-ads
Other
634 stars 126 forks source link

Possible issue on setting requests configuration and ads content rating #159

Closed mcama closed 1 year ago

mcama commented 2 years ago

Hey there!

First off, thanks for this amazing library!

I'm currently using react-native-google-mobile-ads v6.0.0 and react-native v0.61.5. Ads are showing perfectly but I came into an issue regarding ad content not being suitable for the app's intended target audience, which are children and teenagers (under aged).

This is the code I'm using in my project's App.js file to perform the request configuration and SDK initialization:

useEffect(() => {
    // Configure outbound requests
    mobileAds()
        .setRequestConfiguration({
            maxAdContentRating: MaxAdContentRating.PG, // Update all future requests suitable for parental guidance
            tagForChildDirectedTreatment: true, // Content treated as child-directed for purposes of COPPA
            tagForUnderAgeOfConsent: true, // Ad request handled in a manner suitable for users under the age of consent
        })
        .then(() => {
            // Initialize Google Mobile Ads SDK
            mobileAds().initialize();
        });
}, []);

Then, to show the ads, I use the following custom hook useInterstitialAd:

import {useEffect, useState} from 'react';
import {InterstitialAd, AdEventType} from 'react-native-google-mobile-ads';

const adUnitId = 'ca-app-pub-xxxxxxxx~xxxxxxxx'; // Using a real Ad Unit ID here

const interstitialAd = InterstitialAd.createForAdRequest(adUnitId, {
    requestNonPersonalizedAdsOnly: true,
});

export default function useInterstitialAd() {
    const [loaded, setLoaded] = useState(false);
    const [opened, setOpened] = useState(false);
    const [closed, setClosed] = useState(false);
    const [error, setError] = useState(null);

    useEffect(() => {
        interstitialAd.addAdEventsListener(({type, payload}) => {
            if (type === AdEventType.LOADED) {
                setLoaded(true);
            }

            if (type === AdEventType.OPENED) {
                setOpened(true);
            }

            if (type === AdEventType.CLOSED) {
                setClosed(true);
            }

            if (type === AdEventType.ERROR) {
                setError(payload);
            }
        });

        // Start loading the interstitial straight away
        interstitialAd.load();

        // Unsubscribe from all events on unmount
        return () => interstitialAd.removeAllListeners();
    }, []);

    function show() {
        interstitialAd.show();
    }

    function isReadyToShow() {
        return (
            interstitialAd &&
            interstitialAd.loaded &&
            !interstitialAd.error &&
            !interstitialAd.closed
        );
    }

    return {
        loaded,
        opened,
        closed,
        error,
        show,
        isReadyToShow,
    };
}

And finally, I'm showing the ad in the intended screen by using:

const interstitialAd = useInterstitialAd();

useEffect(() => {
    if (interstitialAd.isReadyToShow()) {
        interstitialAd.show();
    }
}, [interstitialAd]);

Any insights on why the issue commented above would be happening?

Thanks in advance for your help and sorry in advance as well if this is just a missconfiguration issue!

mikehardy commented 2 years ago

react-native v0.61.5

I would be careful with this, specifically I'm not sure how you even build with android API30/API31 (required for play store isn't it? or Xcode 13+ (required for app store submissions in just a couple months) and as part of the react-native release-testers group I'm deeply involved in solving compile / release problems with new versions. You should schedule an upgrade ASAP to at least 0.67.x, better 0.68.x - https://react-native-community.github.io/upgrade-helper/

To your question though: according to our docs you are doing everything correctly https://docs.page/invertase/react-native-google-mobile-ads#configure-outbound-requests

...so unfortunately I don't have a quick answer

What platforms do you see the problem on, or is it on both? Do you see anything in native logs (for example adb logcat for android)?

As context, these are the android APIs, just focusing on that platform: https://developers.google.com/admob/android/targeting

You could chase through / instrument the code here in javascript

https://github.com/invertase/react-native-google-mobile-ads/blob/d4ed8ede312ca90886be182516de33525267b939/src/MobileAds.ts#L60-L71

and here in java

https://github.com/invertase/react-native-google-mobile-ads/blob/d4ed8ede312ca90886be182516de33525267b939/android/src/main/java/io/invertase/googlemobileads/ReactNativeGoogleMobileAdsModule.java#L142-L145

I'm not sure what a better path would be - I don't have time to chase this right now, but that's what I'd do - first step, crack open the source in node_modules in an editor, and add a bunch of console.logging to verify assumptions / API call parameters / API call returns, at javascript and native level, then carefully watch metro console and adb logcat to see what's not working

mcama commented 2 years ago

Hey, Mike! Many thanks for the prompt reply!

I will check those tips out! I currently working with the Android platform only.

BTW, something I just discovered is that despite removing the request configuration and the SDK initialization code from App.js, the ads are loading anyways (as if the request configuration and the SDK initialization were not necessary or being ignored when present).

useEffect(() => {
  // Configure outbound requests
  mobileAds()
      .setRequestConfiguration({
          maxAdContentRating: MaxAdContentRating.PG, // Update all future requests suitable for parental guidance
          tagForChildDirectedTreatment: true, // Content treated as child-directed for purposes of COPPA
          tagForUnderAgeOfConsent: true, // Ad request handled in a manner suitable for users under the age of consent
      })
      .then(() => {
          // Initialize Google Mobile Ads SDK
          mobileAds().initialize();
      });
}, []);

Maybe including that code somewhere else or performing some other action inside the then()'s callback of the mobileAds().initialize() could help somehow?

Thanks again.

mikehardy commented 2 years ago

I think doing it in useEffect is perhaps too late for you but can't be sure, it loads after your view hierarchy has already been rendered.

You might want to put critical things like this in your App.js as "critical / run before rendering" type things, for instance, you might have a state variable "initializing" that is false to start and you render your whole view hierarchy differently based on it so that you may ensure bootstrap type functions are done before showing things

Stylewise, sorta like this https://github.com/zoontek/react-native-bootsplash/blob/c070e86908a5a1b24d67874852cdd5ce5ddcc15d/example/App.tsx#L53-L101

or https://github.com/invertase/react-native-firebase-authentication-example/blob/main/template/src/app/App.tsx

...each of those has "critical startup tasks" that block main app render until completed, and might be an idea.

mcama commented 2 years ago

Thanks again, Mike! I happen to have react-native-bootsplash installed on my project but moving the SDK initialization and request configuration code inside the init() function promise resolution did not make any difference.

So, I believe this leads me to one last question... Is there a chance then that the SDK is being auto-initialized when importing the module? I'm asking since as I previously mentionted ads are are being loaded and shown despite removing all the request configuration and the SDK initialization code from the app.

Thanks a lot for your time!

mikehardy commented 2 years ago

Yeah, I did catch that mention but did not have a good answer, sorry.

You may be on to something though, our example app is actually coded contrary to our docs, where we say basically "you must initialize", since I cannot find "initialize" anywhere in index.js or App.tsx https://github.com/invertase/react-native-google-mobile-ads/blob/main/RNGoogleMobileAdsExample/App.tsx

What's going on?

I have to admit, it's unexpected to not see the initialize call anywhere, and yet I know I see ads in the example module, so I reproduce what you see but do not have a good explanation.

My only thought is perhaps it is related to this setting in app.json, but honestly I doubt it

https://github.com/invertase/react-native-google-mobile-ads/blob/d4ed8ede312ca90886be182516de33525267b939/android/src/main/AndroidManifest.xml#L15 https://github.com/invertase/react-native-google-mobile-ads/blob/d4ed8ede312ca90886be182516de33525267b939/RNGoogleMobileAdsExample/app.json#L7

Even that should not make it so you cannot set the configuration though, as the android docs clearly say "update":

https://developers.google.com/admob/android/targeting#requestconfiguration

To update the request configuration

Implying you could update it again, without initializing again - that setting config and initializing are separate.

Their example indicates it is supposed to be as simple as it seems: https://github.com/googleads/googleads-mobile-android-examples/blob/master/java/advanced/APIDemo/app/src/main/java/com/google/android/gms/example/apidemo/AdMobAdTargetingFragment.java

github-actions[bot] commented 1 year ago

Hello 👋, to help manage issues we automatically close stale issues.

This issue has been automatically marked as stale because it has not had activity for quite some time.Has this issue been fixed, or does it still require attention?

This issue will be closed in 15 days if no further activity occurs.

Thank you for your contributions.