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
651 stars 134 forks source link

[🐛] Bug Report Title - RewardedAd.show() The requested RewardedAd has not loaded and could not be shown - in IOS #603

Open Seventro opened 1 month ago

Seventro commented 1 month ago

What happened?

So I am making a basic normal 2D game in react native and I in my game if user wants more coins then he can watch Rewarded Ad, So I implemented Rewarded Ads in my Page. But the problem is The ad is not loading, it fails everytime, I tried every solution but still facing same problem.

Error : RewardedAd.show() The requested RewardedAd has not loaded and could not be shown

Here is my Wallet.Js code (I Removed the CSS from this code, so you can focus on main code) :

import React, { useEffect, useState } from "react"; import { View, Text, Image, StyleSheet, TouchableOpacity, Modal, Animated, ImageBackground, } from "react-native"; import { RewardedAd, RewardedAdEventType, TestIds } from 'react-native-google-mobile-ads';

const adUnitId = DEV ? TestIds.REWARDED : 'ca-app-pub-3764917219536492/9321659815';

export function adMobRewardedAds() { const rewardedAd = RewardedAd.createForAdRequest(adUnitId, { requestNonPersonalizedAdsOnly: true, keywords: ['xx'], });

const [isLoaded, setIsLoaded] = useState(false);

useEffect(() => { const unsubscribeLoaded = rewardedAd.addAdEventListener( RewardedAdEventType.LOADED, () => { setIsLoaded(true); } );

const unsubscribeEarned = rewardedAd.addAdEventListener(
  RewardedAdEventType.EARNED_REWARD,
  reward => {
    console.log('User earned reward of ', reward);
  }
);

rewardedAd.load();

return () => {
  unsubscribeLoaded();
  unsubscribeEarned();
};
}, []);

const openAd = () => { try { if (isLoaded) { rewardedAd.show(); console.log("Reward showed"); } else { throw new Error("Ad not loaded yet"); } } catch (error) { rewardedAd.load(); console.log("Ad loaded"); } };

return { isLoaded, openAd, }; }

const Wallet = ({ balance, setBalance, navigation }) => { const { isLoaded, openAd } = adMobRewardedAds(); const [showAdModal, setShowAdModal] = useState(false); const [showRewardModal, setShowRewardModal] = useState(false); const [visible, setVisible] = useState(false); const [message, setMessage] = useState(""); const [animation, setAnimation] = useState(new Animated.Value(0));

const showMessage = (message) => { setMessage(message); setVisible(true); Animated.spring(animation, { toValue: 1, friction: 4, tension: 40, useNativeDriver: true, }).start(() => { setTimeout(() => { Animated.spring(animation, { toValue: 0, friction: 4, tension: 40, useNativeDriver: true, }).start(() => { setVisible(false); }); }, 2000); }); };

const handleGiftBox = (cost, numCoupons) => { setShowRewardModal(false); if (balance >= cost) { setBalance(balance - cost); navigation.navigate("Rewardscreen", { numCoupons }); } else { showMessage("You're broke! Get a job!"); } };

return ( <View style={styles.container}> <Image source={require("../assets/arrow.png")} style={styles.Bicon} /> <View style={styles.Coincontainer}> <Text style={styles.balanceText}> <Image source={require("../assets/coin.png")} style={styles.icon} /> {parseFloat(balance).toFixed(2)} </Text> {visible && ( <Animated.View style={[ styles.toastContainer, { transform: [{ scale: animation }], opacity: animation, }, ]}> <Text style={styles.toastText}>{message}</Text> </Animated.View> )} <TouchableOpacity style={styles.Walletcontainer} onPress={() => setShowAdModal(true)}> <Image source={require("../assets/wallet.png")} style={styles.Wicon} /> </TouchableOpacity> </View> <View> <Text>{" "}</Text> </View>

  <Modal
    animationType="slide"
    transparent={true}
    visible={showAdModal}
    onRequestClose={() => {
      setShowAdModal(false);
    }}>
    <View style={styles.modalContainer}>
      <View style={styles.modalContent}>
        <Image
          source={{ uri: "https://via.placeholder.com/300x250" }}
          style={styles.adImage}
        />
        <View style={styles.adInfoContainer}>
          <Text style={styles.adTitle}>Watch Ad to Earn Coins!</Text>
          <Text style={styles.adDescription}>
            Watch this ad to earn 50 coins! You will be rewarded with 50
            coins after watching the ad.
          </Text>
        </View>
        <TouchableOpacity
          style={styles.modalButton}
          onPress={() => {
            openAd();
            setShowAdModal(false);
          }}>
          <Text style={styles.modalButtonText}>Watch Ad</Text>
        </TouchableOpacity>
        <TouchableOpacity
          style={styles.modalRewardButton}
          onPress={() => {
            setShowAdModal(false);
            setShowRewardModal(true);
          }}>
          <Text style={styles.modalButtonText}>Reward</Text>
        </TouchableOpacity>
      </View>
    </View>
  </Modal>

  <Modal
    animationType="slide"
    transparent={true}
    visible={showRewardModal}
    onRequestClose={() => {
      setShowRewardModal(false);
    }}>
    <View style={styles.rewardModalContainer}>
      <ImageBackground
        source={require("../assets/Rbg.png")}
        style={styles.rewardBackgroundImage}>
        <TouchableOpacity
          style={styles.Giftbox}
          onPress={() => handleGiftBox(2000, 1)}>
          <Image
            source={require("../assets/Gift.png")}
            style={styles.GiftboxImage}
          />
          <View style={styles.GiftboxCard}>
            <Text
              style={[
                styles.Giftboxprice,
                balance < 2000 ? { color: "red" } : null,
              ]}>
              2000{" "}
              <Image
                source={require("../assets/coin.png")}
                style={styles.Cicon}
              />
            </Text>
          </View>
        </TouchableOpacity>
        <TouchableOpacity
          style={styles.Giftbox}
          onPress={() => handleGiftBox(5000, 3)}>
          <Image
            source={require("../assets/megaGift.png")}
            style={styles.GiftboxImage}
          />
          <View style={styles.GiftboxCard}>
            <Text
              style={[
                styles.Giftboxprice,
                balance < 5000 ? { color: "red" } : null,
              ]}>
              5000{" "}
              <Image
                source={require("../assets/coin.png")}
                style={styles.Cicon}
              />
            </Text>
          </View>
        </TouchableOpacity>
      </ImageBackground>
    </View>
  </Modal>
</View>
); };

Platforms

iOS, but have not tested behavior on Android

React Native Info

System:
  OS: macOS 13.6.4
  CPU: (8) arm64 Apple M1
  Memory: 58.84 MB / 8.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 20.15.0
    path: /usr/local/bin/node
  Yarn:
    version: 1.22.22
    path: /opt/homebrew/bin/yarn
  npm:
    version: 10.7.0
    path: /usr/local/bin/npm
  Watchman:
    version: 2024.06.24.00
    path: /opt/homebrew/bin/watchman
Managers:
  CocoaPods:
    version: 1.15.2
    path: /opt/homebrew/bin/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 23.2
      - iOS 17.2
      - macOS 14.2
      - tvOS 17.2
      - visionOS 1.0
      - watchOS 10.2
  Android SDK: Not Found
IDEs:
  Android Studio: 2022.1 AI-221.6008.13.2211.9619390
  Xcode:
    version: 15.2/15C500b
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 22.0.1
    path: /usr/bin/javac
  Ruby:
    version: 3.3.3
    path: /opt/homebrew/opt/ruby/bin/ruby
npmPackages:
  "@react-native-community/cli": Not Found
  react:
    installed: 18.2.0
    wanted: 18.2.0
  react-native:
    installed: 0.74.2
    wanted: 0.74.2
  react-native-macos: Not Found
npmGlobalPackages:
  "react-native": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: false
iOS:
  hermesEnabled: true
  newArchEnabled: false

Are your using Typescript?

package.json

{
  "name": "minebuddy",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "android": "react-native run-android",
    "ios": "react-native run-ios",
    "lint": "eslint .",
    "start": "react-native start",
    "test": "jest"
  },
  "dependencies": {
    "@react-native-async-storage/async-storage": "^1.23.1",
    "@react-navigation/native": "^6.1.17",
    "@react-navigation/native-stack": "^6.10.0",
    "react": "18.2.0",
    "react-native": "0.74.2",
    "react-native-admob": "^1.3.2",
    "react-native-eject": "^0.2.0",
    "react-native-google-mobile-ads": "^13.6.1",
    "react-native-safe-area-context": "^4.10.7",
    "react-native-screens": "^3.32.0",
    "react-native-sound": "^0.11.2",
    "react-navigation": "^4.4.4",
    "react-navigation-stack": "^2.10.4"
  },
  "devDependencies": {
    "@babel/core": "^7.20.0",
    "@babel/preset-env": "^7.20.0",
    "@babel/runtime": "^7.20.0",
    "@react-native/babel-preset": "0.74.84",
    "@react-native/eslint-config": "0.74.84",
    "@react-native/metro-config": "0.74.84",
    "@react-native/typescript-config": "0.74.84",
    "@types/react": "^18.2.6",
    "@types/react-test-renderer": "^18.0.0",
    "babel-jest": "^29.6.3",
    "eslint": "^8.19.0",
    "jest": "^29.6.3",
    "prettier": "2.8.8",
    "react-test-renderer": "18.2.0",
    "typescript": "5.0.4"
  },
  "engines": {
    "node": ">=18"
  }
}

app.json

{
  "name": "minebuddy",
  "displayName": "minebuddy",
  "slug": "minebuddy",
  "version": "1.0.0",
  "orientation": "portrait",
  "icon": "./assets/images/icon.png",
  "scheme": "myapp",
  "userInterfaceStyle": "automatic",
  "splash": {
    "image": "./assets/images/splash.png",
    "resizeMode": "contain",
    "backgroundColor": "#ffffff"
  },
  "ios": {
    "supportsTablet": true
  },
  "android": {
    "adaptiveIcon": {
      "foregroundImage": "./assets/images/adaptive-icon.png",
      "backgroundColor": "#ffffff"
    }
  },
  "web": {
    "bundler": "metro",
    "output": "static",
    "favicon": "./assets/images/favicon.png"
  },
  "plugins": [
    "expo-router"
  ],
  "experiments": {
    "typedRoutes": true
  },
  "react-native-google-mobile-ads": {
    "android_app_id": "ca-app-pub-3940256099942544~3347511713",
    "ios_app_id": "ca-app-pub-3764917219536492~3984104045"
  }
}

ios/Podfile

# Resolve react_native_pods.rb with node to allow for hoisting
require Pod::Executable.execute_command('node', ['-p',
  'require.resolve(
    "react-native/scripts/react_native_pods.rb",
    {paths: [process.argv[1]]},
  )', _dir_]).strip

platform :ios, min_ios_version_supported
prepare_react_native_project!

linkage = ENV['USE_FRAMEWORKS']
if linkage != nil
  Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
  use_frameworks! :linkage => linkage.to_sym
end

$RNGoogleMobileAdsAsStaticFramework = true

target 'minebuddy' do
  config = use_native_modules!

  use_react_native!(
    :path => config[:reactNativePath],
    # An absolute path to your application root.
    :app_path => "#{Pod::Config.instance.installation_root}/.."
  )

  pod 'RNGoogleMobileAds', :path => '../node_modules/react-native-google-mobile-ads'

  target 'minebuddyTests' do
    inherit! :complete
    # Pods for testing
  end

  post_install do |installer|
    # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
    react_native_post_install(
      installer,
      config[:reactNativePath],
      :mac_catalyst_enabled => false,
      # :ccache_enabled => true
    )
  end
end

android/build.gradle

No response

android/app/build.gradle

No response

android/settings.gradle

No response

AndroidManifest.xml

No response

chen8520 commented 1 month ago

The same issue, successfully loaded in the simulator, but unable to load successfully in the real machine

fabioscarneiro commented 1 month ago

I had the same issue on both Android and iOS devices. This error occurs due to the requestId in MobileAd.ts of the library. In the following code, the requestId is passed to the load method:

 public load() {
    // Prevent multiple load calls
    if (this._loaded || this._isLoadCalled) {
      return;
    }

    this._isLoadCalled = true;
    const load: AdLoadFunction = NativeModules[this._nativeModule][`${this._camelCaseType}Load`];
    load(this._requestId, this._adUnitId, this._requestOptions);
  }

In my case, the problem was happening because when calling load, a requestId was being passed, and when calling show, another requestId was being passed. This indicated that different instances were being called. The solution I implemented and that worked was to add RewardedAd.createForAdRequest inside the useEffect, as shown below:

 const [loaded, setLoaded] = useState(false);
    let rewarded;

    useEffect(() => {
        rewarded = RewardedAd.createForAdRequest(constants.adUnitIdRewarded, {
            requestNonPersonalizedAdsOnly: true
        });

        const loadAd = () => {
            console.log('Loading rewarded ad');
            rewarded.load();
        };

        const unsubscribeLoaded = rewarded.addAdEventListener(RewardedAdEventType.LOADED, () => {
            console.log('Rewarded ad loaded');
            setLoaded(true);
        });

        const unsubscribeEarned = rewarded.addAdEventListener(RewardedAdEventType.EARNED_REWARD, reward => {
            console.log('Reward earned:', reward);
        });

        const unsubscribeClosed = rewarded.addAdEventListener(AdEventType.CLOSED, () => {
            console.log('Rewarded ad closed');
            setLoaded(false);
            loadAd();
        });

        const unsubscribeFailed = rewarded.addAdEventListener(AdEventType.ERROR, error => {
            console.error('Failed to load rewarded ad:', error);
            setLoaded(false);
        });

        loadAd();

        return () => {
            unsubscribeLoaded();
            unsubscribeEarned();
            unsubscribeClosed();
            unsubscribeFailed();
        };
    }, [loaded]);

    const showRewardedAd = () => {
            rewarded.show();
    };

See if this solution works for you as well.

github-actions[bot] commented 6 days 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.