React Native App Stopped Logging Conversions to Google Ads

I have a React Native Expo app for iOS that uses Firebase and Firebase Analytics to log user installs. The app correctly logs events to Firebase and Firebase Analytics, where I can see events such as first_open and session_start. This Firebase project is connected to my Google Ads account, where I run app install ads. I can see new users in Google Analytics, but these installs are not reflected in Google Ads.

The last logged event was on July 12th. Since then, no app download/install events have been recorded in Google Ads.

Here’s what I’ve done so far:

Ensured Firebase is correctly linked to Google Ads in the project settings/integrations.
Confirmed that events are properly logging in Google Analytics and the Firebase console.
Set events as key events in Firebase.
Ensured conversions are active in Google Ads.
I also tried adding the AdSupport framework to my app using a script from this GitHub post, but I’m not sure if it succeeded. After this update, there seems to be an issue with first_open reporting to Firebase.

I am certain that the events displayed in analytics originate from Google Ads and not from the App Store. This is because the app is new and has very low visibility in the App Store. Additionally, when I disable the ads, no new users are acquired.

Did anyone had experience with problem like this and if yes did you solve it and how?

My app configuration:

app.json:

{
  "expo": {
    "name": "******",
    "slug": "******",
    "version": "1.3.12",
    "orientation": "portrait",
    "icon": "./assets/images/icon.png",
    "scheme": "******",
    "userInterfaceStyle": "automatic",
    "facebookAppId": "******",
    "facebookDisplayName": "******",
    "facebookScheme": "fb******",
    "splash": {
      "image": "./assets/images/splash.png",
      "resizeMode": "contain",
      "backgroundColor": "#ffffff"
    },
    "ios": {
      "supportsTablet": true,
      "bundleIdentifier": "******",
      "appStoreUrl": "https://apps.apple.com/app/id******",
      "googleServicesFile": "./GoogleService-Info.plist",
      "infoPlist": {
        "NSAppTransportSecurity": {
          "NSAllowsArbitraryLoads": true
        },
        "UIBackgroundModes": ["fetch", "remote-notification"],
        "FirebaseAppDelegateProxyEnabled": true,
        "AppsFlyerAppID": "******",
        "FacebookClientToken": "******",
        "NSUserTrackingUsageDescription": "This identifier will help us understand how you found us. No other data will be collected."
      },
      "entitlements": {
        "aps-environment": "production"
      },
      "buildNumber": "1"
    },
    "android": {
      "versionCode": 1,
      "googleServicesFile": "./google-services.json",
      "permissions": ["INTERNET", "com.android.vending.BILLING", "ACCESS_NETWORK_STATE", "ACCESS_WIFI_STATE", "WAKE_LOCK", "RECEIVE_BOOT_COMPLETED"],
      "adaptiveIcon": {
        "foregroundImage": "./assets/images/adaptive-icon.png",
        "backgroundColor": "#ffffff"
      },
      "package": "******"
    },
    "web": {
      "bundler": "metro",
      "output": "static",
      "favicon": "./assets/images/favicon.png"
    },
    "plugins": [
      "expo-router",
      "@react-native-firebase/app",
      "@react-native-firebase/messaging",
      "@react-native-firebase/crashlytics",
      "./plugins/withPodfile",
      [
        "expo-build-properties",
        {
          "ios": {
            "useFrameworks": "static",
            "extraPodspecs": ["pod 'AdSupport', '>= 1.0'"]
          },
          "android": {
            "permissions": ["com.android.vending.BILLING"]
          }
        }
      ],
      "expo-font",
      [
        "react-native-fbsdk-next",
        {
          "appID": "******",
          "clientToken": "******",
          "displayName": "******",
          "scheme": "fb******",
          "advertiserIDCollectionEnabled": true,
          "autoLogAppEventsEnabled": true,
          "isAutoInitEnabled": true,
          "iosUserTrackingPermission": "This identifier will help us understand how you found us. No other data will be collected."
        }
      ]
    ],
    "experiments": {
      "typedRoutes": true
    },
    "extra": {
      "router": {
        "origin": false
      },
      "eas": {
        "projectId": "******",
        "env": "production"
      }
    }
  }
}

eas.json:

{
  "cli": {
    "version": ">= 9.0.5",
    "appVersionSource": "remote"
  },
  "build": {
    "development": {
      "developmentClient": true,
      "distribution": "internal"
    },
    "preview": {
      "distribution": "internal",
      "channel": "preview"
    },
    "production": {
      "channel": "production",
      "autoIncrement": true,
      "android": {
        "buildType": "app-bundle"
      }
    }
  },
  "submit": {
    "production": {}
  }
}

GoogleService-Info.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>API_KEY</key>
  <string>******</string>
  <key>GCM_SENDER_ID</key>
  <string>******</string>
  <key>PLIST_VERSION</key>
  <string>1</string>
  <key>BUNDLE_ID</key>
  <string>******</string>
  <key>PROJECT_ID</key>
  <string>******</string>
  <key>STORAGE_BUCKET</key>
  <string>******</string>
  <key>IS_ADS_ENABLED</key>
  <true/>
  <key>IS_ANALYTICS_ENABLED</key>
  <true/>
  <key>IS_APPINVITE_ENABLED</key>
  <true/>
  <key>IS_GCM_ENABLED</key>
  <true/>
  <key>IS_SIGNIN_ENABLED</key>
  <true/>
  <key>GOOGLE_APP_ID</key>
  <string>******</string>
</dict>
</plist>

google-services.json:

{
  "project_info": {
    "project_number": "******",
    "project_id": "******",
    "storage_bucket": "******"
  },
  "client": [
    {
      "client_info": {
        "mobilesdk_app_id": "******",
        "android_client_info": {
          "package_name": "******"
        }
      },
      "oauth_client": [],
      "api_key": [
        {
          "current_key": "******"
        }
      ],
      "services": {
        "appinvite_service": {
          "other_platform_oauth_client": []
        }
      }
    }
  ],
  "configuration_version": "1"
}

package.json:

{
  "name": "******",
  "main": "expo-router/entry",
  "version": "1.0.0",
  "scripts": {
    "start": "expo start",
    "android": "expo run:android",
    "ios": "expo run:ios",
    "web": "expo start --web",
    "test": "jest --watchAll",
    "copy": "node ./scripts/copy-config.js",
    "clean": "node ./scripts/clean-config.js",
    "setversion": "node ./scripts/setversion.js",
  },
  "jest": {
    "preset": "jest-expo"
  },
  "dependencies": {
    "@expo/config-plugins": "~8.0.5",
    "@expo/dev-server": "0.5.5",
    "@expo/metro-config": "^0.18.7",
    "@expo/vector-icons": "^14.0.2",
    "@react-native-async-storage/async-storage": "^1.23.1",
    "@react-native-firebase/analytics": "^20.1.0",
    "@react-native-firebase/app": "^20.1.0",
    "@react-native-firebase/crashlytics": "^20.1.0",
    "@react-native-firebase/messaging": "^20.1.0",
    "@react-navigation/native": "^6.1.17",
    "@sentry/react-native": "^5.24.1",
    "axios": "^1.7.2",
    "babel-plugin-module-resolver": "^5.0.2",
    "date-fns": "^3.6.0",
    "dotenv": "^16.4.5",
    "eas": "^0.1.0",
    "expo": "51.0.14",
    "expo-build-properties": "~0.12.3",
    "expo-constants": "~16.0.2",
    "expo-dev-client": "~4.0.18",
    "expo-device": "~6.0.2",
    "expo-font": "~12.0.7",
    "expo-linear-gradient": "~13.0.2",
    "expo-linking": "~6.3.1",
    "expo-notifications": "~0.28.9",
    "expo-router": "~3.5.16",
    "expo-splash-screen": "~0.27.5",
    "expo-status-bar": "~1.12.1",
    "expo-store-review": "~7.0.2",
    "expo-system-ui": "~3.0.6",
    "expo-tracking-transparency": "~4.0.2",
    "expo-updates": "~0.25.17",
    "expo-web-browser": "~13.0.3",
    "install": "^0.13.0",
    "jest-expo": "~51.0.2",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-native": "0.74.2",
    "react-native-branch": "^6.2.2",
    "react-native-fbsdk-next": "^13.0.0",
    "react-native-get-random-values": "^1.11.0",
    "react-native-glassfy-module": "^1.6.2",
    "react-native-global-props": "^1.1.5",
    "react-native-reanimated-carousel": "^3.5.1",
    "react-native-safe-area-context": "4.10.5",
    "react-native-screens": "3.32.0",
    "react-native-svg": "^15.3.0",
    "react-native-swiper": "^1.6.0",
    "react-native-ux-cam": "^5.4.16",
    "react-native-web": "~0.19.12",
    "styled-components": "^6.1.11",
    "uuid": "^10.0.0",
    "with-google-idfa-support": "^0.1.0"
  },
  "devDependencies": {
    "@babel/core": "^7.24.7",
    "@types/react": "~18.3.3",
    "@types/react-native-global-props": "^1.1.6",
    "@types/react-native-vector-icons": "^6.4.18",
    "@types/uuid": "^10.0.0",
    "jest": "^29.7.0",
    "jest-expo": "~51.0.2",
    "metro-react-native-babel-preset": "^0.77.0",
    "react-test-renderer": "18.2.0",
    "typescript": "~5.5.2"
  },
  "private": true
}

layout.tsx:

import 'react-native-get-random-values';
import { v4 as uuidv4 } from 'uuid';
import React, { useEffect, useState } from 'react';
import { StatusBar } from 'react-native';
import * as SplashScreen from 'expo-splash-screen';
import { ThemeProvider, DefaultTheme } from '@react-navigation/native';
import { useFonts } from 'expo-font';
import { Stack } from 'expo-router';
import FontAwesome from '@expo/vector-icons/FontAwesome';
import { AppProvider, useAppContext } from '@contexts/AppContext';
import { setCustomText } from 'react-native-global-props';
import { GlassfyProvider, useGlassfy } from './contexts/GlassfyContext';
import Constants from 'expo-constants';
import Strings from '@root/constants/strings';
import { getFcmToken } from './utils/permissionUtils';
import * as Sentry from '@sentry/react-native';
import { DetailedStatsProvider } from './contexts/DetailedStatsContext';
import { SnapshotProvider } from './contexts/SnapShotContext';
import { createUser, updateNotificationLogClicked, createAttribution } from './api/Questions';
import { getData, storeData } from './utils/asyncStorage';
import { getUUID } from './utils/uuId';
import * as Linking from 'expo-linking';
import * as Notifications from 'expo-notifications';
import RNUxcam from 'react-native-ux-cam';

let messaging: typeof import('@react-native-firebase/messaging').default | undefined;
let firebase: typeof import('@react-native-firebase/app').default | undefined;
let AppEventsLogger: typeof import('react-native-fbsdk-next').AppEventsLogger | undefined;
let Settings: typeof import('react-native-fbsdk-next').Settings | undefined;
let requestTrackingPermissionsAsync: typeof import('expo-tracking-transparency').requestTrackingPermissionsAsync | undefined;
let analytics: typeof import('@react-native-firebase/analytics').default | undefined;

const isProduction = Constants.expoConfig?.extra?.eas?.env === 'production';

if (isProduction) {
  messaging = require('@react-native-firebase/messaging').default;
  firebase = require('@react-native-firebase/app').default;
  AppEventsLogger = require('react-native-fbsdk-next').AppEventsLogger;
  Settings = require('react-native-fbsdk-next').Settings;
  requestTrackingPermissionsAsync = require('expo-tracking-transparency').requestTrackingPermissionsAsync;
  analytics = require('@react-native-firebase/analytics').default;

  Sentry.init({
    dsn: Strings.SENTRY_DSN,
    enableSpotlight: __DEV__,
  });

  const originalConsoleError = console.error;
  console.error = (...args) => {
    Sentry.captureException(new Error(args.join(" ")));
    originalConsoleError(...args);
  };
}

SplashScreen.preventAutoHideAsync();

const linking = {
  prefixes: [Strings.LINKING_PREFIX],
  config: {
    screens: {
      PaymentScreen: '/PaymentScreen',
      Home: '/(tabs)',
    },
  },
};

export default function RootComponent() {
  const [fontsLoaded] = useFonts({
    FontThin: require('@assets/fonts/Lato/Lato-Thin.ttf'),
    FontLight: require('@assets/fonts/Lato/Lato-Light.ttf'),
    FontRegular: require('@assets/fonts/Lato/Lato-Regular.ttf'),
    FontSemiBold: require('@assets/fonts/Lato/Lato-Bold.ttf'),
    FontBold: require('@assets/fonts/Lato/Lato-Bold.ttf'),
    ...FontAwesome.font,
  });

  const [isFacebookInitialized, setIsFacebookInitialized] = useState(false);
  const [isEventLogged, setIsEventLogged] = useState(false);
  const [isUxCamInitialized, setIsUxCamInitialized] = useState(false);
  const [isAnalyticsLoaded, setIsAnalyticsLoaded] = useState(false);

  useEffect(() => {
    if (isProduction) {
      try {
        const initizeUxCam = () => {
          if (isUxCamInitialized) return;

          try {
            RNUxcam?.optIntoSchematicRecordings();
            const configuration = {
              userAppKey: Strings.UXCAM_APP_KEY,
              enableAutomaticScreenNameTagging: false,
              enableImprovedScreenCapture: true,
            };
            RNUxcam?.startWithConfiguration(configuration);

            setIsUxCamInitialized(true);
            Sentry.addBreadcrumb({
              category: 'uxcam',
              message: 'UXCam initialized successfully',
              level: 'info',
            });
          } catch (initError) {
            console.error("Error initializing UXCam:", initError);
            Sentry.captureException(initError);
          }
        };

        const registerUxcam = async () => {
          try {
            const device_uuid = await getUUID();

            if (RNUxcam) {
              RNUxcam.setUserIdentity(device_uuid);
              Sentry.addBreadcrumb({
                category: 'uxcam',
                message: 'UXCam user identity set successfully',
                level: 'info',
              });
            } else {
              const error = new Error("RNUxcam is undefined");
              console.error("Error setting RNUxcam Identity, RNUXcam is undefined");
              Sentry.captureException(error);
            }
          } catch (error) {
            console.error("Error setting RNUxcam Identity, device_id: ", error);
            Sentry.captureException(error);
          }
        };

        initizeUxCam();
        console.log("UxCam Init True");
        Sentry.addBreadcrumb({
          category: 'uxcam',
          message: 'Calling registerUxcam function',
          level: 'info',
        });

        registerUxcam();
      } catch (error) {
        console.error("UxCam Init Error", error);
        Sentry.captureException(error);
      }
    }
  }, [isUxCamInitialized, isProduction, RNUxcam]);

  useEffect(() => {
    if (isProduction) {
      try {
        const initializeAnalytics = async () => {
          if (isAnalyticsLoaded) return;

          await analytics?.().setAnalyticsCollectionEnabled(true);
          setIsAnalyticsLoaded(true);
          console.log("Firebase Analytics Initialized");
        };

        initializeAnalytics();
      } catch (error) {
        console.error("Firebase Analytics Init Error", error);
      }
    }
  }, [isAnalyticsLoaded, isProduction]);

  useEffect(() => {
    async function initialize() {
      try {
      } catch (error) {
        console.error('Initialization error:', error);
      } finally {
        if (fontsLoaded) {
          await SplashScreen.hideAsync();
          console.log('Splash screen hidden');
        }
      }
    }

    if (fontsLoaded) {
      initialize();
    }
  }, [fontsLoaded, isProduction]);

  useEffect(() => {
    if (isProduction) {
      async function initializeFacebookSDK() {
        if (isFacebookInitialized) return;

        try {
          Settings?.initializeSDK();
          setIsFacebookInitialized(true);
        } catch (error) {
          console.error('Facebook SDK initialization error:', error);
        }

        if (!isEventLogged) {
          try {
            AppEventsLogger?.logEvent('fb_mobile_activate_app');
            setIsEventLogged(true);
          } catch (error) {
            console.error('Error logging Facebook event:', error);
          }
        }
      }

      async function registerUser() {
        try {
          console.log("Starting registerUser function...");

          const device_uuid = await getUUID();
          console.log("Device UUID retrieved:", device_uuid);

          const onBoardingFinished = await getData('onboardingFinished');
          console.log("Onboarding status retrieved:", onBoardingFinished);

          if (device_uuid) {
            const otherTracking = "";

            console.log("Calling createUser API with device_uuid:", device_uuid, "and otherTracking:", otherTracking);
            const user = await createUser(device_uuid, otherTracking);
            console.log('User created:', user);

            try {
              Sentry.setUser({
                id: device_uuid,
              });
            } catch (error) {
              console.error("Error setting Sentry User device_id: ", device_uuid, "Error: ", error);
            }

            try {
              if (analytics) {
                await analytics().setUserId(device_uuid);
              } else {
                console.error("Error setting Firebase Analytics user, Firebase Analytics is undefined");
              }
            } catch (error) {
              console.error("Error setting Firebase Analytics User, device_id:", device_uuid, "Error:", error);
            }
          }
          try {
            if (onBoardingFinished === 'true') {
              await getFcmToken();
            }
          } catch (err) {
            console.error('Error getting FCM token:', err);
          }
        } catch (error) {
          console.error('Error registering user:', error);
        }
      }

      registerUser();
      initializeFacebookSDK();
    }
  }, [isFacebookInitialized, isEventLogged, isProduction,]);

  if (!fontsLoaded) {
    return null;
  }

  const customTextProps = {
    style: {
      fontFamily: 'FontLight',
      fontSize: 14,
      lineHeight: 14,
    },
  };

  setCustomText(customTextProps);

  return (
    <AppProvider>
      <GlassfyProvider>
        <DetailedStatsProvider>
          <SnapshotProvider>
            <App />
          </SnapshotProvider>
        </DetailedStatsProvider>
      </GlassfyProvider>
    </AppProvider>
  );
}

const App = () => {
  const { subscription, setSubscription, setIsNotificationLink } = useAppContext();
  const { checkActiveSubscription } = useGlassfy();

  useEffect(() => {
    const checkSubscriptionStatus = async () => {
      try {
        const isActive = await checkActiveSubscription();
        setSubscription(isActive);
        console.log('Subscription status checked:', isActive);
      } catch (error) {
        console.error('Error checking subscription status:', error);
      }
    };

    checkSubscriptionStatus();
  }, [subscription, checkActiveSubscription]);

  useEffect(() => {
    Notifications.addNotificationResponseReceivedListener(response => {
      console.log('Notification received:', JSON.stringify(response)); // Log the entire notification object

      const trigger = response.notification.request.trigger as Notifications.PushNotificationTrigger;
      const url: string | undefined = trigger?.payload?.link as string | undefined;
      const notificationLogId: string | undefined = trigger?.payload?.notificationLogId as string | undefined;
      const adminSubscription: string | undefined = trigger?.payload?.adminSubscription as string | undefined;

      if (url) {
        setIsNotificationLink(true);
        Linking.openURL(url).catch(err => {
          console.error('Failed to open URL:', err);
        });
      } else {
        console.error('No URL found in notification data');
      }

      if (notificationLogId) {
        updateNotificationLogClicked(parseInt(notificationLogId), true)
          .then(() => {
            console.log(`Notification log ${notificationLogId} marked as clicked.`);
          })
          .catch(error => {
            console.error(`Error updating notification log ${notificationLogId}:`, error);
          });
      } else {
        console.log('No notification log ID found in notification data');
      }

      if (adminSubscription) {
        storeData("adminSubscription", adminSubscription);
      } else {
        console.log('No adminSubscription has been found in notification data');
      }
    });

    const getInitialURL = async () => {
      try {
        const initialURL = await Linking.getInitialURL();
        if (initialURL) {
          setIsNotificationLink(true);
          Linking.openURL(initialURL).catch(err => {
            console.error('Failed to open initial URL:', err);
          });
        }
      } catch (error) {
        console.error('Failed to get initial URL:', error);
      }
    };

    getInitialURL();
  }, [setIsNotificationLink]);

  return (
    <>
      <StatusBar
        barStyle="dark-content"
        translucent={true}
        backgroundColor="transparent"
      />
      <RootLayoutNav />
    </>
  );
};

function RootLayoutNav() {
  return (
    <ThemeProvider value={DefaultTheme}>
      <Stack screenOptions={{ headerShown: false }}>
        {/* Your Stack screens go here */}
      </Stack>
    </ThemeProvider>
  );
}

Firebase/Google Ads Setup screenshots:

https://ibb.co/st30BLH
https://ibb.co/g7wVPTW
https://ibb.co/frsR1Pv
https://ibb.co/D86bqRX
https://ibb.co/0qmtgBx
https://ibb.co/27RsCsJ
https://ibb.co/nRNrvd4
https://ibb.co/7bD3s0x
https://ibb.co/L9YP05B
https://ibb.co/NZWgYPW

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật