Rewarded Ads logic and debugging
This writing is about how I make some logic to show the correct hint in Sudoku after watching Google AdMob rewarded ads. The project is based on expo bare project with package, react-native-google-mobile-ads.
This project was based on below environment.
- Expo bare project (npx expo prebuild) with template TypeScript.
- expo: 54.0.23
- react-native: 0.81.5
- react-native-google-mobile-ads: 16.0.0
What I was trying to achieve is that in the hint modal, if I touch the WATCH AD button, it shows rewarded ads from Google AdMob. After watching it, hint modal should be closed automatically and 1 hidden sudoku number should appear in random as a reward.
The first challenge that I encounter was that the reward action was not carried out at the first ads watch. From the second ads watch reward action was carried out well, but not the first one. After several trial and error I found that it was because when reward action function was called it referred old status of the function so first reward was not given but from the second one. So I solve it by keeping the reward action function in latest status with useRef.
const onRewardEarned = () => { // some reward action logic (show 1 hidden cell) // change the color of appeared cell // return to original color after 10secs } const onRewardEarnedRef = useRef<() => void>(() => {}); useEffect(() => { onRewardEarnedRef.current = onRewardEarned; }, [onRewardEarned]); // onRewardEarned is a stable function here; if it's re-created often, it's ok
The second challenge I met was about rewarded ads instance. Unlike the interstitial ads instance, rewarded ads instance can be shown only once and cannot be used again. After use it, it must be recreated. I solved it by making simple toggle and put it in the dependency where rewarded ads is made.
useEffect(() => { rewardedRef.current = RewardedAd.createForAdRequest(rewardedUnitId, { requestNonPersonalizedAdsOnly: true, }); const rewarded = rewardedRef.current; const unsubscribeLoaded = rewarded.addAdEventListener( RewardedAdEventType.LOADED, () => setRewardedLoaded(true) ); const unsubscribeEarned = rewarded.addAdEventListener( RewardedAdEventType.EARNED_REWARD, reward => { try { onRewardEarnedRef.current(); } catch (e) { console.warn('onRewardEarnedRef error', e); } setRewardedLoaded(false); setTimeout(() => { setHintModalVisible(false); }, 500); setTimeout(() => { setToggle(prev => !prev); }, 5000); } ); rewarded.load(); return () => { unsubscribeLoaded(); unsubscribeEarned(); }; }, [toggle]);
Thanks to simple toggle, rewarded instance could be recreated and rewarded ads, action could be carried out well.
The reason I put a setTimeout with HintModal is that if hint modal close too soon, the state can be mixed before the rewarded action is executed, so set the timer around 0.5 sec.
setTimeout for toggle for 5 secs has same reason. After EARNED_REWARD arrives, if toggle works too fast before reward action is executed, event listener would be removed and recreated which result no actual reward. So I set the timer around for 5 secs.
At first I just put the toggle after rewarded.show() was called, but it was totally bad idea. Rewarded ads starts to show and toggle works immediately, so during the rewarded ads is being shown for around 5secs, event listener is removed and new instance is created. As a result no actual reward is given. So the toggle should be located after reward action ( onRewardEarnedRef.current() ) is executed.
const showRewardAd = () => { const rewarded = rewardedRef.current; if (rewarded && rewardedLoaded) { rewarded.show(); setRewardedLoaded(false); // setToggle(!toggle) was here but it should not be here!! // It should be located after reward action! } else { Alert.alert('Ad is not ready yet.'); } };
Comments
Post a Comment