Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,18 @@ module.exports = {
modulePathIgnorePatterns: [
'<rootDir>/RNGoogleMobileAdsExample/node_modules',
'<rootDir>/packages/core/lib/',
'<rootDir>/packages/_template/lib/',
'<rootDir>/packages/applovin/lib/',
'<rootDir>/packages/facebook/lib/',
'<rootDir>/packages/unity/lib/',
'<rootDir>/packages/pangle/lib/',
'<rootDir>/packages/vungle/lib/',
'<rootDir>/packages/moloco/lib/',
'<rootDir>/packages/mintegral/lib/',
'<rootDir>/packages/inmobi/lib/',
'<rootDir>/packages/yandex/lib/',
],

setupFiles: ['./jest.setup.ts'],
testPathIgnorePatterns: ['<rootDir>/RNGoogleMobileAdsExample'],
testRegex: '(/__tests__/.*\\.(test|spec))\\.[jt]sx?$',
Expand Down
32 changes: 32 additions & 0 deletions packages/_template/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
Apache-2.0 License
------------------

Copyright (c) 2021-present Invertase Limited <oss@invertase.io>

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this library except in compliance with the License.

You may obtain a copy of the Apache-2.0 License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.


Creative Commons Attribution 3.0 License
----------------------------------------

Copyright (c) 2016-present Invertase Limited <oss@invertase.io>

Documentation and other instructional materials provided for this project
(including on a separate documentation repository or it's documentation website) are
licensed under the Creative Commons Attribution 3.0 License. Code samples/blocks
contained therein are licensed under the Apache License, Version 2.0 (the "License"), as above.

You may obtain a copy of the Creative Commons Attribution 3.0 License at

https://creativecommons.org/licenses/by/3.0/
60 changes: 60 additions & 0 deletions packages/_template/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# GAM adapter package template

**Private scaffold** (`@invertase/rngma-gam-adapter-template`). Character network packages copy from here; this directory is **not** a publishable `@react-native-google-mobile-ads/<network>` package.

Scope:

- Scoped package layout + bob/exports
- Android Gradle hook for `com.google.ads.mediation:<network>`
- CocoaPods hook for `GoogleMobileAdsMediation*`
- Optional Expo plugin slice (SKAdNetwork merge only — app IDs stay in core)
- Public JS surface: `nativeAdapterClassName` + `networkSlug` only (no ad APIs)

Out of scope: MAX / CloudX / Yandex-as-host packages; filling AppLovin/Meta/… product SDK pins (add one network package per commit); core public JS API changes.

## Layout

```text
packages/_template/
package.json # private; gamAdapter + sdkVersions placeholders
src/index.ts # nativeAdapterClassName / networkSlug
android/build.gradle # api mediation artifact from package.json
android/src/main/... # empty manifest + package marker
ios/*.m # pod source marker
RNGoogleMobileAdsAdapter.podspec
app.plugin.js + plugin/ # optional Expo SKAdNetwork merge
react-native.config.js # autolinking (no TurboModule)
__tests__/ # template surface unit tests
README.md # this file
```

## Instantiating a network package

1. Copy this tree to `packages/<network>/` (or fill an existing empty network shell under `packages/`).
2. Set `private` → omit / `false`, rename package to `@react-native-google-mobile-ads/<network>`.
3. Replace every `__PLACEHOLDER__` token:

| Token | Example (AppLovin GAM adapter — verify at ship time) |
| ----- | ---------------------------------------------------- |
| `__NETWORK__` | `applovin` |
| `__ANDROID_ARTIFACT__` | `applovin` → artifact `com.google.ads.mediation:applovin` |
| `__ANDROID_MEDIATION_VERSION__` | vendor pin from Google mediation release notes |
| `__ANDROID_ADAPTER_CLASS__` | `com.google.ads.mediation.applovin.AppLovinMediationAdapter` |
| `__IOS_MEDIATION_POD__` | `GoogleMobileAdsMediationAppLovin` |
| `__IOS_MEDIATION_VERSION__` | vendor pin |
| `__IOS_ADAPTER_CLASS__` | `GADMediationAdapterAppLovin` |

4. Rename podspec / `s.name` / Android `namespace` / Java package / Expo plugin export to the network.
5. Update `repository.directory`, keywords, README (Gradle/pod FQCN for GAM UI paste).
6. Do **not** add MAX host, CloudX host, or Yandex-as-host SDKs.
7. Peer-depend on `react-native-google-mobile-ads` only; never import core internals.

Empty shells under `packages/{applovin,facebook,…}/` stay metadata-only until each network is filled in its own commit.

## Build

```bash
yarn workspace @invertase/rngma-gam-adapter-template prepare
```

Root `yarn prepare` (Lerna) also builds this private package when present.
39 changes: 39 additions & 0 deletions packages/_template/RNGoogleMobileAdsAdapter.podspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
require 'json'

package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
gam_adapter = package['gamAdapter']
mediation_pod = gam_adapter['iosPod']
mediation_version = package['sdkVersions']['ios']['googleMobileAdsMediation']

Pod::Spec.new do |s|
# When instantiating: rename file + s.name to RNGoogleMobileAdsAdapter<Network>
# e.g. RNGoogleMobileAdsAdapterApplovin.podspec / RNGoogleMobileAdsAdapterApplovin
s.name = "RNGoogleMobileAdsAdapterTemplate"
s.version = package["version"]
s.summary = package["description"]
s.description = package["description"]
s.homepage = "https://github.com/invertase/react-native-google-mobile-ads"
s.license = package["license"]
s.authors = "Invertase Limited"
s.source = {
:git => "#{package["repository"]["url"]}.git",
:tag => "v#{s.version}"
}
s.platforms = { :ios => "12.0" }
s.source_files = "ios/**/*.{h,m,mm,swift}"

if respond_to?(:install_modules_dependencies, true)
install_modules_dependencies(s)
else
s.dependency "React-Core"
end

# Google-published GAM mediation adapter pod. Replace __IOS_MEDIATION_POD__ /
# __IOS_MEDIATION_VERSION__ when instantiating. Core ships Google-Mobile-Ads-SDK.
if mediation_pod && !mediation_pod.include?("__") &&
mediation_version && !mediation_version.include?("__")
s.dependency mediation_pod, mediation_version
else
Pod::UI.puts "#{s.name}: placeholder mediation pod/version — replace tokens before app use (A-0 template)."
end
end
20 changes: 20 additions & 0 deletions packages/_template/__tests__/adapterTemplate.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { nativeAdapterClassName, networkSlug } from '../src';

describe('A-0 GAM adapter template public surface', () => {
it('exports networkSlug placeholder for copy-replace', () => {
expect(networkSlug).toBe('__NETWORK__');
});

it('exports nativeAdapterClassName placeholders (API design §5.3)', () => {
expect(nativeAdapterClassName).toEqual({
android: '__ANDROID_ADAPTER_CLASS__',
ios: '__IOS_ADAPTER_CLASS__',
});
});

it('does not invent JS ad APIs on the template surface', () => {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const surface = require('../src') as Record<string, unknown>;
expect(Object.keys(surface).sort()).toEqual(['nativeAdapterClassName', 'networkSlug']);
});
});
78 changes: 78 additions & 0 deletions packages/_template/android/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import groovy.json.JsonSlurper

buildscript {
ext.getExtOrDefault = { name, fallback ->
return rootProject.ext.has(name) ? rootProject.ext.get(name) : fallback
}

// AGP is only required when opening this android/ folder stand-alone.
if (project == rootProject) {
repositories {
google()
mavenCentral()
}
dependencies {
classpath("com.android.tools.build:gradle:8.1.1")
}
}
}

apply plugin: "com.android.library"

def packageJson = new JsonSlurper().parseText(file("${projectDir}/../package.json").text)
def gamAdapter = packageJson.gamAdapter
def sdkVersions = packageJson.sdkVersions.android
def mediationArtifact = "${gamAdapter.androidArtifact}:${sdkVersions.googleMobileAdsMediation}"

android {
namespace "io.invertase.googlemobileads.adapters.template"

compileSdkVersion sdkVersions.compileSdk as Integer

defaultConfig {
minSdkVersion sdkVersions.minSdk as Integer
targetSdkVersion sdkVersions.targetSdk as Integer
buildConfigField "String", "GAM_ADAPTER_NETWORK", "\"${gamAdapter.network}\""
buildConfigField "String", "GAM_ADAPTER_ANDROID_CLASS", "\"${gamAdapter.androidAdapterClassName}\""
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}

buildFeatures {
buildConfig true
}

sourceSets {
main {
java.srcDirs = ["src/main/java"]
}
}
}

repositories {
google()
mavenCentral()
}

dependencies {
// Google-published GAM mediation adapter (same coordinates for classic + Next-Gen).
// Replace __ANDROID_ARTIFACT__ / __ANDROID_MEDIATION_VERSION__ when instantiating.
// Core GMA is provided by react-native-google-mobile-ads; do not re-pin it here.
api mediationArtifact
}

// Guard: fail fast if this private template is linked without token replacement.
afterEvaluate {
if (gamAdapter.network == "__NETWORK__" ||
"${sdkVersions.googleMobileAdsMediation}".contains("__") ||
"${gamAdapter.androidArtifact}".contains("__")) {
logger.warn(
"[rngma-gam-adapter-template] Placeholder tokens remain in package.json. " +
"Copy packages/_template → packages/<network>/ and replace __PLACEHOLDER__ values " +
"before depending on this package in an app."
)
}
}
6 changes: 6 additions & 0 deletions packages/_template/android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
GAM adapter packages carry Google mediation artifacts only.
App ID / measurement flags live in core (react-native-google-mobile-ads).
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.invertase.googlemobileads.adapters.template;

/**
* Package marker so the Android library module has a compilable source root.
* Network packages do not expose a TurboModule — mediation is linked via Gradle.
*
* When copying the template, rename this package path to match
* {@code io.invertase.googlemobileads.adapters.<network>} and update
* {@code android.namespace} in {@code build.gradle}.
*/
public final class AdapterPackageMarker {
private AdapterPackageMarker() {}
}
1 change: 1 addition & 0 deletions packages/_template/app.plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('./plugin/build');
12 changes: 12 additions & 0 deletions packages/_template/ios/RNGoogleMobileAdsAdapterTemplateMarker.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#import <Foundation/Foundation.h>

/**
* Package marker so the podspec has a compilable source file.
* Mediation is linked via the GoogleMobileAdsMediation* pod dependency.
* Rename/remove when instantiating if the network package needs no ObjC.
*/
@interface RNGoogleMobileAdsAdapterTemplateMarker : NSObject
@end

@implementation RNGoogleMobileAdsAdapterTemplateMarker
@end
Loading
Loading