novvy-ads-react-native-admob 0.0.0-stage → 1.2.1-stable.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,50 @@
1
+ # Changelog
2
+
3
+ ## 1.2.1-stable.0
4
+
5
+ Unreleased — not yet published to npm.
6
+
7
+ Initial release. Delivers the native AdMob mediation adapter — rewarded,
8
+ interstitial and banner served to an existing AdMob waterfall as a custom
9
+ event — on both platforms:
10
+
11
+ - **Android**: `ai.novvy.android:admob-adapter:1.2.0.0` from Maven Central,
12
+ as a single `implementation` line. The AAR carries its own
13
+ `consumer-rules.pro`, so R8 keeps the adapter class even though nothing
14
+ references it at compile time.
15
+ - **iOS**: the adapter's Swift sources, compiled into this package's pod and
16
+ linked against the `NovvyAds.xcframework` that `novvy-ads-react-native`
17
+ vendors. `pod install` fetches them from the `admob-v1.2.1.0` tag of
18
+ `novvy-ads-cocoapods` — always from the tag, never from a local checkout —
19
+ and the pod adds `-ObjC` to the app's link flags itself, which a stock React
20
+ Native `Podfile` does not supply.
21
+
22
+ No JavaScript API. The package exports four version constants for diagnostics
23
+ (`NOVVY_ADMOB_ADAPTER_VERSION_ANDROID` / `_IOS`,
24
+ `NOVVY_ADS_CORE_VERSION_ANDROID` / `_IOS`) and nothing else; adding the
25
+ dependency is the entire integration, and autolinking finds both native halves
26
+ without a `react-native.config.js`.
27
+
28
+ Requires:
29
+
30
+ - `novvy-ads-react-native` **exactly `1.2.1`**. Each native half is compiled
31
+ against one core; a range risks two SDK instances in the process, which is a
32
+ permanent silent no-fill. `1.2.1` is the first GA core — it carries the
33
+ banner placement that `1.2.0-beta.1` introduced as a prerelease, with the
34
+ native SDKs split onto independent version lines (iOS 1.2.1, Android 1.2.0).
35
+ - `react-native-google-mobile-ads` **`>=15.0.0 <16.1.0`**. The iOS adapter needs
36
+ Google Mobile Ads SDK 12.x, and RNGMA pins GMA to an exact version, so the
37
+ usable window is narrow. A 16.1+ host fails at `pod install`.
38
+ - React Native 0.76+ with the New Architecture, Android `minSdk` 24, iOS 13.0.
39
+
40
+ Android places no constraint on the Google Mobile Ads version: the adapter
41
+ compiles against `play-services-ads` as `compileOnly`, so the host keeps
42
+ ownership of it and this package declares no Gradle dependency on it.
43
+
44
+ > Versions: this package's version names the `novvy-ads-react-native` version it
45
+ > is built against (`1.2.1`) plus a release counter (`-stable.0`). The native
46
+ > adapters it delivers are versioned separately, per platform, as the core each
47
+ > was compiled against plus a revision — Android `1.2.0.0`, iOS `1.2.1.0`. The
48
+ > three lines spell three different strings at this release and that is the
49
+ > scheme working, not drift: the npm line follows the core npm package, the
50
+ > native lines follow their own platforms' SDKs. See the README.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) NovvyAI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,237 @@
1
+ # iOS half of novvy-ads-react-native-admob.
2
+ #
3
+ # There is no source file of our own in this pod — see the `source_files` note
4
+ # below. Everything here exists to get the adapter's Swift sources compiled into
5
+ # the host app and to stop the linker from throwing them away again.
6
+
7
+ # The two values a release edits.
8
+ #
9
+ # Composed rather than written out, so the NovvyAds version this pod's sources
10
+ # were cut against and the version encoded in the pod's own version are the same
11
+ # string by construction. Mirrors `novvy-ads-cocoapods/NovvyAdsAdMob.podspec`
12
+ # and the Flutter package's `novvy_ads_admob.podspec`, which compose the same
13
+ # two values for the same reason — and the reason is worth restating:
14
+ # `1.2.0-beta.1.0` split as core `1.2.0-beta` + revision `1.0` just as readily
15
+ # as it split the way it was meant, and both spellings look plausible enough to
16
+ # publish.
17
+ #
18
+ # ⚠️ These two are the NATIVE version line. They are not this npm package's
19
+ # `version` field, which tracks `novvy-ads-react-native` instead and reads
20
+ # `1.2.1-stable.0` at this release — visibly a different string from
21
+ # `1.2.1.0`, which is the divergence the previous release's comment predicted
22
+ # for the day the core reached GA. The two lines move independently — see the
23
+ # "two version lines" table in docs/plan/admob-mediation.md §四, and the
24
+ # exported constants in index.js, which are generated from nothing and must be
25
+ # edited alongside these.
26
+ #
27
+ # ⚠️ The native line is per-platform since the core's 1.2.1 generation:
28
+ # `novvy-ads-react-native` 1.2.1 vendors NovvyAds iOS 1.2.1 while resolving
29
+ # `ai.novvy.android:sdk:1.2.0` on Android, so this pod's sources come from the
30
+ # `admob-v1.2.1.0` tag while `android/build.gradle` pins the Android adapter at
31
+ # `1.2.0.0`. The iOS line moved; the Android one deliberately did not — see the
32
+ # comment on that coordinate.
33
+ NOVVY_ADS_VERSION = '1.2.1'
34
+ ADAPTER_REVISION = 0
35
+
36
+ # The tag in `novvy-ads-cocoapods` the adapter sources come from, and the stamp
37
+ # value `prepare_command` writes so a second `pod install` is a no-op.
38
+ ADAPTER_TAG = "admob-v#{NOVVY_ADS_VERSION}.#{ADAPTER_REVISION}"
39
+ ADAPTER_TARBALL = "https://github.com/NovvyAI/novvy-ads-cocoapods/archive/refs/tags/#{ADAPTER_TAG}.tar.gz"
40
+
41
+ Pod::Spec.new do |s|
42
+ s.name = 'NovvyAdsReactNativeAdMob'
43
+ s.version = "#{NOVVY_ADS_VERSION}.#{ADAPTER_REVISION}"
44
+
45
+ s.summary = 'AdMob mediation adapter for the Novvy Ads React Native SDK.'
46
+ s.description = <<-DESC
47
+ Lets an existing AdMob waterfall call Novvy as a custom event. Carries the
48
+ native adapter into the host build; no JavaScript API and no TurboModule.
49
+ DESC
50
+ s.homepage = 'https://novvy.ai/'
51
+ # The package's own LICENSE, not the repo root's. A `:file => '../LICENSE'`
52
+ # would resolve in this checkout and fail for every consumer, because npm ships
53
+ # only what package.json's `files` whitelist names — the repo root is not in
54
+ # the tarball.
55
+ s.license = { :type => 'MIT', :file => 'LICENSE' }
56
+ s.author = { 'NovvyAI' => 'dev@novvy.ai' }
57
+
58
+ # Never fetched by CocoaPods. React Native's autolinking always declares this
59
+ # pod as `:path => '../node_modules/novvy-ads-react-native-admob'`, so the
60
+ # source is whatever npm installed. Deliberately not `:git => ..., :tag =>
61
+ # "#{s.version}"` like the SDK repo's podspec: s.version here is the native
62
+ # line (`1.2.1.0`) and no git tag by that name exists in this repo.
63
+ s.source = { :path => '.' }
64
+ s.platform = :ios, '13.0'
65
+ s.swift_version = '5.0'
66
+
67
+ # The adapter sources, put there by `prepare_command` below — not in git.
68
+ #
69
+ # ⚠️ Asymmetry with the Flutter package, which also lists a `Classes/**/*`
70
+ # holding an empty plugin registrant: Flutter's tooling requires a registrant
71
+ # class per plugin, so it has a file there with nothing in it. React Native's
72
+ # iOS autolinking names no plugin class at all — it discovers pods from the
73
+ # podspec and TurboModules from codegen, neither of which this package has. So
74
+ # the iOS half needs zero source files of its own.
75
+ #
76
+ # This is the exact mirror image of Android, where RN is the platform that
77
+ # *does* demand an empty registration class (see NovvyAdsAdMobPackage.kt) and
78
+ # Flutter's requirement is the silent one. Neither framework is consistently
79
+ # stricter; each is strict on a different side.
80
+ s.source_files = 'ios/NovvyAdsAdMob/**/*.swift'
81
+
82
+ # Where `import NovvyAds` resolves from: NovvyAdsReactNative vendors
83
+ # NovvyAds.xcframework and CocoaPods propagates a dependency's
84
+ # FRAMEWORK_SEARCH_PATHS to dependent pod targets.
85
+ #
86
+ # No version requirement, deliberately. The coupling is real — these sources
87
+ # come from a tag that pins one exact core version — but it is stated once, in
88
+ # package.json's exact `peerDependencies` pin, where npm can act on it.
89
+ # Restating it here would mean two constraints to keep in agreement, and the
90
+ # podspec's copy would be the one nobody remembers to bump.
91
+ s.dependency 'NovvyAdsReactNative'
92
+
93
+ # The Google Mobile Ads SDK, which on React Native also arrives via
94
+ # `react-native-google-mobile-ads`. Unlike Android — where the adapter takes
95
+ # play-services-ads as `compileOnly` and lets the host own the version — a
96
+ # source pod has no compileOnly: without this dependency GMA's headers are not
97
+ # on the search path and the adapter does not compile.
98
+ #
99
+ # `~> 12.0` is the range the adapter sources are written against (they use the
100
+ # un-prefixed GMA 12.x Swift API — `MediationAdapter`, `VersionNumber`). It has
101
+ # to intersect whatever `react-native-google-mobile-ads` asks for, and RNGMA
102
+ # asks with a *bare* version string, which CocoaPods reads as `= x.y.z`
103
+ # (RNGoogleMobileAds.podspec:65, value from package.json's
104
+ # sdkVersions.ios.googleMobileAds). So the intersection is a single point, and
105
+ # the usable RNGMA range is narrow:
106
+ #
107
+ # react-native-google-mobile-ads 14.2.5 -> GMA 11.7.0 no intersection
108
+ # react-native-google-mobile-ads 15.0.0 -> GMA 12.2.0 ok (lower bound)
109
+ # react-native-google-mobile-ads 16.0.3 -> GMA 12.14.0 ok (upper bound)
110
+ # react-native-google-mobile-ads 16.1.0 -> GMA 13.1.0 no intersection
111
+ #
112
+ # Hence the `>=15.0.0 <16.1.0` peer range in package.json. A 16.1+ host fails
113
+ # during `pod install`, which is the good failure — loud, before a build.
114
+ #
115
+ # ⚠️ RNGMA offers an escape hatch that turns that good failure into a bad one:
116
+ # `$RNGoogleMobileAdsSDKVersion` in the Podfile overrides its GMA version
117
+ # (RNGoogleMobileAds.podspec:58-61). Forcing 12.14.0 on a 16.1+ host makes
118
+ # `pod install` succeed and then breaks RNGMA's own code, which is compiled
119
+ # against the GMA 13 API — compile errors, or worse, a build that links and is
120
+ # missing symbols at runtime. Supporting GMA 13 means new adapter sources in
121
+ # novvy-ads-cocoapods, not a wider range here.
122
+ s.dependency 'Google-Mobile-Ads-SDK', '~> 12.0'
123
+
124
+ # Deliberately NOT called: `install_modules_dependencies(s)`.
125
+ #
126
+ # The SDK repo's NovvyAdsReactNative.podspec calls it because it has
127
+ # Objective-C++ and Swift that include React headers and Fabric/TurboModule
128
+ # glue. This pod has no React Native code of any kind — pulling in React-Core,
129
+ # folly compiler flags and the new-architecture header search paths would be
130
+ # build time spent on headers nothing here includes.
131
+ s.prepare_command = <<-CMD
132
+ set -e
133
+ DEST="ios/NovvyAdsAdMob"
134
+ STAMP="$DEST/.source.version"
135
+
136
+ if [ "$(cat "$STAMP" 2>/dev/null)" != "#{ADAPTER_TAG}" ]; then
137
+ rm -rf "$DEST" ios/NovvyAdsAdMob.tmp
138
+ mkdir -p ios/NovvyAdsAdMob.tmp
139
+ curl -fsSL "#{ADAPTER_TARBALL}" | tar -xz -C ios/NovvyAdsAdMob.tmp --strip-components=1
140
+ if [ ! -d ios/NovvyAdsAdMob.tmp/Adapters/AdMob ]; then
141
+ echo "NovvyAdsReactNativeAdMob: tag #{ADAPTER_TAG} has no Adapters/AdMob" >&2
142
+ exit 1
143
+ fi
144
+ mkdir -p "$DEST"
145
+ cp ios/NovvyAdsAdMob.tmp/Adapters/AdMob/*.swift "$DEST/"
146
+ rm -rf ios/NovvyAdsAdMob.tmp
147
+ echo "#{ADAPTER_TAG}" > "$STAMP"
148
+ fi
149
+
150
+ # The pod is useless without sources. Fail loudly here rather than let
151
+ # source_files match nothing and ship an adapter-less build.
152
+ if [ -z "$(ls "$DEST"/*.swift 2>/dev/null)" ]; then
153
+ echo "NovvyAdsReactNativeAdMob: no adapter sources in $DEST — refusing to install" >&2
154
+ exit 1
155
+ fi
156
+ CMD
157
+
158
+ # Notes on the script above, in rough order of how much trouble each saves.
159
+ #
160
+ # **It runs on every `pod install`, and that is what makes this work.**
161
+ # CocoaPods runs `prepare_command` once for a pod it downloads into its cache,
162
+ # but for a local pod it runs on every install —
163
+ # `pod_source_installer.rb`: `PodSourcePreparer.new(...).prepare! if local?`.
164
+ # Every autolinked React Native package is a `:path` pod pointing into
165
+ # node_modules, so this is the supported way for one to acquire native
166
+ # sources. NovvyAdsReactNative.podspec gets NovvyAds.xcframework the same way.
167
+ #
168
+ # **One source only: the `admob-v*` tag.** The Flutter package once had a
169
+ # branch here that symlinked a sibling `novvy-ads-cocoapods` checkout whenever
170
+ # one existed. It fired on the mere presence of the directory, so the machine
171
+ # authoring the SDK was the one machine never testing what it shipped — an
172
+ # uncommitted adapter edit built fine there and did not exist for anyone else.
173
+ # This package has no such branch and no `NOVVY_ADS_LOCAL_*` escape hatch
174
+ # either, which is a deliberate asymmetry with NovvyAdsReactNative.podspec:
175
+ # that one needs its hatch because an unreleased *binary* core cannot be
176
+ # obtained any other way, whereas adapter *sources* are one `git tag` away.
177
+ # Editing the adapter means tagging first. `ADAPTER_TAG` is the contract.
178
+ #
179
+ # **Copy the files; never symlink the directory.** CocoaPods' `PathList` does
180
+ # one `Dir.glob(root + '**/*')` and matches `source_files` against that cached
181
+ # list, never touching the filesystem again (`sandbox/path_list.rb`). Ruby's
182
+ # `**` does not descend into a symlinked directory, so
183
+ # `ln -s <checkout>/Adapters/AdMob ios/NovvyAdsAdMob` makes every .swift inside
184
+ # invisible: the pattern matches zero files, the pod compiles zero adapter
185
+ # files, **the build succeeds**, and `NovvyAdMobMediationAdapter` is absent
186
+ # from the binary. AdMob cannot instantiate a class that is not there, and a
187
+ # missing custom event is a silent no-fill — no crash, no log.
188
+ #
189
+ # (NovvyAdsReactNative.podspec symlinks a directory under its local escape
190
+ # hatch and gets away with it, because `vendored_frameworks` needs only the
191
+ # top-level entry, which the glob does see. `source_files` needs the children.
192
+ # Same symlink, opposite outcome — worth knowing before copying a pattern from
193
+ # a podspec that survives it.)
194
+ #
195
+ # After changing anything here, check the binary rather than the build log:
196
+ #
197
+ # nm <app>.app/<binary> | grep '_OBJC_CLASS_$_NovvyAdMobMediationAdapter'
198
+
199
+ # A static framework even under a plain dynamic `use_frameworks!`.
200
+ #
201
+ # This pod links NovvyAdsReactNative's vendored *dynamic* NovvyAds.xcframework,
202
+ # and CocoaPods will not build a dynamic framework that links a vendored one.
203
+ # React Native's default Podfile uses static libraries and never reaches this,
204
+ # but the SDK repo's own example flips to frameworks when USE_FRAMEWORKS is
205
+ # set, so the configuration is one env var away from being real.
206
+ s.static_framework = true
207
+
208
+ s.pod_target_xcconfig = {
209
+ 'DEFINES_MODULE' => 'YES',
210
+ # NovvyAds.xcframework ships ios-arm64 and ios-arm64-simulator only.
211
+ 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386 x86_64',
212
+ }
213
+
214
+ s.user_target_xcconfig = {
215
+ 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'x86_64',
216
+
217
+ # The iOS counterpart of the Android adapter's consumer ProGuard rule, and
218
+ # just as load-bearing.
219
+ #
220
+ # AdMob instantiates `NovvyAdMobMediationAdapter` by the class-name string
221
+ # typed into the dashboard, so nothing references it at compile time and the
222
+ # linker is free to drop the whole object file. `-ObjC` forces every member
223
+ # of a linked static archive in. Without it there is no build error and no
224
+ # log line — the ad source simply never fills.
225
+ #
226
+ # ⚠️ Unlike the Flutter package, where this flag is redundant for a host
227
+ # following the README (`use_frameworks! :linkage => :static` already adds
228
+ # it), here it is **required in the default configuration**: a stock React
229
+ # Native Podfile adds no `use_frameworks!` at all and no `-ObjC`, so nothing
230
+ # else puts the flag in the app's OTHER_LDFLAGS. This is not a belt-and-
231
+ # braces duplicate. Removing it silently disables the ad source.
232
+ #
233
+ # (CocoaPods warns about setting OTHER_LDFLAGS in user_target_xcconfig. The
234
+ # warning is by far the cheaper problem.)
235
+ 'OTHER_LDFLAGS' => '$(inherited) -ObjC',
236
+ }
237
+ end
package/README.md CHANGED
@@ -1,3 +1,314 @@
1
- # Temporary Holding Version
1
+ # novvy-ads-react-native-admob
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ AdMob mediation for the [Novvy Ads React Native SDK](https://www.npmjs.com/package/novvy-ads-react-native).
4
+ Lets a publisher's existing AdMob waterfall call Novvy as a custom event.
5
+
6
+ **No JavaScript API.** Android and iOS.
7
+
8
+ ## What this package does
9
+
10
+ Nothing at runtime. It carries the native Novvy AdMob adapter into your app's
11
+ build — which is the only way an npm package can do that without you editing
12
+ `android/` and `ios/` yourself.
13
+
14
+ A mediated ad never crosses the TurboModule boundary:
15
+
16
+ ```text
17
+ JS: react-native-google-mobile-ads (BannerAd / InterstitialAd / RewardedAd)
18
+ → native AdMob SDK → waterfall
19
+ → reflectively loads NovvyAdMobMediationAdapter
20
+ → NovvySDK
21
+ ```
22
+
23
+ ## Install
24
+
25
+ ```sh
26
+ npm install novvy-ads-react-native-admob
27
+ ```
28
+
29
+ Three packages have to be present, and npm will install the two peers for you if
30
+ they are not already there:
31
+
32
+ ```jsonc
33
+ {
34
+ "dependencies": {
35
+ "novvy-ads-react-native": "1.2.1", // exact — see Versioning
36
+ "react-native-google-mobile-ads": "^16.0.3", // yours; supplies the Google Mobile Ads SDK
37
+ "novvy-ads-react-native-admob": "1.2.1-stable.0" // this package
38
+ }
39
+ }
40
+ ```
41
+
42
+ Then `cd ios && pod install`. There is nothing to import and no
43
+ `react-native.config.js` to write — autolinking finds both halves on its own.
44
+ Adding the dependency is the integration.
45
+
46
+ > `react-native-google-mobile-ads` **must be `>=15.0.0 <16.1.0`**. See
47
+ > [Requirements](#requirements); a 16.1+ host fails at `pod install`.
48
+
49
+ ### iOS `pod install` needs network access
50
+
51
+ The adapter's Swift sources are **not** in the npm tarball. The podspec's
52
+ `prepare_command` downloads them from a tag in
53
+ [`novvy-ads-cocoapods`](https://github.com/NovvyAI/novvy-ads-cocoapods) on every
54
+ `pod install`, verifies they landed, and refuses to install if they did not.
55
+
56
+ This is not an extra requirement so much as an existing one restated:
57
+ `novvy-ads-react-native`'s own podspec fetches `NovvyAds.xcframework` the same
58
+ way, so a machine that cannot reach GitHub cannot install the Novvy iOS SDK at
59
+ all, with or without this package. Shipping a second copy of the sources in the
60
+ tarball would not change that, and it would make the published tarball depend on
61
+ whatever happened to be sitting in the publisher's working directory.
62
+
63
+ ## Two things you must still do
64
+
65
+ ### 1. Initialize Novvy before any AdMob ad is requested
66
+
67
+ ```ts
68
+ import { NovvyAds } from 'novvy-ads-react-native';
69
+
70
+ await NovvyAds.initialize({
71
+ appId: 'YOUR_APP_ID',
72
+ endpoint: 'https://bid.novvy.ai',
73
+ apiKey: 'YOUR_API_KEY',
74
+ });
75
+ ```
76
+
77
+ The adapter **cannot** initialize the SDK — an app id, endpoint or api key
78
+ mistyped into a dashboard field would be a silent no-fill with no diagnostic
79
+ anywhere, so those values are deliberately not dashboard fields. Every load
80
+ re-checks initialization instead and reports a no-fill while it is missing.
81
+
82
+ Calling this once at app start, as you would for direct integration, is
83
+ sufficient; it does not need to be tied to AdMob's lifecycle.
84
+
85
+ ### 2. Configure the custom event in the AdMob dashboard
86
+
87
+ One entry per platform, because the class name is not the same on both:
88
+
89
+ | Platform | Class Name |
90
+ | --- | --- |
91
+ | Android | `ai.novvy.android.mediation.admob.NovvyAdMobMediationAdapter` |
92
+ | iOS | `NovvyAdMobMediationAdapter` |
93
+
94
+ | Field | Value |
95
+ | --- | --- |
96
+ | Parameter | `{"adUnitId":"<your Novvy ad unit id>"}` |
97
+
98
+ The adapter is loaded reflectively from that class-name string — on Android by
99
+ fully-qualified name, on iOS by the adapter's `@objc` name, which is why it is
100
+ unqualified there. Nothing else triggers it, so this step is not optional, and a
101
+ typo produces a no-fill rather than an error.
102
+
103
+ ## Callbacks come from react-native-google-mobile-ads
104
+
105
+ Lifecycle events for a Novvy ad shown through mediation arrive on the
106
+ `react-native-google-mobile-ads` listeners, **not** on `NovvyAdEvent`. That is
107
+ intended: the mediation layer owns the impression count for these ads, and a
108
+ second set of callbacks would leave you reconciling two numbers that cannot be
109
+ made to agree.
110
+
111
+ `novvy-ads-react-native`'s own listeners continue to serve direct-integration
112
+ placements (scroll / pause / insert / banner) as usual. The two paths coexist and
113
+ do not interact.
114
+
115
+ ## Supported formats
116
+
117
+ Rewarded, interstitial and banner.
118
+
119
+ Banner rotation is switched off on the mediated path: AdMob's `AdView` re-runs
120
+ the whole waterfall on its own interval and hands the adapter a fresh instance
121
+ each time, so a second rotation underneath would swap creatives the mediation
122
+ never counted an impression for.
123
+
124
+ ## User and content targeting
125
+
126
+ Set through `novvy-ads-react-native` as usual — `NovvyAds.setUserContext(...)` /
127
+ `NovvyAds.setContentContext(...)`. The values are read when a bid request is
128
+ built, so a mediated request picks up whatever is current, with no per-ad
129
+ plumbing.
130
+
131
+ One consequence worth knowing: a mediated request can be triggered by AdMob at a
132
+ moment you do not control (a banner's own refresh timer, for instance). Keep the
133
+ content context maintained as screen state — updated when the user enters a drama
134
+ or advances an episode — rather than set immediately before a load.
135
+
136
+ ## Requirements
137
+
138
+ - React Native 0.76+ with the New Architecture, Android `minSdk` 24 and iOS 13.0,
139
+ matching `novvy-ads-react-native`.
140
+ - **`react-native-google-mobile-ads` `>=15.0.0 <16.1.0`.** The iOS adapter
141
+ sources are written against the Google Mobile Ads SDK 12.x Swift API and the
142
+ pod requires `~> 12.0`, which has to intersect what
143
+ `react-native-google-mobile-ads` asks for. Unlike its Flutter counterpart, it
144
+ asks with a *bare* version string, which CocoaPods reads as `= x.y.z` — so the
145
+ intersection is a single point and the usable range is narrow:
146
+
147
+ | `react-native-google-mobile-ads` | Google-Mobile-Ads-SDK | |
148
+ | --- | --- | --- |
149
+ | 14.2.5 | 11.7.0 | no intersection |
150
+ | **15.0.0** | 12.2.0 | ok — lower bound |
151
+ | 15.8.3 | 12.11.0 | ok |
152
+ | **16.0.3** | 12.14.0 | ok — upper bound |
153
+ | 16.1.0 | 13.1.0 | no intersection |
154
+ | 16.5.0 | 13.5.0 | no intersection |
155
+
156
+ A 16.1+ host fails during `pod install` — loud, and before a build.
157
+
158
+ > ⚠️ **Do not reach for `$RNGoogleMobileAdsSDKVersion`.** That Podfile global
159
+ > overrides the GMA version `react-native-google-mobile-ads` requests, so a
160
+ > 16.1+ host can force 12.14.0 and make `pod install` succeed. It then breaks
161
+ > `react-native-google-mobile-ads`'s own iOS code, which is compiled against
162
+ > the GMA 13 API — compile errors if you are lucky, a build that links and is
163
+ > missing symbols at runtime if you are not. Supporting GMA 13 means new
164
+ > adapter sources upstream, not a wider range here.
165
+
166
+ Android is unconstrained: the adapter takes `play-services-ads` as
167
+ `compileOnly` so the host keeps ownership of that version, which is why this
168
+ package declares no Gradle dependency on it.
169
+ - Android: your app module needs
170
+ `freeCompilerArgs += ["-Xskip-metadata-version-check"]`, exactly as
171
+ `novvy-ads-react-native`'s README already requires — the Novvy native SDK is
172
+ built with Kotlin 2.2.20 while React Native 0.76 pins the Kotlin plugin to
173
+ 1.9.24. This package inherits the requirement rather than adding one; if you
174
+ have already installed `novvy-ads-react-native`, the flag is there.
175
+ - iOS: nothing to configure. The pod adds `-ObjC` to your app's link flags
176
+ itself, and needs to — a stock React Native `Podfile` uses static libraries and
177
+ adds no `-ObjC` of its own, and without it the linker is free to discard an
178
+ adapter class that nothing references at compile time. (This differs from the
179
+ Flutter package, where the recommended `use_frameworks! :linkage => :static`
180
+ already supplies the flag and the podspec's copy is redundant.) If you override
181
+ `OTHER_LDFLAGS` in your own build settings, keep `$(inherited)`.
182
+ - Expo: no config plugin is needed — this package has nothing to write into the
183
+ native projects, unlike the AdMob app id, which
184
+ `react-native-google-mobile-ads` already handles. Run `npx expo prebuild` and
185
+ build as usual. Expo Go cannot run it; nothing native can.
186
+
187
+ ## Versioning
188
+
189
+ This package's version names the `novvy-ads-react-native` version it is built
190
+ against, plus a counter for releases against that version:
191
+
192
+ | `novvy-ads-react-native` | this package | rule |
193
+ | --- | --- | --- |
194
+ | `1.2.0` (release) | `1.2.0-stable.0`, `1.2.0-stable.1`, … | append `-stable.<N>` |
195
+ | `1.2.0-beta.1` (prerelease) | `1.2.0-beta.1.0`, `1.2.0-beta.1.1`, … | append `.<N>` |
196
+
197
+ **This release is the first row**: the core it targets, `1.2.1`, is a release,
198
+ so the version is `1.2.1-stable.0`. The core spent one generation as the
199
+ prerelease `1.2.0-beta.1` — where the banner placement first landed — and
200
+ reached GA with the native SDKs split onto independent version lines: iOS
201
+ `NovvyAds` 1.2.1, Android `ai.novvy.android:sdk` 1.2.0. The banner placement
202
+ both lines carry is the same one that prerelease introduced.
203
+
204
+ The two native artifacts are versioned per platform now, and at this release
205
+ they do not agree with each other either:
206
+
207
+ | native line | value | where |
208
+ | --- | --- | --- |
209
+ | Android adapter | `ai.novvy.android:admob-adapter:1.2.0.0` | this package's `android/build.gradle` |
210
+ | iOS adapter | sources from the `admob-v1.2.1.0` tag of `novvy-ads-cocoapods` | this package's podspec |
211
+ | Android core | `1.2.0` | resolved transitively by `novvy-ads-react-native` |
212
+ | iOS core | `1.2.1` | vendored by `novvy-ads-react-native` |
213
+
214
+ The Android adapter deliberately stays on `1.2.0.0`: no `admob-adapter:1.2.1.0`
215
+ exists, and one that pinned `sdk:1.2.1` would drag a second SDK instance into
216
+ the process beside the one `novvy-ads-react-native` initializes — a permanent
217
+ silent no-fill. See the comment on that coordinate in `android/build.gradle`.
218
+
219
+ `novvy-ads-react-native` is pinned to an **exact** version in
220
+ `peerDependencies`, not a range, and you should pin this package the same way.
221
+ Each native half is built against one specific core version — the Android AAR
222
+ pins `ai.novvy.android:sdk`, and the iOS sources come from a tag that pins
223
+ `NovvyAds` — so if a range let your app resolve a different core, you would get
224
+ either an ABI mismatch or two SDK instances, and the adapter's `isInitialized`
225
+ check would be looking at the one JavaScript did not initialize. The symptom is a
226
+ permanent silent no-fill.
227
+
228
+ ### Why `stable`, and why not `1.2.0-0`
229
+
230
+ Because the revision has to sort *after* the core prereleases, and semver's
231
+ ordering rules make the obvious spellings wrong.
232
+
233
+ semver §11.4.3 ranks purely numeric prerelease identifiers **below**
234
+ alphanumeric ones. So the most natural version, `1.2.0-0`, sorts before every
235
+ beta:
236
+
237
+ ```text
238
+ 1.2.0-beta.2.0 > 1.2.0-0 ← backwards
239
+ ^1.2.0-0 → 1.2.0-beta.2.0 ← `npm update` moves a GA-core adapter back to a beta one
240
+ ```
241
+
242
+ And a beta adapter against a GA core is exactly the mismatch above. So the
243
+ revision must begin with a letter, and that letter has to put the word after
244
+ every prerelease tag the core uses:
245
+
246
+ | candidate | |
247
+ | --- | --- |
248
+ | `stable`, `rev`, `release` | sorts after `alpha` / `beta` / `rc` |
249
+ | `ga`, `final`, `r`, `dev`, `patch` | all sort **before** `rc` |
250
+
251
+ The dividing line is `rc`: `g`, `f`, `d` and `p` all precede `r`, and a bare `r`
252
+ is a prefix of `rc` and therefore smaller. The two words that feel most natural,
253
+ `ga` and `final`, are both wrong — which is why this is not a matter of taste.
254
+
255
+ Two constraints follow, and both are frozen:
256
+
257
+ 1. **`novvy-ads-react-native` prerelease tags are limited to `alpha`, `beta` and
258
+ `rc`.** `stable` also beats `snapshot`, but `test` or `zeta` would flip the
259
+ order.
260
+ 2. **`stable` cannot be changed.** Any lexically smaller word would make new
261
+ versions sort before old ones.
262
+
263
+ `scripts/check-version.js` in this package's repository enforces the
264
+ consequences on every publish. It cannot enforce the causes — see
265
+ [the plan document](https://github.com/NovvyAI/novvy-ads-react-native-mediation/blob/main/docs/plan/admob-mediation.md).
266
+
267
+ ### Two things this buys, and two it costs
268
+
269
+ The scheme is self-describing: you can read the core version straight out of the
270
+ package version. And because every version of this package is a prerelease, a
271
+ `^` range is **structurally** confined to one core — `^1.2.0-beta.1.0` resolves
272
+ only within `1.2.0-*` and cannot reach `1.3.0-beta.1.0` or `1.3.0-stable.0`,
273
+ where a plain `^1.2.0` on an ordinary package would climb to `1.3.0` without
274
+ asking. That is a stronger guarantee than `peerDependencies`, which reports the
275
+ conflict after the fact.
276
+
277
+ One thing that range *does* reach is the GA-core adapter for the same core:
278
+ `^1.2.0-beta.1.0` matches `1.2.0-stable.0`, because both are prereleases of
279
+ `1.2.0`. So `npm update` will carry you from a beta-core adapter to a GA-core
280
+ one, while your `novvy-ads-react-native` is still pinned to `1.2.0-beta.1` —
281
+ and npm will refuse the install on the peer conflict. That is the intended
282
+ outcome, not a bug: the two must be upgraded together, and this is the loud
283
+ version of finding out.
284
+
285
+ What it costs:
286
+
287
+ - **`npm install novvy-ads-react-native-admob` picks a prerelease.** Because
288
+ there is no non-prerelease version to fall back on, a bare install resolves
289
+ through the `latest` dist-tag like any other package — but what `latest` points
290
+ at here is always a prerelease, and a version range you write by hand will only
291
+ match one if it says so. `^1.2.1` matches nothing at all; `^1.2.1-stable.0` is
292
+ the form to write, and the `npm install` above produces it for you.
293
+ - **The counter at the end of the version is not the native adapter revision.**
294
+ It counts releases of *this npm package* against one core, so a
295
+ documentation-only release increments it while the native artifacts stand
296
+ still. The native revision lives in the exported constants —
297
+ `NOVVY_ADMOB_ADAPTER_VERSION_ANDROID` and
298
+ `NOVVY_ADMOB_ADAPTER_VERSION_IOS` — which is the only place it can be read;
299
+ `NOVVY_ADS_CORE_VERSION_ANDROID` / `_IOS` give the core each half was
300
+ compiled against. There are two of each because the native lines are
301
+ per-platform now: at this release the npm version is `1.2.1-stable.0` while
302
+ the native artifacts read `1.2.0.0` (Android) and `admob-v1.2.1.0` (iOS) —
303
+ no two of the three lines spell the same string, which is the point. All
304
+ four constants are diagnostics — nothing reads them at runtime, and you
305
+ never need to import this package.
306
+ - **Renovate and Dependabot ignore prerelease versions by default**, so you will
307
+ not get automatic upgrade PRs for this package. That is the intended behaviour
308
+ — it should move when you move `novvy-ads-react-native`, not on a bot's
309
+ schedule — but it means "no PRs" is not evidence that the package is
310
+ unmaintained.
311
+
312
+ ## License
313
+
314
+ MIT — see [LICENSE](./LICENSE).
@@ -0,0 +1,158 @@
1
+ // Android half of novvy-ads-react-native-admob.
2
+ //
3
+ // This module compiles to an .aar containing exactly one class: an empty
4
+ // ReactPackage (see NovvyAdsAdMobPackage.kt). The actual payload is the single
5
+ // `implementation` line at the bottom of this file. That is the whole point of
6
+ // the module — RN's Gradle autolinking turns this directory into a Gradle
7
+ // subproject of the host app, which is the only way an npm package can add an
8
+ // Android dependency without the publisher editing their own `android/` files.
9
+
10
+ buildscript {
11
+ ext.getExtOrDefault = { name ->
12
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties["NovvyAdsAdMob_" + name]
13
+ }
14
+
15
+ repositories {
16
+ google()
17
+ mavenCentral()
18
+ }
19
+
20
+ dependencies {
21
+ classpath "com.android.tools.build:gradle:8.6.0"
22
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
23
+ }
24
+ }
25
+
26
+ apply plugin: "com.android.library"
27
+ apply plugin: "kotlin-android"
28
+
29
+ // Deliberately NOT applied: `com.facebook.react`.
30
+ //
31
+ // The SDK repo's android/build.gradle applies it under `isNewArchitectureEnabled()`
32
+ // because it has a TurboModule spec to run codegen over. This module has no
33
+ // spec, no generated/java, no generated/jni — it never crosses the bridge. See
34
+ // the class doc in NovvyAdsAdMobPackage.kt for why that is structural rather
35
+ // than a shortcut.
36
+
37
+ def getExtOrIntegerDefault = { name ->
38
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["NovvyAdsAdMob_" + name]).toInteger()
39
+ }
40
+
41
+ android {
42
+ namespace "ai.novvy.reactnative.admob"
43
+
44
+ compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
45
+
46
+ defaultConfig {
47
+ minSdkVersion getExtOrIntegerDefault("minSdkVersion")
48
+ targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
49
+ }
50
+
51
+ compileOptions {
52
+ sourceCompatibility JavaVersion.VERSION_17
53
+ targetCompatibility JavaVersion.VERSION_17
54
+ }
55
+
56
+ kotlinOptions {
57
+ jvmTarget = "17"
58
+ // React Native 0.76 pins the Kotlin Gradle plugin to 1.9.24, and a 1.9
59
+ // compiler reads class metadata only up to version 2.0.0. Anything on the
60
+ // compile classpath built with Kotlin 2.2.20 (metadata 2.2.0) is therefore
61
+ // unreadable without this flag.
62
+ //
63
+ // It is tempting to think this module escapes that, since it references no
64
+ // Novvy symbol — the adapter AAR is named as a coordinate and never
65
+ // imported. That reasoning is wrong, and the POM says why:
66
+ //
67
+ // ai.novvy.android:admob-adapter:1.2.0.0
68
+ // -> org.jetbrains.kotlin:kotlin-stdlib:2.2.20 scope=compile
69
+ //
70
+ // Maven `compile` scope maps to Gradle's api variant, so kotlin-stdlib
71
+ // 2.2.20 lands on THIS module's compile classpath transitively, and
72
+ // conflict resolution picks it over the 1.9.24 stdlib that KGP contributes.
73
+ // The trigger is not a Novvy type but the standard library, which every
74
+ // Kotlin file reads — the SDK repo's README records the same failure in a
75
+ // host app that touches no Novvy code either:
76
+ //
77
+ // e: MainApplication.kt: Class 'kotlin.Unit' was compiled with an
78
+ // incompatible version of Kotlin.
79
+ //
80
+ // Raising the Kotlin version instead is not an option on 0.76: KGP >= 2.1
81
+ // turns KotlinTopLevelExtension from a class into an interface, which the
82
+ // React Native Gradle plugin cannot load.
83
+ //
84
+ // Not yet confirmed by a Gradle run — gate B needs novvy-ads-react-native
85
+ // published first. The flag is added ahead of that because the asymmetry is
86
+ // one-sided: unnecessary here it costs nothing, missing here it fails every
87
+ // consumer's build.
88
+ freeCompilerArgs += ["-Xskip-metadata-version-check"]
89
+ }
90
+ }
91
+
92
+ repositories {
93
+ google()
94
+ mavenCentral()
95
+ }
96
+
97
+ dependencies {
98
+ // The entire reason this package exists.
99
+ //
100
+ // A dependency-graph island: nothing in this module references it, and nothing
101
+ // can. The Google Mobile Ads SDK instantiates
102
+ // `ai.novvy.android.mediation.admob.NovvyAdMobMediationAdapter` reflectively
103
+ // from the class-name string the publisher types into the AdMob dashboard, so
104
+ // there is no compile-time reference anywhere in the app to keep it alive.
105
+ //
106
+ // What keeps R8 from deleting it is the AAR's own `consumer-rules.pro`, which
107
+ // travels with the artifact and keeps the adapter plus its three loaders with
108
+ // `{ *; }`. Nothing here needs to restate those rules — and if that file ever
109
+ // stopped shipping, the release build would still succeed and the class would
110
+ // simply be gone. See Step 5.2 of the plan: the dex grep is the only automated
111
+ // check that the payload survived, and it is the first thing to run.
112
+ //
113
+ // Pinned exactly, never a dynamic version (`1.2.0.+`): the same source
114
+ // would otherwise build different apks on different days, and the adapter must
115
+ // match the core SDK the app initializes — a mismatched pair fails as a
116
+ // permanent silent no-fill, because the adapter's isInitialized check reads
117
+ // the SDK instance JS never initialized.
118
+ //
119
+ // `1.2.0.0` = core `1.2.0` + adapter revision `0`. This is the native
120
+ // version line and it is NOT this npm package's own version — see the
121
+ // "two version lines" table in §四 of the plan, and the per-platform
122
+ // constants in index.js.
123
+ //
124
+ // Deliberately `1.2.0.0`, not `1.2.1.0`, at this release: the native SDKs
125
+ // release on independent lines now, and the 1.2.1 generation moved only the
126
+ // iOS one. `novvy-ads-react-native` 1.2.1 itself resolves
127
+ // `ai.novvy.android:sdk:1.2.0`, and no `admob-adapter:1.2.1.0` exists — an
128
+ // adapter that pinned `sdk:1.2.1` would drag a second NovvySDK instance into
129
+ // the process beside the one the core package initializes, and the
130
+ // adapter's isInitialized check would read the wrong one. The iOS half of
131
+ // this package does move to the `admob-v1.2.1.0` tag this release — the
132
+ // asymmetry is deliberate, and mirrors the Flutter package's coordinates.
133
+ implementation "ai.novvy.android:admob-adapter:1.2.0.0"
134
+
135
+ // Only to compile the empty ReactPackage in NovvyAdsAdMobPackage.kt, which
136
+ // exists because RN 0.76's autolinking requires a non-null `packageInstance`.
137
+ //
138
+ // compileOnly, not implementation: React Native is supplied by the host app at
139
+ // runtime by definition, and this module has no runtime behaviour of its own.
140
+ // No version string — RNGP substitutes the host's React Native version across
141
+ // all configurations.
142
+ compileOnly "com.facebook.react:react-android"
143
+
144
+ // Deliberately NOT declared: com.google.android.gms:play-services-ads.
145
+ //
146
+ // The adapter compiles against it as `compileOnly` (verified:
147
+ // NovvyAdMobAdapter/build.gradle.kts:74 pins 23.0.0 there) precisely so the
148
+ // Google Mobile Ads version stays the host's to choose. On React Native the
149
+ // host's choice is made transitively by `react-native-google-mobile-ads`,
150
+ // whose 15.x/16.0.x line brings GMA Android 24.1.0–24.9.0. Declaring any
151
+ // version here would contend with what is already in the host's graph.
152
+ //
153
+ // ⚠️ compileOnly 23.0.0 against a 24.x runtime has no compile-time guarantee
154
+ // of compatibility, only Google's own API stability. This is the plan's
155
+ // largest unverified assumption (§九 #1) and can only be settled on a real
156
+ // device. If it does break, the fix is to raise the compileOnly floor in
157
+ // novvy-ads-android-sdk — not to add a constraint here.
158
+ }
@@ -0,0 +1,19 @@
1
+ # Defaults for this module's Android build, read by android/build.gradle through
2
+ # `getExtOrDefault` / `getExtOrIntegerDefault`. A host app overrides any of them
3
+ # by setting the un-prefixed name in its root project's ext block (the usual
4
+ # `ext { compileSdkVersion = 35 }` in android/build.gradle), which is why the
5
+ # prefix is here: it keeps this module's fallbacks from colliding with the SDK
6
+ # package's `NovvyAds_` ones when both are autolinked into the same build.
7
+ #
8
+ # Values track novvy-ads-react-native's android/gradle.properties. They differ
9
+ # from the Flutter mediation package (compileSdk 34, no targetSdk) because that
10
+ # one is not built against React Native.
11
+
12
+ # React Native 0.76 pins the Kotlin Gradle plugin to 1.9.24 in
13
+ # @react-native/gradle-plugin's version catalog. Raising this is not a free
14
+ # choice: KGP >= 2.1 turns KotlinTopLevelExtension from a class into an
15
+ # interface, which the React Native Gradle plugin cannot load.
16
+ NovvyAdsAdMob_kotlinVersion=1.9.24
17
+ NovvyAdsAdMob_minSdkVersion=24
18
+ NovvyAdsAdMob_targetSdkVersion=34
19
+ NovvyAdsAdMob_compileSdkVersion=35
@@ -0,0 +1,19 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ <!-- Intentionally empty.
3
+
4
+ No permissions are declared here. The ones the ad stack needs (INTERNET,
5
+ ACCESS_NETWORK_STATE, com.google.android.gms.permission.AD_ID) come from
6
+ the manifests of the artifacts that actually use them and are merged into
7
+ the host app automatically: ai.novvy.android:sdk, pulled in by the host's
8
+ novvy-ads-react-native dependency, and play-services-ads, pulled in by
9
+ react-native-google-mobile-ads.
10
+
11
+ Nor is there an AdMob application-id meta-data entry. That belongs to the
12
+ host app and react-native-google-mobile-ads already owns the mechanism for
13
+ it (its `react-native-google-mobile-ads` key in app.json / Expo config
14
+ plugin). Declaring it here would either be ignored or, worse, conflict at
15
+ merge time with the publisher's real one.
16
+
17
+ An AndroidManifest.xml is still required: com.android.library refuses to
18
+ configure without one. -->
19
+ </manifest>
@@ -0,0 +1,101 @@
1
+ package ai.novvy.reactnative.admob
2
+
3
+ import com.facebook.react.ReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.uimanager.ViewManager
7
+
8
+ /**
9
+ * An intentionally empty [ReactPackage]. It registers no native modules and no
10
+ * view managers, and it never will.
11
+ *
12
+ * ## Why an empty class has to exist at all
13
+ *
14
+ * This module's only payload is the `implementation` line in `build.gradle`
15
+ * that pulls in the AdMob mediation adapter AAR. There is no JavaScript API
16
+ * here, nothing crosses the bridge, and so there is nothing for a
17
+ * [ReactPackage] to contain. React Native 0.76's Gradle autolinking still
18
+ * demands one:
19
+ *
20
+ * - `ModelAutolinkingDependenciesPlatformAndroidJson.kt:13` declares
21
+ * `val packageInstance: String` — a **non-null** type.
22
+ * - `GeneratePackageListTask.kt:84` then does
23
+ * `requireNotNull(dep.packageInstance) { "RNGP - Autolinking: Missing `packageInstance` in `config` for dependency $name..." }`.
24
+ *
25
+ * So the `packageInstance: null` trick that the Flutter mediation package's
26
+ * README suggests for React Native does not work here — it fails the Android
27
+ * build outright. The only escape hatch in `filterAndroidPackages()` is
28
+ * `isPureCxxDependency == true`, which is semantically wrong (it is for pure
29
+ * C++ TurboModules and also changes the new-architecture CMake generation
30
+ * path), so it is not used.
31
+ *
32
+ * Worth stating plainly, because it is the opposite of the Flutter situation:
33
+ * omitting the registration class on Flutter makes the toolchain **silently
34
+ * ignore the whole Android directory** — the payload vanishes and the build
35
+ * succeeds. On React Native 0.76 it is a **loud Gradle error with a precise
36
+ * message**. React Native is better here. The class is still required.
37
+ *
38
+ * ## No `react-native.config.js` is needed — but mind the class declaration
39
+ *
40
+ * The CLI finds this class by scanning source, so autolinking works with no
41
+ * config file at all (verified against
42
+ * `@react-native-community/cli-platform-android/build/config`):
43
+ *
44
+ * - `findPackageClassName.js` globs `**\/*Package.kt` under `android/`, reads each
45
+ * match, and applies
46
+ * `/class\s+(\w+[^(\s]*)[\s\w():]*(\s+implements\s+|:)[\s\w():,]*[^{]*ReactPackage/`.
47
+ * - `getAndroidProject.js` takes the package name from this manifest's `package`
48
+ * attribute, and — since AGP 8 manifests have none — falls back to
49
+ * `parseNamespaceFromBuildGradleFile`, which reads `namespace` out of
50
+ * `android/build.gradle`.
51
+ * - `config/index.js:135` then emits
52
+ * `import ai.novvy.reactnative.admob.NovvyAdsAdMobPackage;` and
53
+ * `new NovvyAdsAdMobPackage()`.
54
+ *
55
+ * ⚠️ That regex runs over the **whole file, comments included**, and `String.match`
56
+ * returns the first hit. So a doc line that reads like a declaration — an
57
+ * illustrative `class <SomeName> : ReactPackage` written out in prose, say —
58
+ * gets picked up ahead of the real class, and autolinking then emits
59
+ * `new <SomeName>()`: a Gradle error at best, the wrong class at worst.
60
+ *
61
+ * Note the angle brackets above: they are what keeps that example from matching
62
+ * (the pattern needs `\w` straight after `class`). This was not hypothetical —
63
+ * the first draft of this very paragraph spelled the example out literally and
64
+ * made the CLI report the placeholder name instead of `NovvyAdsAdMobPackage`.
65
+ * After editing this doc, re-check with:
66
+ *
67
+ * ```
68
+ * node -e "const {matchClassName}=require('<app>/node_modules/@react-native-community/cli-platform-android/build/config/findPackageClassName.js');
69
+ * console.log(matchClassName(require('fs').readFileSync('<this file>','utf8'))[1])"
70
+ * ```
71
+ *
72
+ * ## Do not give it work to do
73
+ *
74
+ * The temptation, once a registration class exists, is to hang something off
75
+ * it — an initializer, a version getter, a convenience module. Don't.
76
+ *
77
+ * Anything that needs JavaScript belongs in `novvy-ads-react-native`, whose
78
+ * TurboModule is already established, already versioned, and already the thing
79
+ * publishers depend on. A second bridge surface shipped from a mediation
80
+ * package would be a second place to keep in sync, on a package whose whole
81
+ * design is that installing it is the entire integration.
82
+ *
83
+ * Initialization in particular must stay out of here. `NovvyAds.initialize(...)`
84
+ * is the publisher's call to make from JavaScript, deliberately: the adapter
85
+ * cannot initialize the SDK on their behalf because the app id / endpoint /
86
+ * apiKey would then come from the AdMob dashboard's parameter field, where a
87
+ * typo produces a silent no-fill with no diagnostic at all.
88
+ */
89
+ class NovvyAdsAdMobPackage : ReactPackage {
90
+ override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> =
91
+ emptyList()
92
+
93
+ // `ReactPackage.createViewManagers` is declared in Java as a raw
94
+ // `List<ViewManager>` (ReactPackage.java:53), which Kotlin sees as
95
+ // `List<ViewManager<*, *>>`. Spelling the type arguments out any further —
96
+ // `ViewManager<out View, out ReactShadowNode<*>>`, say — produces a different
97
+ // type and fails to override.
98
+ override fun createViewManagers(
99
+ reactContext: ReactApplicationContext
100
+ ): List<ViewManager<*, *>> = emptyList()
101
+ }
package/index.d.ts ADDED
@@ -0,0 +1,47 @@
1
+ // Hand-written to match index.js. There is no codegen and no `tsc` step in this
2
+ // package — see the note at the top of index.js.
3
+
4
+ /**
5
+ * Version of the native AdMob mediation adapter this package delivers on
6
+ * Android: the Maven coordinate `ai.novvy.android:admob-adapter:<this>`.
7
+ *
8
+ * ⚠️ **This is not the version of this npm package.** The npm version
9
+ * (`1.2.1-stable.0`) tracks `novvy-ads-react-native`; the native line tracks
10
+ * the native core plus an adapter revision — and it is per-platform since the
11
+ * core's 1.2.1 generation, so this constant and
12
+ * {@link NOVVY_ADMOB_ADAPTER_VERSION_IOS} are expected to disagree. See
13
+ * index.js for the table.
14
+ *
15
+ * Diagnostics only; nothing reads it at runtime.
16
+ */
17
+ export declare const NOVVY_ADMOB_ADAPTER_VERSION_ANDROID: string;
18
+
19
+ /**
20
+ * Version of the native AdMob mediation adapter this package delivers on iOS:
21
+ * the `admob-v<this>` tag of `novvy-ads-cocoapods` the Swift sources come
22
+ * from. `<NovvyAds iOS version>.<adapter revision>`.
23
+ *
24
+ * Diagnostics only; nothing reads it at runtime.
25
+ */
26
+ export declare const NOVVY_ADMOB_ADAPTER_VERSION_IOS: string;
27
+
28
+ /**
29
+ * Version of the native Novvy core SDK the Android adapter was compiled
30
+ * against: `ai.novvy.android:sdk:<this>`.
31
+ *
32
+ * Pinned exactly on purpose — a range risks two SDK instances in the process,
33
+ * which is a permanent silent no-fill. Also not this package's `version`.
34
+ *
35
+ * Diagnostics only; nothing reads it at runtime.
36
+ */
37
+ export declare const NOVVY_ADS_CORE_VERSION_ANDROID: string;
38
+
39
+ /**
40
+ * Version of the native Novvy core SDK the iOS adapter was compiled against:
41
+ * the `NovvyAds.xcframework` that `novvy-ads-react-native` vendors. Differs
42
+ * from the Android core because the two native SDKs release on their own
43
+ * lines.
44
+ *
45
+ * Diagnostics only; nothing reads it at runtime.
46
+ */
47
+ export declare const NOVVY_ADS_CORE_VERSION_IOS: string;
package/index.js ADDED
@@ -0,0 +1,97 @@
1
+ 'use strict';
2
+
3
+ // The whole JavaScript surface of this package: two strings, for diagnostics.
4
+ //
5
+ // There is no API here and there is not going to be one. The payload is native
6
+ // and arrives through the build system — `android/build.gradle`'s single
7
+ // `implementation` line and `NovvyAdsReactNativeAdMob.podspec`'s
8
+ // `prepare_command`. Adding this package to `dependencies` is the entire
9
+ // integration; nothing needs to be imported for the ad source to work.
10
+ //
11
+ // Written by hand as CommonJS, with a hand-written `index.d.ts` beside it. No
12
+ // bob, no tsc, no `lib/` — a build pipeline for two constants would cost more
13
+ // to maintain than it could ever save, and it would put a `prepack` step
14
+ // between an author and a correct tarball.
15
+
16
+ /**
17
+ * Version of the native AdMob mediation adapter this package delivers on
18
+ * Android: the Maven coordinate `ai.novvy.android:admob-adapter:<this>`.
19
+ *
20
+ * ⚠️ **This is not the version of this npm package.** Three version lines meet
21
+ * in this repository and it is a mistake to reconcile them:
22
+ *
23
+ * | Line | Tracks | Appears in |
24
+ * | --- | --- | --- |
25
+ * | npm | `novvy-ads-react-native@1.2.1` | `package.json`'s `version` (`1.2.1-stable.0`) and its peer pin |
26
+ * | native Android | core `1.2.0` + adapter revision `0` | the Gradle coordinate, and this constant |
27
+ * | native iOS | core `1.2.1` + adapter revision `0` | the podspec's tag, and {@link NOVVY_ADMOB_ADAPTER_VERSION_IOS} |
28
+ *
29
+ * There used to be one native line and one constant. They split when the two
30
+ * native SDKs moved onto independent release lines — `novvy-ads-react-native`
31
+ * 1.2.1 vendors NovvyAds iOS 1.2.1 while resolving `ai.novvy.android:sdk:1.2.0`
32
+ * — and each adapter generation now follows its own platform's core. The two
33
+ * constants below are therefore expected to disagree routinely: they are two
34
+ * facts, not a value and its typo. See the comment on the Gradle coordinate in
35
+ * `android/build.gradle` for why Android stays on 1.2.0 this generation.
36
+ *
37
+ * Nothing reads this at runtime. It exists so a version can be pasted into a
38
+ * bug report or logged from a host app, which is the only way to find out which
39
+ * native adapter build that app actually contains — `package.json`'s version
40
+ * cannot tell you.
41
+ *
42
+ * @type {string}
43
+ */
44
+ const NOVVY_ADMOB_ADAPTER_VERSION_ANDROID = '1.2.0.0';
45
+
46
+ /**
47
+ * Version of the native AdMob mediation adapter this package delivers on iOS:
48
+ * the adapter's Swift sources are fetched from the `admob-v<this>` tag of
49
+ * `novvy-ads-cocoapods` at `pod install` and compiled into the pod.
50
+ *
51
+ * `<NovvyAds iOS version>.<adapter revision>` — `1.2.1.0` here, because the
52
+ * iOS core moved to 1.2.1 this generation. Not this npm package's version; see
53
+ * the table on {@link NOVVY_ADMOB_ADAPTER_VERSION_ANDROID}.
54
+ *
55
+ * The adapter sources are byte-identical to the previous generation's — the
56
+ * tag moved because every NovvyAds release needs a matching adapter release
57
+ * even when nothing in the adapter changed, not because the adapter did.
58
+ *
59
+ * @type {string}
60
+ */
61
+ const NOVVY_ADMOB_ADAPTER_VERSION_IOS = '1.2.1.0';
62
+
63
+ /**
64
+ * Version of the native Novvy core SDK the Android adapter was compiled
65
+ * against: `ai.novvy.android:sdk:<this>`.
66
+ *
67
+ * Also not this npm package's `version` — see the table on
68
+ * {@link NOVVY_ADMOB_ADAPTER_VERSION_ANDROID}.
69
+ *
70
+ * The core is pinned exactly rather than to a range on purpose. Each platform
71
+ * compiles the adapter against one core, and a range risks either an ABI
72
+ * mismatch or two SDK instances in the process — in the second case the
73
+ * adapter's `isInitialized` check reads the instance JavaScript never
74
+ * initialized, and the ad source returns no fill forever, silently.
75
+ *
76
+ * @type {string}
77
+ */
78
+ const NOVVY_ADS_CORE_VERSION_ANDROID = '1.2.0';
79
+
80
+ /**
81
+ * Version of the native Novvy core SDK the iOS adapter was compiled against:
82
+ * the `NovvyAds.xcframework` that `novvy-ads-react-native` vendors.
83
+ *
84
+ * Differs from {@link NOVVY_ADS_CORE_VERSION_ANDROID} because the two native
85
+ * SDKs release on their own lines — see the table on
86
+ * {@link NOVVY_ADMOB_ADAPTER_VERSION_ANDROID}. Not this package's `version`.
87
+ *
88
+ * @type {string}
89
+ */
90
+ const NOVVY_ADS_CORE_VERSION_IOS = '1.2.1';
91
+
92
+ module.exports = {
93
+ NOVVY_ADMOB_ADAPTER_VERSION_ANDROID,
94
+ NOVVY_ADMOB_ADAPTER_VERSION_IOS,
95
+ NOVVY_ADS_CORE_VERSION_ANDROID,
96
+ NOVVY_ADS_CORE_VERSION_IOS,
97
+ };
package/package.json CHANGED
@@ -1,6 +1,52 @@
1
1
  {
2
2
  "name": "novvy-ads-react-native-admob",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.2.1-stable.0",
4
+ "description": "AdMob mediation adapter for the Novvy Ads React Native SDK — lets an existing AdMob waterfall call Novvy as a custom event. No JavaScript API: installing the package is the whole integration.",
5
+ "main": "index.js",
6
+ "types": "index.d.ts",
7
+ "files": [
8
+ "index.js",
9
+ "index.d.ts",
10
+ "android",
11
+ "ios",
12
+ "NovvyAdsReactNativeAdMob.podspec",
13
+ "README.md",
14
+ "CHANGELOG.md",
15
+ "LICENSE",
16
+ "!android/build",
17
+ "!android/.gradle",
18
+ "!ios/NovvyAdsAdMob"
19
+ ],
20
+ "scripts": {
21
+ "check-version": "node scripts/check-version.js",
22
+ "prepublishOnly": "node scripts/check-version.js"
23
+ },
24
+ "keywords": [
25
+ "react-native",
26
+ "admob",
27
+ "mediation",
28
+ "custom-event",
29
+ "ads",
30
+ "novvy",
31
+ "short-drama",
32
+ "rewarded",
33
+ "interstitial",
34
+ "android",
35
+ "ios"
36
+ ],
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "git+https://github.com/NovvyAI/novvy-ads-react-native-mediation.git",
40
+ "directory": "novvy-ads-admob"
41
+ },
42
+ "author": "NovvyAI <dev@novvy.ai>",
43
+ "license": "MIT",
44
+ "homepage": "https://github.com/NovvyAI/novvy-ads-react-native-mediation/tree/main/novvy-ads-admob#readme",
45
+ "peerDependencies": {
46
+ "novvy-ads-react-native": "1.2.1",
47
+ "react-native-google-mobile-ads": ">=15.0.0 <16.1.0"
48
+ },
49
+ "devDependencies": {
50
+ "semver": "^7.6.3"
51
+ }
52
+ }