React Native: Complete React Native plugin documentation
# API reference
> Complete TypeScript API of @qartvelo/react-native-ads 0.3.0.
```ts
import {
QartveloAds, // also the default export
QartveloAdsBanner,
QartveloAdsError,
isQartveloAdsError,
} from '@qartvelo/react-native-ads';
```
## QartveloAds [Section titled “QartveloAds”](#qartveloads) | Method | Returns | Description | | --------------------------------------------- | ----------------------------- | ---------------------------------------------------------------- | | `initialize(options: QartveloAdsInitOptions)` | `Promise` | Starts the SDK once per process | | `isInitialized()` | `Promise` | First initialization attempt finished | | `loadInterstitial(placementId)` | `Promise` | Resolves when an ad is ready, rejects when none | | `showInterstitial(placementId)` | `Promise` | Resolves on dismiss, or `{ shown: false }` when nothing is ready | | `isInterstitialReady(placementId)` | `Promise` | | | `loadRewarded(placementId)` | `Promise` | | | `showRewarded(placementId)` | `Promise` | `rewarded` is true only after confirmed completion | | `isRewardedReady(placementId)` | `Promise` | | | `addListener(type, listener)` | `QartveloAdsSubscription` | Observe events of all placements | | `removeAllListeners(type?)` | `void` | | | `setLogLevel(level: LogLevel)` | `void` | No-op where unsupported | | `setPrivacy(privacy: QartveloAdsPrivacy)` | `void` | No-op where unsupported | | `isSupported()` | `boolean` | Android with the native module built in | ## QartveloAdsBanner props [Section titled “QartveloAdsBanner props”](#qartveloadsbanner-props) | Prop | Type | | -------------------------------------- | ---------------------------------------------------------------------------------------- | | `placementId` | `string` (required) | | `style` | `StyleProp`; usually `{ width: '100%' }` | | `onLoaded` | `(e: QartveloAdsEventMap['loaded']) => void` | | `onLoadFailed` | `(e: QartveloAdsEventMap['loadFailed']) => void` | | `onShown`, `onImpression`, `onClicked` | `(e) => void` | | `onFallbackStarted` | `(e: QartveloAdsEventMap['fallbackStarted']) => void` | | `onNoAdAvailable` | `(e: QartveloAdsEventMap['noAdAvailable']) => void` | | `onSizeChange` | `(size: { width: number; height: number }) => void` (dp; 0 x 0 when nothing is rendered) | Other `ViewProps` (such as `testID`) are passed through. ## Types [Section titled “Types”](#types)
```ts
type AdFormat = 'banner' | 'interstitial' | 'rewarded';
type AdSource = 'qartvelo' | 'admob';
type LogLevel = 'none' | 'error' | 'info' | 'debug';
type FallbackReason = 'no_fill' | 'timeout' | 'error' | 'creative_failed' | 'disabled';
interface QartveloAdsInitOptions {
appKey: string;
requestTimeoutMs?: number;
testMode?: boolean;
testForceNoFill?: boolean;
admobFallback?: boolean;
logLevel?: LogLevel;
baseUrl?: string;
admobAdUnits?: Record;
}
interface AdInfo {
placementId: string;
format: AdFormat;
source: AdSource;
campaignId?: string; // Qartvelo Ads ads only
creativeId?: string; // Qartvelo Ads ads only
}
interface Reward { type: string; amount: number }
interface ShowResult { shown: boolean; source?: AdSource }
interface RewardedShowResult extends ShowResult { rewarded: boolean; reward?: Reward }
interface QartveloAdsPrivacy {
consentGiven?: boolean;
childDirected?: boolean;
underAgeOfConsent?: boolean;
}
type QartveloAdsErrorCode =
| 'not_initialized' | 'invalid_placement' | 'network_error' | 'timeout' | 'no_fill'
| 'creative_failed' | 'ad_expired' | 'show_failed' | 'already_showing' | 'internal_error'
| 'unsupported_platform' | 'module_unavailable' | 'invalid_argument';
class QartveloAdsError extends Error {
readonly code: QartveloAdsErrorCode;
readonly placementId?: string;
}
interface QartveloAdsSubscription { remove(): void }
```
Event payload types are exported as `QartveloAdsEventMap`, `QartveloAdsEvent`, `QartveloAdsEventType` and `QartveloAdsEventListener`. ## Testing with Jest [Section titled “Testing with Jest”](#testing-with-jest) Mock the package in your own tests:
```ts
jest.mock('@qartvelo/react-native-ads', () => ({
QartveloAds: {
initialize: jest.fn().mockResolvedValue(undefined),
loadInterstitial: jest.fn().mockResolvedValue({ placementId: 'game_end', format: 'interstitial', source: 'qartvelo' }),
showInterstitial: jest.fn().mockResolvedValue({ shown: true, source: 'qartvelo' }),
loadRewarded: jest.fn(),
showRewarded: jest.fn().mockResolvedValue({ shown: true, rewarded: true, reward: { type: 'reward', amount: 1 } }),
addListener: jest.fn(() => ({ remove: jest.fn() })),
setPrivacy: jest.fn(),
setLogLevel: jest.fn(),
},
QartveloAdsBanner: () => null,
isQartveloAdsError: () => false,
}));
```
Without a mock it behaves as on iOS (the Jest preset reports iOS): promises reject with `unsupported_platform` and the banner renders nothing.
# Events and errors
> Subscribe to SDK events and handle QartveloAdsError codes in React Native.
## Events [Section titled “Events”](#events) `QartveloAds.addListener(type, listener)` observes events of every placement and format, banners included. Remove subscriptions when the screen goes away:
```tsx
useEffect(() => {
const subscriptions = [
QartveloAds.addListener('fallbackStarted', (e) => log(`fallback ${e.placementId}: ${e.reason}`)),
QartveloAds.addListener('impression', (e) => analytics.track('ad_impression', { placement: e.placementId, source: e.source })),
];
return () => subscriptions.forEach((s) => s.remove());
}, []);
```
| Event | When | Extra fields | | ----------------- | --------------------------------------------------------- | ----------------------------------------------------------------------------------------- | | `loaded` | An ad is ready (Qartvelo Ads, or AdMob after a fallback) | `source`, `campaignId?`, `creativeId?` | | `loadFailed` | A load or show failed | `error: { code, message }`; `format` may be missing for a placement this app never loaded | | `shown` | The ad is on screen | `source`, ids | | `impression` | The impression was counted | `source`, ids | | `clicked` | The user tapped the ad (at most once per ad) | `source`, ids | | `dismissed` | A full-screen ad closed | `source`, ids | | `rewarded` | Completion confirmed for a rewarded ad | `source`, `reward: { type, amount }` | | `fallbackStarted` | Qartvelo Ads could not serve; the fallback is being tried | `reason`: `no_fill`, `timeout`, `error`, `creative_failed`, `disabled` | | `noAdAvailable` | No source has an ad | | Every payload has `type`, `placementId` and `format` (`banner`, `interstitial` or `rewarded`). `source` is `qartvelo` or `admob`; `campaignId` and `creativeId` are set only for Qartvelo Ads ads. Each `addListener` call is an independent subscription; `remove()` is safe to call twice. `QartveloAds.removeAllListeners(type?)` clears listeners in bulk. The native event stream is open only while at least one listener exists. Payloads are fully typed through `QartveloAdsEventMap`. ## Errors [Section titled “Errors”](#errors) Every promise rejects with a `QartveloAdsError` that has `code`, `message`, and `placementId` for placement calls. Narrow with `isQartveloAdsError`:
```ts
import { isQartveloAdsError } from '@qartvelo/react-native-ads';
try {
await QartveloAds.loadRewarded('reward_coins');
} catch (error) {
if (isQartveloAdsError(error) && error.code === 'no_fill') {
hideRewardButton();
}
}
```
| Code | Meaning | | --------------------------- | --------------------------------------------------------------------------------- | | `not_initialized` | `initialize()` was not called, or the backend rejected the app key / package name | | `invalid_placement` | Unknown placement code, or a placement used with the wrong format | | `no_fill` | Neither Qartvelo Ads nor the fallback had an ad | | `timeout` / `network_error` | The backend was slow or unreachable and no fallback was available | | `creative_failed` | The creative could not be downloaded or decoded | | `already_showing` | Another full-screen ad is on screen | | `show_failed` | The ad could not be displayed (for example no foreground Activity) | | `ad_expired` | The ad expired before it was shown | | `internal_error` | Unexpected native failure, or an unknown code | | `invalid_argument` | A JavaScript argument was rejected before reaching native code | | `unsupported_platform` | Called on a platform without the SDK (iOS, web) | | `module_unavailable` | The native module is missing: rebuild the Android app |
# Installation
> Install @qartvelo/react-native-ads and configure the Android build.
`@qartvelo/react-native-ads` brings Qartvelo Ads banner, interstitial and rewarded ads to React Native apps on Android. It is a thin bridge: campaign selection, timeouts, AdMob fallback, request de-duplication and the reward-once guarantee all run in the native [Kotlin SDK](/android/installation/). The JavaScript layer validates arguments, returns typed results and forwards events. * New Architecture only: a TurboModule (`NativeQartveloAds`) and a Fabric component (`QartveloAdsBannerView`), generated by codegen and autolinked. * Android only for now. On iOS and web every promise rejects with `unsupported_platform`, `` renders nothing and `QartveloAds.isSupported()` returns `false`, so shared code runs everywhere. ## Requirements [Section titled “Requirements”](#requirements) | | | | ------------ | ------------------------------------------------------------------------------------ | | React Native | 0.79 or newer with the New Architecture enabled (`newArchEnabled=true`, the default) | | Android | minSdk 24, compileSdk 35 or newer | | Build JDK | 17 | | Expo | Bare workflow or a development build (`expo prebuild`); not Expo Go | Tested with React Native 0.87.1 (React 19.2). ## 1. Install the package [Section titled “1. Install the package”](#1-install-the-package)
```sh
npm install @qartvelo/react-native-ads
# or: yarn add @qartvelo/react-native-ads
```
## 2. Add JitPack [Section titled “2. Add JitPack”](#2-add-jitpack) The plugin depends on the native SDK `com.qartvelo.ads:core:0.3.0` from JitPack. Add the repository to every project: android/build.gradle
```groovy
allprojects {
repositories {
maven {
url "https://jitpack.io"
content { includeGroup("com.qartvelo.ads") }
}
}
}
```
## 3. Enable the AdMob fallback (optional) [Section titled “3. Enable the AdMob fallback (optional)”](#3-enable-the-admob-fallback-optional) By default only the core SDK is included and a Qartvelo Ads no-fill simply reports “no ad”. To fall back to your own AdMob units: android/gradle.properties
```properties
QartveloAds_admobEnabled=true
# Optional: pin a different native SDK version (default 0.3.0)
# QartveloAds_sdkVersion=0.3.0
```
This adds `com.qartvelo.ads:admob`, which brings Google’s `play-services-ads` (25.4.0). Google requires your AdMob App ID in the manifest, or the app crashes at start-up: android/app/src/main/AndroidManifest.xml
```xml
```
Google’s sample App ID `ca-app-pub-3940256099942544~3347511713` works for development. ## 4. Rebuild [Section titled “4. Rebuild”](#4-rebuild) The TurboModule and banner component are compiled into the app, so rebuild after installing or upgrading:
```sh
npx react-native run-android
```
A JavaScript reload is not enough: calls reject with `module_unavailable` until the native app is rebuilt. ## Local backend over HTTP (debug only) [Section titled “Local backend over HTTP (debug only)”](#local-backend-over-http-debug-only) For a local backend at `http://10.0.2.2:8000/`, allow cleartext in debug builds: android/app/src/debug/res/xml/network_security_config.xml
```xml
10.0.2.2
localhost
```
android/app/src/debug/AndroidManifest.xml
```xml
```
`localhost` keeps Metro working on physical devices through `adb reverse tcp:8081 tcp:8081`. Next: [Usage](/react-native/usage/).
# Troubleshooting
> Fixes for common React Native build and runtime problems.
| Symptom | Fix | | ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | `Could not find com.qartvelo.ads:core:0.3.0` | Add JitPack to `allprojects.repositories` in `android/build.gradle` ([Installation](/react-native/installation/#2-add-jitpack)) | | App crashes at start: “The Google Mobile Ads SDK was initialized incorrectly” | `QartveloAds_admobEnabled=true` without the AdMob `APPLICATION_ID` meta-data. Add it, or disable the adapter | | Promises reject with `module_unavailable` | The app binary predates the package. Rebuild with `npx react-native run-android` | | Promises reject with `unsupported_platform` | Running on iOS or web; only Android has an SDK today. Guard with `QartveloAds.isSupported()` | | `initialize` rejects with `network_error` on the emulator (local backend) | Use `http://10.0.2.2:/`, not `localhost`, and allow cleartext for it in debug | | `initialize` rejects with `not_initialized` | The backend rejected the app key: check the key, that the package name equals your `applicationId`, and that the app is approved (or use `testMode`) | | Options seem ignored | Initialization happens once per process; force-stop and restart the app after changing options | | Every load ends with `fallbackStarted` reason `timeout` | The backend answered after the request timeout. Common on slow debug networks; raise the placement timeout in the dashboard while testing | | `fallbackStarted` but no AdMob ad | `QartveloAds_admobEnabled` is not set, `admobFallback` is false, the placement’s fallback is `none`, or no AdMob unit is configured for it | | Banner stays empty | Check `onLoadFailed`; give the component a width (it defaults to `100%`) and make sure only one banner per placement code is visible | | Two copies of React in Metro (local package) | Point Metro’s `watchFolders` at the package and block its `node_modules/react(-native)`, as in the example app’s `metro.config.js` | More in the general [Troubleshooting](/resources/troubleshooting/) page.
# Usage
> Initialize, show banners, interstitials and rewarded ads from React Native.
## Initialize [Section titled “Initialize”](#initialize) Call `initialize` once, as early as possible, for example in your root component. App.tsx
```tsx
import { useEffect } from 'react';
import { QartveloAds, isQartveloAdsError } from '@qartvelo/react-native-ads';
export default function App() {
useEffect(() => {
QartveloAds.setPrivacy({ consentGiven: undefined }); // see Privacy guide
QartveloAds.initialize({
appKey: 'app_xxxxxxxxxxxxxxxxxxxxxxxx',
testMode: __DEV__,
logLevel: __DEV__ ? 'debug' : 'error',
}).catch((error) => {
// Not fatal: the SDK keeps working on its cached config and the AdMob fallback.
if (isQartveloAdsError(error)) console.warn(error.code, error.message);
});
}, []);
return ;
}
```
| Option | Type | Default | Meaning | | ------------------ | ---------------------------------------- | --------------------------- | ------------------------------------------------------------------------------------ | | `appKey` | `string` | required | Publisher app key (`app_...`) | | `baseUrl` | `string` | `https://ads.qartvelo.com/` | API origin; change only for a self-hosted or local backend | | `requestTimeoutMs` | `number` | `800` | Qartvelo Ads time budget before falling back. The dashboard value per placement wins | | `testMode` | `boolean` | `false` | Non-billable test ads; AdMob uses Google’s test units | | `testForceNoFill` | `boolean` | `false` | Force Qartvelo Ads “no fill” to see the AdMob fallback | | `admobFallback` | `boolean` | `true` | Allow the AdMob adapter (needs `QartveloAds_admobEnabled=true`) | | `logLevel` | `'none' \| 'error' \| 'info' \| 'debug'` | `'error'` | Logcat verbosity (tag `QartveloAds`) | | `admobAdUnits` | `Record` | `{}` | Placement code to AdMob unit id; overrides the dashboard | * Initialization is idempotent for the life of the **process**. Later calls resolve or reject with the first result and ignore new options. Restart the app to change them; a JS reload is not enough. * A rejection (`network_error`, `timeout`, or `not_initialized` for a rejected key) does not disable ads. * Loads issued while initialization is still running wait for it. * `await QartveloAds.isInitialized()` is `true` once the first attempt finished. ## Banner [Section titled “Banner”](#banner)
```tsx
import { QartveloAdsBanner } from '@qartvelo/react-native-ads';
console.log('banner from', e.source)}
onLoadFailed={(e) => console.log(e.error.code)}
onNoAdAvailable={() => setShowBanner(false)}
/>
```
* Collapsed (height 0) until an ad is rendered, then takes the creative’s height. Set an explicit `height` to reserve space instead. * Props: `placementId` (required), `style`, `onLoaded`, `onLoadFailed`, `onShown`, `onImpression`, `onClicked`, `onFallbackStarted`, `onNoAdAvailable`, `onSizeChange`. * Re-renders never reach the SDK. Changing `placementId` loads the new placement. * Remounting (navigation, list recycling) does not request a new ad: the SDK keeps one banner per placement and the new view re-attaches. The component’s `onLoaded` fires again for the new view; global listeners do not get a second `loaded`. * Refresh follows the placement’s `banner_refresh_seconds` (minimum 30 s) and pauses while off screen or in the background. * One visible banner per placement code at a time. * A banner mounted before `initialize()` waits and loads as soon as initialization starts. ## Interstitial [Section titled “Interstitial”](#interstitial)
```tsx
async function onLevelComplete() {
try {
const result = await QartveloAds.showInterstitial('game_end'); // resolves on dismiss
if (!result.shown) {
// nothing was ready
}
} catch (error) {
// already_showing, show_failed, ...
} finally {
continueGame();
QartveloAds.loadInterstitial('game_end').catch(() => {}); // preload the next one
}
}
// Preload when the level starts
QartveloAds.loadInterstitial('game_end').catch(() => {});
```
* `loadInterstitial` resolves with `AdInfo` (`{ placementId, format, source, campaignId?, creativeId? }`) when an ad from Qartvelo Ads or AdMob is ready, and rejects when no source has one. Concurrent loads share one request. * `showInterstitial` resolves `{ shown: true, source }` after the ad is dismissed, or `{ shown: false }` immediately when nothing was ready. It rejects with `already_showing` while another full-screen ad is visible and with `show_failed` if the ad could not be displayed. * `isInterstitialReady(placementId)` resolves whether a show would display an ad now. * A Qartvelo Ads ad is valid for 30 minutes after loading. ## Rewarded [Section titled “Rewarded”](#rewarded)
```tsx
await QartveloAds.loadRewarded('reward_coins');
const result = await QartveloAds.showRewarded('reward_coins');
if (result.rewarded) {
grantCoins(50);
}
```
* `result.rewarded` is `true` exactly once per show and only after the SDK confirmed completion, whichever network served the ad. A skipped ad resolves `{ shown: true, rewarded: false }`. * `result.reward` is `{ type, amount }`: `{ type: 'reward', amount: 1 }` for Qartvelo Ads, your AdMob unit’s settings for AdMob. * Grant the reward from the promise result **or** the `rewarded` event, never from both. ## Hooks pattern [Section titled “Hooks pattern”](#hooks-pattern)
```tsx
import { useCallback, useEffect, useState } from 'react';
import { QartveloAds } from '@qartvelo/react-native-ads';
export function useRewarded(placementId: string) {
const [ready, setReady] = useState(false);
const load = useCallback(() => {
setReady(false);
QartveloAds.loadRewarded(placementId).then(() => setReady(true), () => setReady(false));
}, [placementId]);
useEffect(load, [load]);
const show = useCallback(async () => {
const result = await QartveloAds.showRewarded(placementId);
load();
return result.rewarded;
}, [placementId, load]);
return { ready, show };
}
```
## Privacy and logs [Section titled “Privacy and logs”](#privacy-and-logs)
```ts
QartveloAds.setPrivacy({ consentGiven: false, childDirected: true });
QartveloAds.setLogLevel('debug');
```
Omitted privacy fields stay unknown; consent is never assumed. See [Privacy](/guides/privacy/).
# AdMob fallback
> How the SDK falls back to your own AdMob ad units, and how to set it up.
Qartvelo Ads is the primary ad source. When it has no eligible campaign, fails, is switched off or exceeds its time budget, the SDK can show an ad from **your own** Google AdMob account instead, so a placement is never left empty. ## Your AdMob account, your ad units, your revenue [Section titled “Your AdMob account, your ad units, your revenue”](#your-admob-account-your-ad-units-your-revenue) * You create your own AdMob account, your own AdMob app and your own ad units (one per placement and format) and give those ids to Qartvelo Ads. * Qartvelo Ads never owns, shares, proxies or pools AdMob inventory and never routes several publishers through one AdMob account. * Google pays AdMob revenue directly to you. Qartvelo Ads does not receive, report, take a share of, or have access to your AdMob revenue or account. * The adapter uses Google’s official `com.google.android.gms:play-services-ads` artifact as a normal Gradle dependency, never a modified copy. * You remain responsible for complying with AdMob policies (placement, invalid traffic, consent). ## Setup [Section titled “Setup”](#setup) 1. In AdMob, create an app for your package and ad units that match your placements: banner units for banner placements, interstitial units for interstitial placements, rewarded units for rewarded placements. 2. Add the AdMob **App ID** (contains `~`) to `AndroidManifest.xml` as `com.google.android.gms.ads.APPLICATION_ID` meta-data. 3. Add the adapter: `implementation("com.qartvelo.ads:admob:0.3.0")`, or `QartveloAds_admobEnabled=true` in React Native. 4. Map each placement to its ad unit **ID** (contains `/`), either on the placement in the dashboard (fallback provider **AdMob**) or in code:
```kotlin
QartveloAdsOptions(
admobAdUnits = mapOf(
"home_banner" to "ca-app-pub-XXX/111",
"game_end" to "ca-app-pub-XXX/222",
"reward_coins" to "ca-app-pub-XXX/333",
),
)
```
The in-code mapping wins over the dashboard value for the same placement code. No other code is needed. The SDK discovers the adapter automatically, initializes Google Mobile Ads once on a background thread and reports AdMob ads through the same callbacks with `info.source == AdSource.ADMOB` (`'admob'` in React Native). ## When a fallback is possible [Section titled “When a fallback is possible”](#when-a-fallback-is-possible) All of these must hold for a placement: * the adapter is present (or a custom adapter was registered with `QartveloAds.registerFallbackAdapter`); * `admobFallback` is `true` in the options and the remote config allows fallback; * the placement’s fallback provider is `admob` (and the ad response did not say `fallback: none`); * an ad unit id is known for the placement (in code or in the dashboard), or test mode is on. ## Decision flow [Section titled “Decision flow”](#decision-flow) **Interstitial and rewarded loads** 1. A still-valid cached Qartvelo Ads ad completes the load immediately. 2. Otherwise the SDK requests Qartvelo Ads and, if a fallback is possible, **preloads AdMob in parallel**. 3. Qartvelo Ads fill: the creative is downloaded and validated, then `onLoaded(source = QARTVELO)`. 4. No fill, timeout, network error, creative failure or kill switch: `onFallbackStarted(reason)`, then `onLoaded(source = ADMOB)` as soon as AdMob is ready, or `onNoAdAvailable` + `onLoadFailed` if AdMob also has nothing. 5. Show prefers a valid Qartvelo Ads ad, then a ready AdMob ad, else `onNoAdAvailable`. **Banners** request Qartvelo Ads first. On failure, the AdMob anchored adaptive banner is created in the same view and refreshes itself according to your AdMob settings. The next successful Qartvelo Ads refresh replaces it. ## Fallback reasons [Section titled “Fallback reasons”](#fallback-reasons) | Reason | Meaning | | ----------------- | ------------------------------------------------------------------ | | `no_fill` | The backend had no eligible campaign (or test mode forced it) | | `timeout` | No answer within the request timeout, or the creative was too slow | | `error` | Network or server error | | `creative_failed` | The creative could not be downloaded, decoded or rendered | | `disabled` | Qartvelo Ads is switched off for this placement, app or publisher | The SDK also sends a fire-and-forget `POST /api/v1/events/fallback` with the placement code and the reason (never any AdMob data), so your reports show fallback counts. ## Timeouts and remote configuration [Section titled “Timeouts and remote configuration”](#timeouts-and-remote-configuration) * The Qartvelo Ads request budget (session plus ad request) defaults to 800 ms. Users never wait for it: loads are asynchronous and AdMob is already preloading. * Effective timeout: the placement’s remote value, else `requestTimeoutMs`, clamped to 100..10000 ms. * Creative downloads have separate limits (images 10 s, video 45 s), because a Qartvelo Ads ad is only reported as loaded once its creative is on the device. * Without an app update, the dashboard can switch Qartvelo Ads off per placement (the SDK goes straight to AdMob), change the fallback provider, change the timeout and change the AdMob unit. The last configuration is cached for offline starts. * If the API is unreachable at start-up, the SDK runs on the cached configuration and falls back quickly; repeated session attempts are throttled. ## Test mode [Section titled “Test mode”](#test-mode) With `testMode = true` the adapter replaces every unit with Google’s public test units and Qartvelo Ads serves only non-billable test creatives. Add `testForceNoFill = true` to see the fallback every time. Never ship a release with test mode or Google’s test ids. ## Consent [Section titled “Consent”](#consent) The adapter does not show consent forms and does not set consent. It respects your Google UMP / CMP configuration and only adds the restrictions you pass through `QartveloAds.setPrivacy`. See [Privacy](/guides/privacy/).