Skip to content
Agentmart
Submit a tool

admob-ads-and-consent

Builds AdMob ads behind a port - banners, interstitials, rewarded, UMP consent then ATT, pacing, ADS_MODE test IDs.

Skill by zakariaf

IndexedMIT★ 11
Write a review

Chalk Labs

Need help deploying admob-ads-and-consent?

Chalk Labs sets up agents like this inside real teams: connected to your tools, tested on your data, with a human in the loop where it matters.

Book a call

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/zakariaf-admob-ads-and-consent

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

Builds AdMob ads behind a port - banners, interstitials, rewarded, UMP consent then ATT, pacing, ADS_MODE test IDs. Use when touching ads, consent, ATT, ad IDs or rewarded perks. Not for purchases (in-app-purchases).

Use cases: testing, marketing

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md and code. Checked Oct 26, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found
Runs as
Node.js package

Network hosts it talks to 2

  • codeload.github.com · found in install command
  • developers.google.com · found in code

Files it touches

  • ~/.claude/skills/admob-ads-and-consent

What's inside

Skill name
admob-ads-and-consent
Description
Builds AdMob ads behind a port - banners, interstitials, rewarded, UMP consent then ATT, pacing, ADS_MODE test IDs. Use when touching ads, consent, ATT, ad IDs or rewarded perks. Not for purchases (in-app-purchases).
License
MIT
Instructions
279 lines in SKILL.md · view on GitHub

Before installing

  • Ships executable scripts

Files (128)

  • SKILL.md
  • admob-facts.json · assets/
  • shared.json · assets/
  • ad-privacy-row.tsx · examples/
  • ads-root.tsx · examples/
  • app-config-ads.ts · examples/
  • consent-primer.tsx · examples/
  • create-device-services.ts · examples/
  • tasks-screen.tsx · examples/
  • use-settings-model.ts · examples/
  • use-tasks-model.ts · examples/
  • ad-policy.md · references/
  • consent-flow.md · references/
  • console-privacy-troubleshooting.md · references/
  • placements-and-lifecycle.md · references/
  • setup-ids-and-modes.md · references/
  • check-ad-behaviour.mjs · scripts/
  • check-ads.mjs · scripts/
  • check-lib.mjs · scripts/
  • ads-facts.mjs · scripts/lib/
  • ads-rules.mjs · scripts/lib/
  • ads-scenarios.mjs · scripts/lib/
  • app-modules.mjs · scripts/lib/
  • consent-rules.mjs · scripts/lib/
  • consent-scenarios.mjs · scripts/lib/
  • load-ts.mjs · scripts/lib/
  • repo-scan.mjs · scripts/lib/
  • expo-constants.mjs · scripts/lib/stubs/
  • expo-tracking-transparency.mjs · scripts/lib/stubs/
  • react-native-google-mobile-ads.mjs · scripts/lib/stubs/
  • react-native.mjs · scripts/lib/stubs/
  • react.mjs · scripts/lib/stubs/
  • selftest.mjs · scripts/
  • expo-tracking-transparency.ts · templates/__mocks__/
  • react-native-google-mobile-ads.ts · templates/__mocks__/
  • ads.json · templates/contracts/
  • ads.schema.json · templates/contracts/
  • 01-consent-eea.yaml · templates/maestro/ads-smoke/
  • 02-relaunch.yaml · templates/maestro/ads-smoke/
  • 03-break-interstitial.yaml · templates/maestro/ads-smoke/
  • 04-rewarded.yaml · templates/maestro/ads-smoke/
  • 05-offline.yaml · templates/maestro/ads-smoke/
  • 06-geo-other.yaml · templates/maestro/ads-smoke/
  • refresh-skadnetwork.ts · templates/scripts/ads/
  • skadnetwork-list.test.ts · templates/scripts/ads/
  • skadnetwork-list.ts · templates/scripts/ads/
  • ad-pacing.test.ts · templates/src/config/
  • ad-pacing.ts · templates/src/config/
  • admob-ids.test.ts · templates/src/config/
  • admob-ids.ts · templates/src/config/
  • ads-config.test.ts · templates/src/config/
  • ads-config.ts · templates/src/config/
  • app-variant.test.ts · templates/src/config/
  • app-variant.ts · templates/src/config/
  • skadnetwork-ids.ts · templates/src/config/
  • result.test.ts · templates/src/core/
  • result.ts · templates/src/core/
  • use-ad-context.ts · templates/src/hooks/
  • use-banner-slot.test.tsx · templates/src/hooks/
  • use-banner-slot.ts · templates/src/hooks/
  • …and 68 more

SKILL.md (first lines)

# AdMob ads and consent

Makes an Expo app show Google AdMob ads that never interrupt a focused task: Google's consent and
Apple's tracking prompt come first, every "may an ad appear now?" answer comes from pure, tested
functions, testing builds only ever see Google's test ads, iOS and Android each get their own IDs, and
two zero-dependency checkers prove it on the real code.

## Rules that must hold

1. **Ads never interrupt a focused task.** Banners only on the screens contracts/ads.json `bannerSlots`
   declares (browsing screens, never a form, editor, dialog, paywall or onboarding), and only while that
   screen is focused (`useBannerSlot(screen, facts, useIsFocused())`: stacks and tabs keep it mounted
   under pushed screens); interstitials only from the press handler that leaves a natural break the user
   just finished, never on mount, app start or during onboarding; rewarded ads only on the user's tap
   behind a clear "Watch an ad to ..." label; the AdsPort's show, initialize and preload calls only in
   `ad-moments.ts` and `ad-gate.ts`. *Why:* an interruption costs the user's trust, and Google's policies
   punish ads that surprise or invite accidental taps.
2. **Every ad decision comes from `ad-policy.ts` / `rewarded-offer.ts`** (pure; numbers from
   `src/config/ad-pacing.ts`, at or above contracts/ads.json `pacingFloors`, default 3 breaks before the
   first interstitial, then 3 minutes and 2 breaks between two), and every finished natural break is
   saved with `recordBreak` where it finishes. *Why:* only pure rules can be tested one by one, the
   numbers are configuration, and a history that never counts a break allows exactly one interstitial.
3. **Consent, then Apple's prompt, before any ad request, through the consent moment, and none at all
   when ads are off.** UMP refreshes at every launch; where Google's form is required, the app's optional
   primer and then the form; once `canRequestAds`, ATT through `ConsentPort.requestTracking()` (iOS only,
   while not-determined, the app active); then `setRequestConfiguration` + `initialize` + preload. Only
   after onboarding, online, not Premium, over a focused ad screen. `AdsProvider` (through
   `consent-moment-flow.ts`) is the only runner of `prepareAds`; banners are allowed only after the ATT
   answer and a successful `initialize`, and the adapter mounts its native banner only after it. The
   ConsentPort comes from `createConsentPort(adsMode, () => createAdmobConsentAdapter(...))`, so an
   `ADS_MODE=off` build never calls UMP or ATT. *Why:* Google requires consent (EEA, UK, Switzerland, US
   states), Apple's guideline 5.1.2(i) requires the ATT answer before IDFA use, the SDK may preload at
   initialisation, and a UMP update is a network request that would fail the network audit of off builds.
4. **Testing builds never see real ads; real IDs reach the runtime only in production + live builds.**
   `resolveAdsMode` decides: development and preview -> `test` (or `off`), production -> `live` (or
   `off`); the other pairs throw. IDs live only in `src/config/admob-ids.ts`, read only by
   `app.config.ts`; `admobPluginOptions` always gives both platforms an app ID (Google's sample unless
   live); `adsExtra` writes units only in live builds; the template's placeholder IDs are noted and
   never reach a store build. `expo.extra` is embedded at build time, or replaced by the running EAS
   Update's manifest, so `eas update` runs with the channel's environment and the app reads it through
   `readAdsExtraForBinary(Application.applicationId)`, which turns ads off for a manifest made for
   another variant's app id. *Why:* real ads in a testing build risk the AdMob account; a missing app ID
   stops the app at launch.
5. **`react-native-google-mobile-ads` is exactly 17.2.0 and is imported only by `admob-ads-adapter.ts`,
   `admob-consent-adapter.ts` and the test-only `admob-consent-debug-adapter.ts`**, with the classic
   create/load/show API (no v17 hooks or pools), and the adapters are created only inside
   `createAdsPort` / `createConsentPort` in the composition root. *Why:* an upgrade touches three files,
   pacing stays in pure code, and pools preload (on Android initialise) before consent.
6. **The reward is granted only on `EARNED_REWARD`, and the AdsPort reports a `RewardedStatus`
   (`'loading' | 'ready' | 'unavailable'`), never a loaded flag.** `rewardedOffer` gives a perk the user
   waits on `'loading'` and `'hidden'` only when no ad can come, so a flow waiting on it moves on. *Why:*
   with a flag, "not loaded" means both "still loading" and "cannot come", and a waiting user is stranded.
7. **Pause media around every fullscreen ad (`runFullscreenAd` with the one `MediaLifecycle`), resume in
   `finally`, and make every show settle** on `CLOSED`, on `ERROR` (phase `show`) and on a rejected
   `show()`. *Why:* iOS does not background the app for these ads, so nothing else pauses audio or
   timers, and a show that never settles leaves media suspended and the handler stuck.
8. **Ads are silent and their failures are values.** Never an error, spinner or "please connect" for
   ads; failures go to the error log; branch on `error.phase` then `error.reason`, never `error.code`;
   `initialize()` returns a Result; the banner slot has zero height until an ad has loaded. *Why:* an ad
   is never what the user came for, and an escaped rejection is dropped silently in release builds.
9. **The audience is declared and enforced, and tracking follows Apple's rules.** contracts/ads.json
   `audience`: `general` -> the declared `maxAdContentRating` (default PG), no child tags;
   `child-directed` -> G, both child tags, Android's `AD_ID` permission blocked, and no ATT at all (no
   tracking package, plugin, purpose text or adapter; `tracking: null`). General audience:
   `expo-tracking-transparency` is imported only by `expo-tracking-consent-adapter.ts`, handed to the
   consent adapter as `tracking`; its plugin entry gets the source text and every language's catalog has
   it; the AdMob plugin gets no `userTrackingUsageDescription`; the console publishes no IDFA explainer.
   Declined, restricted or unavailable tracking still serves ads, and nothing is gated on the answer.
   *Why:* the rating must match the console, apps for children may not send device data to third
   parties (App Review 1.3, Play Families), a missing purpose text crashes, and 5.1.2(i) forbids gating
   on ATT.
10. **The SKAdNetwork list is Google's full list of `xxxxxxxxxx.skadnetwork` identifiers, generated and
    checked (`--check`) before every release.** *Why:* the plugin copies only what it gets, and Google
    changes the list.
11. **Consent reset, the debug geography and the Ad Inspector exist only in test-only code** (the
    debug adapter and debug options reached through the test-only entry). *Why:* they change consent
    behaviour for real users.
12. **The developer does the AdMob console steps; ask and wait, never invent IDs or publish anything.**
    *Why:* the console is the developer's account, and an invented ID or a published setting reaches real
    users.

## Workflow

1. **Install.** Read [references/setup-ids-and-modes.md](references/setup-ids-and-modes.md):
   `npm install -E react-native-google-mobile-ads@17.2.0` and
   `npx expo install expo-tracking-transparency expo-network expo-constants expo-application` (a
   child-directed app leaves out `expo-tracking-transparency`).
2. **Copy the templates** at the paths their first lines name (never rewrite them from memory):
   `templates/src/services/ads/`, `templates/src/services/consent/admob-consent-debug-adapter.ts` (+ test),
   `templates/src/config/{ads-config,admob-ids,ad-pacing,skadnetwork-ids}.ts` (+ tests),
   `templates/src/hooks/`, `templates/src/ui/ad-banner-slot.tsx` (+ test), `templates/scripts/ads/`,
   `templates/contracts/ads.json` (+ schema; fill the banner screens, audience and floors). The synced
   canonical files (`src/services/consent/*`, `src/services/connectivity/*`, `src/config/app-variant.ts`,
   `src/core/result.ts`, the two root mocks) are copied only when the app lacks them; a child-directed
   app deletes `expo-tracking-consent-adapter.ts` (+ tests) and the tracking mock.
3. **Wire `app.config.ts`** as [examples/app-config-ads.ts](examples/app-config-ads.ts) shows:
   `resolveAdsMode`, the two plugin entries, `extra: { ...adsExtra(adsMode, ADMOB_IDS) }`, the tracking
   text in every language's `locales` (none of the tracking lines for a child-directed app). With EAS the
   next build applies it; locally rerun `npx expo prebuild --clean`. Refresh the native config snapshot
   (expo-prebuild-and-config-plugins' checker with `--write`: `APPLICATION_ID` and
   `DELAY_APP_MEASUREMENT_INIT` change) and commit it with the plugin change. Publish every later
   `eas update` with the channel's environment (`--environment <env>`, `ADS_MODE` unset).
4. **Compose the ports** in the composition root ([examples/create-device-services.ts](examples/create-device-services.ts)):
   add `ads`, `consent`, `connectivity`, `fullscreenAd` to `Services`, `createDeviceServices` and
   `createTestServices` (architecture-and-boundaries' port recipe), with
   `readAdsExtraForBinary(Application.applicationId)` and the consent adapter's `tracking`.
5. **Mount the consent moment.** Read [references/consent-flow.md](references/consent-flow.md); render
   `AdsRoot` ([examples/ads-root.tsx](examples/ads-root.tsx)) inside `RootStack` around `AppStack`, with
   the app's Premium, onboarding and saved consent facts and, optionally, a primer
   ([examples/consent-primer.tsx](examples/consent-primer.tsx)). Show the "Ad privacy choices" entry only
   while required: the settings model returns `adPrivacy`
   ([examples/use-settings-model.ts](examples/use-settings-model.ts)), the row is a pure view
   ([examples/ad-privacy-row.tsx](examples/ad-privacy-row.tsx)).
6. **Place the ads.** Read [references/ad-policy.md](references/ad-policy.md) and
   [references/placements-and-lifecycle.md](references/placements-and-lifecycle.md); follow
   [examples/use-tasks-model.ts](examples/use-tasks-model.ts) and [examples/tasks-screen.tsx](examples/tasks-screen.tsx):
   `useBannerSlot('<screen>', facts, useIsFocused())` in the model hook and `AdBannerSlot` in the view;
   `recordBreak` where a break finishes; `showInterstitialIfDue` in the press handler that leaves it;
   `useRewardedOffer` and `earnRewardedPerk` for rewards; the `fullscreenAd` gate for every fullscreen ad.

From zakariaf/ReactNative-Skills, MIT.

More from zakariaf/ReactNative-Skills

Signals

Stars
11
Forks
2
Open issues
0
Last commit (this folder)
Oct 17, 2026 (14 days ago)

From GitHub (fetched Oct 24, 2026).

Details

License
MIT
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from zakariaf

Done for you

Rather not set it up yourself?

Chalk Labs scopes, builds and runs agents for your team: the task audit, the eval harness, the integrations and the weekly ops report. Fixed scope, priced before we start.

See how we build AI agents →Read the case studies

Chalk Labs is a two-founder AI and growth studio. We build AI agents, CRMs and MVPs for Web3 and AI companies, and have shipped 50+ product and token launches.

launches shipped
50+
launches shipped
media placements
500+
media placements
founders on every account
2
founders on every account

Press we've placed: Forbes · CoinDesk · Decrypt · Cointelegraph · The Block

Rahil Jain, Co-founder, Chalk Labs. Ex-Goldman Sachs engineer. You talk to the person who builds it.

Get admob-ads-and-consent running in your business

Tell us what you want to automate. A founder replies within one working day with a plan, not a pitch deck.

Goes to the Chalk Labs founders, not the newsletter. Privacy

Need help deploying admob-ads-and-consent?

CallGet help
Cite this page

admob-ads-and-consent skill. Agent Mart. https://agentmart.dev/skills/zakariaf-admob-ads-and-consent (last updated 2026-10-24)