@maccesar/titools 5.1.1 → 5.2.1
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/CONTRIBUTING.md +69 -0
- package/README.md +112 -72
- package/agents/ti-pro.md +5 -1
- package/commands/ti-audit.md +5 -1
- package/docs/examples.md +91 -0
- package/docs/knowledge-index.md +57 -0
- package/lib/config.js +2 -0
- package/lib/downloader.js +1 -1
- package/package.json +4 -2
- package/skills/purgetss/references/multi-density-images.md +1 -1
- package/skills/ti-admob-unified/SKILL.md +58 -0
- package/skills/ti-admob-unified/agents/openai.yaml +4 -0
- package/skills/ti-admob-unified/assets/example/app.js +1207 -0
- package/skills/ti-admob-unified/assets/example/lib/ads.js +646 -0
- package/skills/ti-admob-unified/assets/example/library-usage.js +136 -0
- package/skills/ti-admob-unified/references/api-reference.md +615 -0
- package/skills/ti-admob-unified/references/banners.md +435 -0
- package/skills/ti-admob-unified/references/contributing.md +233 -0
- package/skills/ti-admob-unified/references/dependencies.md +91 -0
- package/skills/ti-admob-unified/references/fullscreen.md +238 -0
- package/skills/ti-admob-unified/references/game-monetization.md +148 -0
- package/skills/ti-admob-unified/references/getting-started.md +319 -0
- package/skills/ti-admob-unified/references/index.md +33 -0
- package/skills/ti-admob-unified/references/integration.md +87 -0
- package/skills/ti-admob-unified/references/library.md +220 -0
- package/skills/ti-admob-unified/references/mediation-parity.md +17 -0
- package/skills/ti-admob-unified/references/mediation.md +145 -0
- package/skills/ti-admob-unified/references/migration.md +259 -0
- package/skills/ti-admob-unified/references/module-changelog.md +84 -0
- package/skills/ti-admob-unified/references/module-license.txt +21 -0
- package/skills/ti-admob-unified/references/privacy.md +281 -0
- package/skills/ti-admob-unified/references/production.md +67 -0
- package/skills/ti-admob-unified/references/rewarded-interstitial.md +101 -0
- package/skills/ti-admob-unified/references/roadmap.md +143 -0
- package/skills/ti-admob-unified/references/source-notes.md +80 -0
- package/skills/ti-admob-unified/references/source-snapshot.json +644 -0
- package/skills/ti-admob-unified/references/third-party-notices.md +22 -0
- package/skills/ti-game/SKILL.md +31 -5
- package/skills/ti-game/references/api.md +36 -24
- package/skills/ti-game/references/debugging-performance.md +22 -7
- package/skills/ti-game/references/examples.md +113 -20
- package/skills/ti-game/references/input.md +101 -0
- package/skills/ti-game/references/patterns.md +56 -10
- package/skills/ti-game/references/physics.md +530 -0
- package/skills/ti-game/references/project-setup.md +131 -47
- package/skills/ti-game/references/recipes-action.md +149 -18
- package/skills/ti-game/references/recipes-adventure-effects.md +47 -4
- package/skills/ti-game/references/roadmap.md +11 -4
- package/skills/ti-game/references/source-notes.md +81 -0
- package/skills/ti-game/references/tilemaps.md +13 -4
- package/skills/ti-game-assets/SKILL.md +53 -0
- package/skills/ti-game-assets/agents/openai.yaml +4 -0
- package/skills/ti-game-assets/references/alpha.md +26 -0
- package/skills/ti-game-assets/references/art-direction.md +37 -0
- package/skills/ti-game-assets/references/integration.md +49 -0
- package/skills/ti-game-assets/references/manifest.example.json +429 -0
- package/skills/ti-game-assets/references/manifest.md +39 -0
- package/skills/ti-game-assets/references/offline-audio.md +33 -0
- package/skills/ti-game-assets/references/prompts.md +33 -0
- package/skills/ti-game-assets/references/repeating-textures.md +17 -0
- package/skills/ti-game-assets/references/responsible-ads.md +31 -0
- package/skills/ti-game-assets/references/sound-assets.md +42 -0
- package/skills/ti-game-assets/references/vector-sources.md +21 -0
- package/skills/ti-game-assets/scripts/alpha_tools.py +160 -0
- package/skills/ti-game-assets/scripts/requirements.txt +1 -0
- package/skills/ti-game-assets/scripts/validate_manifest.py +267 -0
- package/skills/ti-synthengine/SKILL.md +77 -67
- package/skills/ti-synthengine/assets/example/README.md +84 -0
- package/skills/ti-synthengine/assets/example/app.js +1178 -0
- package/skills/ti-synthengine/assets/example/lib/sequenceOwner.js +83 -0
- package/skills/ti-synthengine/assets/example/lib/sequenceScore.js +25 -0
- package/skills/ti-synthengine/references/advanced-examples.md +174 -0
- package/skills/ti-synthengine/references/android-sim80-correction-evidence.zip +0 -0
- package/skills/ti-synthengine/references/android-sim80-correction.json +162 -0
- package/skills/ti-synthengine/references/api.md +573 -153
- package/skills/ti-synthengine/references/audio-engine.md +272 -124
- package/skills/ti-synthengine/references/bypass-physical-recheck-evidence.zip +0 -0
- package/skills/ti-synthengine/references/examples.md +355 -106
- package/skills/ti-synthengine/references/frontier-android-evidence.zip +0 -0
- package/skills/ti-synthengine/references/frontier-ipad-evidence.zip +0 -0
- package/skills/ti-synthengine/references/frontier-physical-validation.json +1918 -0
- package/skills/ti-synthengine/references/guide.md +426 -75
- package/skills/ti-synthengine/references/performance-review-evidence.zip +0 -0
- package/skills/ti-synthengine/references/performance-review.json +1146 -0
- package/skills/ti-synthengine/references/physical-validation.json +1457 -0
- package/skills/ti-synthengine/references/recipes-interaction.md +549 -0
- package/skills/ti-synthengine/references/recipes.md +434 -420
- package/skills/ti-synthengine/references/recommendations.md +72 -85
- package/skills/ti-synthengine/references/sound-design.md +375 -155
- package/skills/ti-synthengine/references/source-notes.md +109 -0
- package/skills/ti-synthengine/references/source-snapshot.json +452 -0
- package/skills/ti-synthengine/references/stats-performance.json +2992 -0
- package/skills/ti-synthengine/references/stats-performance.md +82 -0
- package/skills/ti-synthengine/references/studio-validation.json +6202 -0
- package/skills/ti-synthengine/references/troubleshooting.md +123 -78
- package/skills/ti-synthengine/references/unified-bypass-performance.json +22425 -0
- package/skills/ti-synthengine/references/unified-validation.json +314 -0
- package/AGENTS-VERCEL-RESEARCH.md +0 -175
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
> Source snapshot: official ti.admob.unified 1.0.0 documentation at release source commit `034843299b2984253988e0ffccb9f6a1c91682c9` (copied 2026-10-24). Relative links rebased for this skill.
|
|
2
|
+
|
|
3
|
+
# Privacy and initialization
|
|
4
|
+
|
|
5
|
+
`ti.admob.unified` exposes Google UMP as an event-based workflow. Callback properties are not part of the contract.
|
|
6
|
+
|
|
7
|
+
## Required order
|
|
8
|
+
|
|
9
|
+
Use this order on every app launch:
|
|
10
|
+
|
|
11
|
+
1. Register module listeners.
|
|
12
|
+
2. Call `requestConsent({ ... })`.
|
|
13
|
+
3. Wait for the terminal `consent_complete` event.
|
|
14
|
+
4. Check `event.canRequestAds` or `canRequestAds()`.
|
|
15
|
+
5. Call `initialize({ ... })` once.
|
|
16
|
+
6. Create and load ads only after initialization succeeds.
|
|
17
|
+
|
|
18
|
+
App Tracking Transparency is optional and separate from UMP. `canRequestAds()` remains the only ad-request gate. If the application uses tracking, resolve ATT before `initialize()`; otherwise skip it. See [Optional: if the application requests ATT](getting-started.md#optional-if-the-application-requests-att).
|
|
19
|
+
|
|
20
|
+
Check UMP even when the consent operation reports an error. Consent from a previous session may still allow requests; `canRequestAds` determines whether the application can continue.
|
|
21
|
+
|
|
22
|
+
Google's UMP guidance also requires a consent-information update every app session and warns against deriving status from cached consent strings. See [UMP for Android](https://developers.google.com/admob/android/privacy) and [UMP for iOS](https://developers.google.com/admob/ios/privacy).
|
|
23
|
+
|
|
24
|
+
## `requestConsent(config)`
|
|
25
|
+
|
|
26
|
+
Starts the UMP consent-information update and immediately loads/presents a form if one is required.
|
|
27
|
+
|
|
28
|
+
| Property | Type | Default | Description |
|
|
29
|
+
| ------------------------- | -------- | -------------- | ---------------------------------------------------------------------- |
|
|
30
|
+
| `geography` | Number | Native default | A `DEBUG_GEOGRAPHY_*` constant. Development only. |
|
|
31
|
+
| `testDeviceIds` | String[] | `[]` | UMP hashed test-device identifiers. Used when `geography` is supplied. |
|
|
32
|
+
| `tagForUnderAgeOfConsent` | Boolean | `false` | Marks the consent request as under age of consent. |
|
|
33
|
+
|
|
34
|
+
Use `{}` when no options are needed:
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
Admob.requestConsent({})
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The operation is idempotent:
|
|
41
|
+
|
|
42
|
+
- Concurrent calls share the active native operation.
|
|
43
|
+
- The first accepted call supplies the configuration.
|
|
44
|
+
- A later call after success re-emits the cached `consent_complete` result.
|
|
45
|
+
- After an update or form failure, another `requestConsent({})` can retry without clearing persisted consent.
|
|
46
|
+
- `resetConsent()` clears UMP test state and the module's cached consent operation so a new request can run.
|
|
47
|
+
|
|
48
|
+
Do not put `resetConsent()` in a production launch path.
|
|
49
|
+
|
|
50
|
+
## `consent_complete`
|
|
51
|
+
|
|
52
|
+
Every accepted consent operation reaches this terminal event:
|
|
53
|
+
|
|
54
|
+
```js
|
|
55
|
+
{
|
|
56
|
+
success: true,
|
|
57
|
+
canRequestAds: true,
|
|
58
|
+
privacyOptionsRequired: false
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
On failure:
|
|
63
|
+
|
|
64
|
+
```js
|
|
65
|
+
{
|
|
66
|
+
success: false,
|
|
67
|
+
canRequestAds: false,
|
|
68
|
+
privacyOptionsRequired: false,
|
|
69
|
+
error: 'Native UMP error message',
|
|
70
|
+
code: 1
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
A failed update can still report `canRequestAds: true` from previously stored consent. Check that field even when `success` is false; do not assume an error requires blocking all requests. If it is false, continue the app without ads and offer a later retry.
|
|
75
|
+
|
|
76
|
+
The native `code` depends on the platform and SDK. Avoid tying business logic to a particular value.
|
|
77
|
+
|
|
78
|
+
The payload temporarily retains these deprecated keys:
|
|
79
|
+
|
|
80
|
+
- `canShowAds`
|
|
81
|
+
- `canShowPersonalizedAds`
|
|
82
|
+
- `isGDPR`
|
|
83
|
+
- `isPrivacyOptionsRequired`
|
|
84
|
+
|
|
85
|
+
New code must use `canRequestAds` and `privacyOptionsRequired`.
|
|
86
|
+
|
|
87
|
+
The legacy `consent_ready` and `consent_error` events are emitted before the modern terminal event for compatibility. Do not subscribe to all three for the same workflow or initialization may be attempted more than once.
|
|
88
|
+
|
|
89
|
+
## `canRequestAds()`
|
|
90
|
+
|
|
91
|
+
Returns UMP's current permission to request ads.
|
|
92
|
+
|
|
93
|
+
```js
|
|
94
|
+
if (Admob.canRequestAds()) {
|
|
95
|
+
// An ad request may be made.
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
This is the only supported request gate.
|
|
100
|
+
|
|
101
|
+
- `canShowAds()` is a deprecated alias.
|
|
102
|
+
- `canShowPersonalizedAds()` is deprecated and always returns `false`.
|
|
103
|
+
- `isGDPR()` is a legacy diagnostic and must not gate ads.
|
|
104
|
+
- Do not parse `IABTCF_*` storage.
|
|
105
|
+
- Do not derive or inject an `npa` request flag from these legacy methods.
|
|
106
|
+
|
|
107
|
+
Google Mobile Ads applies the privacy signals collected by UMP.
|
|
108
|
+
|
|
109
|
+
## `initialize(config)`
|
|
110
|
+
|
|
111
|
+
Configures the global Google Mobile Ads request settings and initializes the SDK.
|
|
112
|
+
|
|
113
|
+
| Property | Type | Description |
|
|
114
|
+
| ------------------------------ | -------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
115
|
+
| `testDeviceIds` | String[] | Google Mobile Ads test-device identifiers. |
|
|
116
|
+
| `disableMediation` | Boolean | Optional global session exception before SDK startup; defaults to `false`. See [automatic adapter discovery](api-reference.md#automatic-adapter-discovery). |
|
|
117
|
+
| `ageRestrictedTreatment` | String | One `AGE_RESTRICTED_TREATMENT_*` constant. |
|
|
118
|
+
| `maxAdContentRating` | String | One `MAX_AD_CONTENT_RATING_*` constant. |
|
|
119
|
+
| `tagForChildDirectedTreatment` | Boolean | Deprecated compatibility input; maps to child/unspecified. |
|
|
120
|
+
| `tagForUnderAgeOfConsent` | Boolean | Deprecated initialization input; maps to child/unspecified. This is distinct from the UMP option with the same name. |
|
|
121
|
+
|
|
122
|
+
`applicationId` and `debugGeography` are not initialization properties.
|
|
123
|
+
|
|
124
|
+
Initialization is idempotent:
|
|
125
|
+
|
|
126
|
+
- Concurrent calls share one native operation.
|
|
127
|
+
- The first accepted configuration wins.
|
|
128
|
+
- A call after completion re-emits the cached `module_initialized` result.
|
|
129
|
+
- An Android failure caused by a missing current activity can be retried.
|
|
130
|
+
|
|
131
|
+
### `module_initialized`
|
|
132
|
+
|
|
133
|
+
Success:
|
|
134
|
+
|
|
135
|
+
```js
|
|
136
|
+
{
|
|
137
|
+
success: true,
|
|
138
|
+
status: 'Native initialization status description'
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Android can report a missing-activity failure:
|
|
143
|
+
|
|
144
|
+
```js
|
|
145
|
+
{
|
|
146
|
+
success: false,
|
|
147
|
+
error: 'No current activity',
|
|
148
|
+
code: -1
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Android SDK initialization failures also emit `success: false`, `code: -1` and the failure text; a later call can retry. Request configuration and SDK startup run in the background, while completion events return to the UI thread. The app can continue while SDK work runs; a provider timeout still delays completion.
|
|
153
|
+
|
|
154
|
+
Use `status` for diagnostics. Its text can change and should not drive application logic.
|
|
155
|
+
|
|
156
|
+
## App Tracking Transparency
|
|
157
|
+
|
|
158
|
+
`getTrackingAuthorizationStatus()` reads the current ATT state synchronously, without a prompt, event, or SDK initialization. Call it again after returning from Settings; the result is not cached.
|
|
159
|
+
|
|
160
|
+
`requestTrackingAuthorization()` remains the explicit permission request and emits:
|
|
161
|
+
|
|
162
|
+
```js
|
|
163
|
+
{
|
|
164
|
+
status: 3,
|
|
165
|
+
authorized: true,
|
|
166
|
+
applicable: true
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
On iOS, the numeric status follows Apple's `ATTrackingManagerAuthorizationStatus`:
|
|
171
|
+
|
|
172
|
+
| Value | Meaning |
|
|
173
|
+
| ----: | -------------- |
|
|
174
|
+
| `0` | Not determined |
|
|
175
|
+
| `1` | Restricted |
|
|
176
|
+
| `2` | Denied |
|
|
177
|
+
| `3` | Authorized |
|
|
178
|
+
|
|
179
|
+
On Android, ATT does not apply. The method completes synchronously with `status: 3`, `authorized: true`, and `applicable: false`, allowing the same listener and call site on both platforms. The query returns the same convention. `authorized: true` here is not general permission to track an Android user.
|
|
180
|
+
|
|
181
|
+
For apps that use tracking, run UMP → optional ATT → `canRequestAds()` → `initialize()` → ad loading. Request ATT while the application is active in the foreground, after UMP has dismissed its form and other system prompts have finished. If the app becomes inactive, defer the request until it is active again.
|
|
182
|
+
|
|
183
|
+
When requesting ATT, always wait for `tracking_authorization_completed`, even if the user resolved ATT in an earlier session. Denial changes tracking availability and does not imply `canRequestAds() === false`; UMP still determines permission to request ads.
|
|
184
|
+
|
|
185
|
+
ATT is optional. An application that calls `requestTrackingAuthorization()` must ship `NSUserTrackingUsageDescription` in its plist, because Apple's prompt cannot be presented without one. An IDFA message configured in AdMob can also make UMP request ATT during `requestConsent()`, which requires the same key and translations even if the app never calls the module's explicit request method. Omit the key only when neither the app nor its SDK configuration requests ATT and the app does not track. See Google's [UMP IDFA message guide](https://developers.google.com/admob/ios/privacy/idfa). See [Getting started](getting-started.md#ios).
|
|
186
|
+
|
|
187
|
+
## Privacy options
|
|
188
|
+
|
|
189
|
+
After consent completes, use either the event payload or `isPrivacyOptionsRequired()` to decide whether the app should show a visible, interactive privacy entry point:
|
|
190
|
+
|
|
191
|
+
```js
|
|
192
|
+
privacyButton.visible = Admob.isPrivacyOptionsRequired()
|
|
193
|
+
privacyButton.touchEnabled = privacyButton.visible
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
When the user taps it:
|
|
197
|
+
|
|
198
|
+
```js
|
|
199
|
+
const onPrivacyDismissed = event => {
|
|
200
|
+
if (!event.success) {
|
|
201
|
+
Ti.API.warn(`Privacy options failed: ${event.error}`)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
privacyButton.visible = event.privacyOptionsRequired
|
|
205
|
+
privacyButton.touchEnabled = privacyButton.visible
|
|
206
|
+
// Recheck event.canRequestAds and replace affected placements before
|
|
207
|
+
// requesting more ads. The integration library handles this boundary.
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
Admob.addEventListener('privacy_options_dismissed', onPrivacyDismissed)
|
|
211
|
+
|
|
212
|
+
privacyButton.addEventListener('click', () => {
|
|
213
|
+
Admob.presentPrivacyOptions({})
|
|
214
|
+
})
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
The event payload is:
|
|
218
|
+
|
|
219
|
+
```js
|
|
220
|
+
{
|
|
221
|
+
success: true,
|
|
222
|
+
canRequestAds: true,
|
|
223
|
+
privacyOptionsRequired: true
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Failures add `error` and `code`. Remove the module listener when its owning service is disposed.
|
|
228
|
+
|
|
229
|
+
After a privacy change, stop new loads and presentations until the dismissal result has been evaluated. Discard ads requested under the previous choice and create fresh requests only if `canRequestAds()` allows them. Do not destroy an onscreen fullscreen ad as a way to dismiss it. The [integration library](library.md#privacy-and-diagnostics) manages placement reauthorization.
|
|
230
|
+
|
|
231
|
+
## Testing UMP
|
|
232
|
+
|
|
233
|
+
GMA test-device IDs and UMP test-device IDs belong to separate SDK configurations; registering one does not register the other. Copy each identifier from that SDK's log. `TEST_*` ad units select Google test creatives; they do not force a UMP region.
|
|
234
|
+
|
|
235
|
+
Use debug geography only with registered UMP test devices:
|
|
236
|
+
|
|
237
|
+
```js
|
|
238
|
+
Admob.requestConsent({
|
|
239
|
+
geography: Admob.DEBUG_GEOGRAPHY_EEA,
|
|
240
|
+
testDeviceIds: ['HASHED_UMP_TEST_DEVICE_ID'],
|
|
241
|
+
tagForUnderAgeOfConsent: false
|
|
242
|
+
})
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Available debug constants:
|
|
246
|
+
|
|
247
|
+
- `DEBUG_GEOGRAPHY_DISABLED`
|
|
248
|
+
- `DEBUG_GEOGRAPHY_EEA`
|
|
249
|
+
- `DEBUG_GEOGRAPHY_REGULATED_US_STATE`
|
|
250
|
+
- `DEBUG_GEOGRAPHY_OTHER`
|
|
251
|
+
|
|
252
|
+
Do not store the numeric values; use the names because the native enum values are platform-owned.
|
|
253
|
+
|
|
254
|
+
`resetConsent()` exists for controlled testing. It does not remove the need to request consent information again, and it must not be exposed as a general "revoke consent" implementation. Use `presentPrivacyOptions()` for the publisher-rendered privacy entry point.
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
## Interactive consent demo
|
|
258
|
+
|
|
259
|
+
The published `example/app.js` includes **Consent demo** controls in development/test builds on Android and iOS. First launch uses the normal region. Production builds hide these controls and ignore saved debug settings.
|
|
260
|
+
|
|
261
|
+
1. In the visible **Test device** field, use the physical device's UMP identifier from the SDK log, including any hyphens on iOS; the app remembers it. To prefill a development build, set the string application property `demo.consentDeviceIds.ios` or `demo.consentDeviceIds.android` in the consumer's `tiapp.xml`. A saved nonempty field takes precedence. iOS simulators need no identifier. Enter only finishes typing.
|
|
262
|
+
2. Tap the scenario selector, choose **Europe — consent form**, **US — privacy options**, **Other region — no form**, or **My actual region**, then tap **Run test**. Selecting a scenario alone does not reset consent. Running it clears the previous UMP choice and replaces existing ads.
|
|
263
|
+
3. The single result below the button reports progress and completion. Europe requests consent; US opens privacy options when available; Other and the actual region can finish without a dialog. Which form is available depends on the AdMob message configuration. The banner slot keeps its height while ads reload.
|
|
264
|
+
4. Use **Advertising privacy options** to revise a choice without resetting. Relaunching preserves the chosen mode, hashes and consent. Run **My actual region** to remove the region override.
|
|
265
|
+
|
|
266
|
+
Demo ad unit IDs do not register physical devices for UMP debugging. If the SDK log redacts the hash, use an iOS simulator; an unregistered physical device may ignore forced geography.
|
|
267
|
+
|
|
268
|
+
These test buttons reset UMP consent only. They cannot reset Apple's ATT permission. Do not copy these testing controls or debug geography into a production consent flow. Unknown/error states are displayed separately from a successful result that does not require privacy options.
|
|
269
|
+
|
|
270
|
+
Google reference: [Testing UMP on iOS](https://developers.google.com/admob/ios/privacy#testing) and [Testing UMP on Android](https://developers.google.com/admob/android/privacy#testing).
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
## ATT demo controls
|
|
274
|
+
|
|
275
|
+
The published example includes a Tracking transparency section on both platforms:
|
|
276
|
+
|
|
277
|
+
- Check ATT status calls `getTrackingAuthorizationStatus()` and displays the current status, authorization and applicability without requesting permission or initializing ads. The result also shows when it was checked and a manual check count, so repeated taps have visible feedback.
|
|
278
|
+
- Request ATT permission is enabled only for an undetermined iOS permission while the app is active and no consent/initialization operation is busy. It uses the same request event as startup. Resolved, restricted and Android states keep the button disabled. A resolved iOS permission has a nearby explanation directing changes to Settings.
|
|
279
|
+
- Returning from Settings refreshes the displayed system state. The app cannot reset ATT through UMP or show Apple's prompt again after a resolved choice.
|
|
280
|
+
|
|
281
|
+
An AdMob UMP IDFA message may already resolve ATT during startup. In that case the request button is disabled and the current result remains visible. On Android, the section explains that the portable `authorized: true` value is not general tracking permission. See [ATT configuration](getting-started.md#ios) for the usage description and translations.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
> Source snapshot: official ti.admob.unified 1.0.0 documentation at release source commit `034843299b2984253988e0ffccb9f6a1c91682c9` (copied 2026-10-24). Relative links rebased for this skill.
|
|
2
|
+
|
|
3
|
+
# Shipping an application
|
|
4
|
+
|
|
5
|
+
Use this guide after the [first test integration](getting-started.md) works. Version `1.0.0` supplies the native bridge and an optional JavaScript integration library. Your application controls ad placement, production configuration, consent entry points and store disclosures.
|
|
6
|
+
|
|
7
|
+
## Install and configure the release
|
|
8
|
+
|
|
9
|
+
Install the core ZIP for every target platform and only the mediation complements you use. Extract archives at the application root so the manifests sit under `modules/<platform>/<module-id>/1.0.0/`. Declare matching versions in `tiapp.xml` and rebuild the app after changing modules. Do not add another AdMob module, a second copy of Google Mobile Ads, or duplicate provider frameworks alongside this distribution.
|
|
10
|
+
|
|
11
|
+
Use a maintained Titanium SDK at or above `13.4.1.GA`, iOS `15.0+` and Android API `24+`. The minimums describe the declared support floor; consult [release readiness](roadmap.md#release-readiness) for the device coverage. On Android, Maven dependencies resolve during the app build. On iOS, the ZIP already includes the native frameworks; consumers do not run this repository's CocoaPods installers.
|
|
12
|
+
|
|
13
|
+
Set each platform's own AdMob application ID in `tiapp.xml`, then select an ad-unit ID for that platform **and format**. App IDs contain `~`; unit IDs contain `/`. Keep Google's `TEST_*` constants in development builds and supply your own units through the app's build configuration for distribution. GMA test-device registration and UMP debug-device registration are separate. Remove forced UMP geography, consent-reset controls and demo property overrides from production flows. See [testing guidance for Android](https://developers.google.com/admob/android/test-ads) and [iOS](https://developers.google.com/admob/ios/test-ads).
|
|
14
|
+
|
|
15
|
+
A release ZIP's size is not the installed application size: Android dependencies resolve separately and iOS frameworks contain device/simulator slices. Measure the final store archive for the chosen networks.
|
|
16
|
+
|
|
17
|
+
## Verify the application in AdMob
|
|
18
|
+
|
|
19
|
+
Link the correct store listing to each platform's AdMob app. Follow the verification and app-readiness status shown in the AdMob console; a working Google demo ad does not prove that a production app is approved to serve ads.
|
|
20
|
+
|
|
21
|
+
Publish the account's own `app-ads.txt` entries on the developer website associated with the store listing. This file belongs on that website, not in Titanium's `Resources/`. Follow Google's [app verification instructions](https://support.google.com/admob/answer/14538460?hl=en-GB) and use the console's verification check. Review the selected providers' seller entries when configuring mediation.
|
|
22
|
+
|
|
23
|
+
## Finish privacy and store settings
|
|
24
|
+
|
|
25
|
+
Request a fresh UMP update on every launch, wait for `consent_complete`, check `canRequestAds()`, then initialize and load. A UMP error can still leave requests allowed by prior consent; use the returned gate rather than inferring consent from the error. Keep the application usable when ads are unavailable.
|
|
26
|
+
|
|
27
|
+
Expose an accessible privacy-options action when `isPrivacyOptionsRequired()` is true. After dismissal, reevaluate the gate and replace requests affected by changed choices. The [integration library](library.md) manages that boundary. Do not use `resetConsent()` as a production revocation action. Google's [UMP guide](https://developers.google.com/admob/ios/privacy) describes the update, request gate and privacy entry point.
|
|
28
|
+
|
|
29
|
+
ATT applies to the app's actual tracking use on iOS. If you request it, provide an accurate `NSUserTrackingUsageDescription`, resolve the prompt before SDK initialization, and handle denial without treating it as a UMP denial. Review [Apple's tracking requirements](https://developer.apple.com/app-store/user-privacy-and-data-use/). Configure the UMP under-age input and GMA age treatment for the app's audience; they are separate settings. A maximum content rating alone does not establish an age policy.
|
|
30
|
+
|
|
31
|
+
Review Google's [App Store data disclosure guide](https://developers.google.com/admob/ios/privacy/data-disclosure) and [Google Play data disclosure guide](https://developers.google.com/admob/android/privacy/play-data-disclosure), plus the guidance for each included provider. Complete the app's privacy policy, store privacy answers and advertising declarations based on the SDKs and features it actually uses. Bundled privacy manifests do not fill in those answers for you. Maintain the `SKAdNetworkItems` required by Google and the installed networks, using their current setup guides.
|
|
32
|
+
|
|
33
|
+
## Choose presentation points
|
|
34
|
+
|
|
35
|
+
Reserve real banner space within safe areas and away from navigation controls. For anchored adaptive banners, measure the laid-out host width, reserve the native size and apply the loaded height. The [banner guide](banners.md) covers pause/resume, orientation and cleanup. Let Google's ad-unit policy own automatic refresh.
|
|
36
|
+
|
|
37
|
+
Preload fullscreen ads and show at a deliberate opportunity. Offer rewarded ads explicitly. For [rewarded interstitials](rewarded-interstitial.md), explain the configured reward and provide a skip option before presentation. Grant rewards only from `rewarded`; `closed` and `impression` do not prove a reward. Allow one fullscreen presentation at a time, including UMP, ATT and Inspector. Keep app-open ads within a foreground loading experience; skip the opportunity when the user has reached content. An expired app-open ad needs another `load()` even with `autoReload: true`.
|
|
38
|
+
|
|
39
|
+
The library checks fullscreen dimensions at `show()` and reloads an incompatible ad without presenting it automatically. Raw API consumers must manage that boundary themselves. See [fullscreen guidance](fullscreen.md) and the [library contract](library.md#fullscreen-ads).
|
|
40
|
+
|
|
41
|
+
## Validate selected mediation sources
|
|
42
|
+
|
|
43
|
+
Configure each provider account, credentials, format and AdMob source mapping. Install the corresponding complement and initialize the core normally. `getMediationStatus()` reports Google's initialization diagnostics, not successful delivery or a complete package inventory.
|
|
44
|
+
|
|
45
|
+
Use provider test mode and Ad Inspector single-source testing on a registered test device. Check each platform separately: a Google demo ad or an adapter marked `ready` does not prove that the provider served an ad. Follow the [mediation guide](mediation.md) and the exact versions in [Dependencies](dependencies.md).
|
|
46
|
+
|
|
47
|
+
## Troubleshoot integration failures
|
|
48
|
+
|
|
49
|
+
| Symptom | Check and next action |
|
|
50
|
+
| --- | --- |
|
|
51
|
+
| App terminates before JavaScript startup | Verify `GADApplicationIdentifier` / Android `APPLICATION_ID` in the consumer configuration and inspect native logs. |
|
|
52
|
+
| `canRequestAds()` is false | Inspect `consent_complete`, the app ID and AdMob message setup. Continue without ads; retry a failed consent update later without resetting production consent. |
|
|
53
|
+
| Banner is empty, clipped or offscreen | Inspect host/proxy dimensions, safe areas, available width and the final `postlayout`. On iOS reserve anchored height before loading. |
|
|
54
|
+
| `failed_to_load` | Record event name, format, code and message. Use test units, check connectivity/configuration, then retry at a later opportunity with backoff. Avoid immediate retry loops. |
|
|
55
|
+
| `failed_to_show` | Check readiness, foreground host, expiry, dimensions and another active presentation. Raw `show()` returns no success value; listen for events. |
|
|
56
|
+
| Rewarded/interstitial has controls but no visible creative | Capture the visible failure and SDK logs. An impression does not prove visible rendering. See the known iOS issues in [release readiness](roadmap.md#release-readiness). |
|
|
57
|
+
| Google ads work but a provider does not | Check provider mappings, test mode and Ad Inspector single-source results; core test units do not validate mediation. |
|
|
58
|
+
| iOS extraction reports `EACCES` or a reinstall uses stale files | Replace the affected module extraction with a freshly unpacked release ZIP, then clean/rebuild. Check installed manifest/files; maintainers must package writable frameworks. |
|
|
59
|
+
| Ads disappear after an Android theme change | Preserve placements on Titanium's `_closeFromActivityForcedToDestroy` close event; see [cleanup](library.md#cleanup). |
|
|
60
|
+
|
|
61
|
+
When reporting a problem, include module/Titanium versions, OS/device, format, test versus production unit, complete error payload and a minimal reproduction. Remove credentials, personal test-device identifiers and user data from shared logs. Native load errors, show errors and UMP errors use different code domains; consult the [API reference](api-reference.md#common-google-mobile-ads-error-codes).
|
|
62
|
+
|
|
63
|
+
## Test the final app build
|
|
64
|
+
|
|
65
|
+
Exercise the app on physical iOS and Android devices with test traffic: first launch and saved consent, privacy reopening, applicable ATT outcomes, banner layout/rotation/backgrounding, fullscreen show/close/failure and actual reward delivery. Verify repeated navigation releases listeners and preserves the banner slot. Test an unavailable-ad path so navigation never depends on receiving an ad.
|
|
66
|
+
|
|
67
|
+
Version `1.0.0` implements standard, anchored/inline adaptive and collapsible banners, interstitial, rewarded, rewarded interstitial and app open. The Native factory is unavailable. Server-side reward verification configuration, paid-event callbacks and public response-info access are also outside this release's API. Review [the API reference](api-reference.md) before depending on additional SDK features.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
> Source snapshot: official ti.admob.unified 1.0.0 documentation at release source commit `034843299b2984253988e0ffccb9f6a1c91682c9` (copied 2026-10-24). Relative links rebased for this skill.
|
|
2
|
+
|
|
3
|
+
# Rewarded interstitial ads
|
|
4
|
+
|
|
5
|
+
Rewarded interstitials offer a reward at a natural transition, such as the end of a level. Before showing one, your app must explain the reward and give the user a way to skip the ad. The native proxy does not create that introduction for you. See Google's requirements for [Android](https://developers.google.com/admob/android/rewarded-interstitial) and [iOS](https://developers.google.com/admob/ios/rewarded-interstitial).
|
|
6
|
+
|
|
7
|
+
Use `createRewarded()` for a placement the user explicitly chooses to open in exchange for a reward. Use `createRewardedInterstitial()` for a transition placement with its required introduction. An ordinary interstitial must not offer a reward.
|
|
8
|
+
|
|
9
|
+
## Load ahead of the transition
|
|
10
|
+
|
|
11
|
+
After UMP permits requests and initialization completes:
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
const Admob = require('ti.admob.unified')
|
|
15
|
+
const ad = Admob.createRewardedInterstitial({
|
|
16
|
+
adUnitId: Admob.TEST_REWARDED_INTERSTITIAL,
|
|
17
|
+
autoReload: false,
|
|
18
|
+
requestAgent: 'my Titanium app'
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
function onReward(event) {
|
|
22
|
+
Ti.API.info(`Earned ${event.rewardAmount} ${event.rewardType}`)
|
|
23
|
+
// Credit the reward here, using the values reported by the SDK.
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function onLoaded() {
|
|
27
|
+
Ti.API.info('Rewarded interstitial is ready for a later transition')
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
ad.addEventListener(Admob.AD_REWARDED, onReward)
|
|
31
|
+
ad.addEventListener(Admob.AD_LOADED, onLoaded)
|
|
32
|
+
ad.load()
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Preloading does not present the ad. An explicit Show request may wait for loading and present when ready, provided the app cancels it when the presentation context ends.
|
|
36
|
+
|
|
37
|
+
## Explain the reward and allow skipping
|
|
38
|
+
|
|
39
|
+
The [playground](../assets/example/app.js) explains the demo reward and AdMob’s requirements in its card and links to the [official rule](https://support.google.com/admob/answer/9884467?hl=en). Show opens a dialog with Watch test ad and Skip. Skip cancels the pending presentation; Watch permits it. If a size change requires a replacement, the example waits and presents it without another Show tap. Leaving the foreground, losing eligibility, closing the window or selecting another fullscreen invalidates an outstanding introduction.
|
|
40
|
+
|
|
41
|
+
The production example below uses an AlertDialog with visible Watch and Skip buttons on both platforms. An iPad OptionDialog popover hides its cancel item, so it does not provide the same visible Skip button.
|
|
42
|
+
|
|
43
|
+
Use the reward configured for your ad unit in the introduction. The module reports the actual reward in `rewarded`; it does not expose a pre-presentation reward getter.
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
function offerTransitionAd(rewardDescription, continueApp) {
|
|
47
|
+
if (!Admob.canRequestAds() || !ad.isLoaded()) {
|
|
48
|
+
continueApp()
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const intro = Ti.UI.createAlertDialog({
|
|
53
|
+
title: 'Rewarded interstitial',
|
|
54
|
+
message: `Complete the ad to earn ${rewardDescription}. You can skip it.`,
|
|
55
|
+
buttonNames: ['Watch ad', 'Skip'],
|
|
56
|
+
cancel: 1
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
function onChoice(event) {
|
|
60
|
+
intro.removeEventListener('click', onChoice)
|
|
61
|
+
if (event.index === 0 && Admob.canRequestAds() && ad.isLoaded()) {
|
|
62
|
+
ad.show()
|
|
63
|
+
} else {
|
|
64
|
+
continueApp()
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
intro.addEventListener('click', onChoice)
|
|
69
|
+
intro.show()
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Connect `closed` and `failed_to_show` to the app's continuation too, so a presentation error cannot strand navigation. Use a continuation that runs once. Coordinate the introduction with your window's lifetime and other modal flows. Raw API consumers must also recheck fullscreen dimensions if the app rotates while the introduction is open; the [integration library](library.md#fullscreen-ads) handles that check at `show()`.
|
|
74
|
+
|
|
75
|
+
## Shared lifecycle and rewards
|
|
76
|
+
|
|
77
|
+
The proxy exposes `load()`, `show()`, `isLoaded()` and terminal `destroy()` on both platforms. It accepts the same mutable properties as Rewarded: `adUnitId`, `autoReload`, `keywords`, `requestAgent` and `extras`. See [fullscreen configuration](fullscreen.md#creation-properties).
|
|
78
|
+
|
|
79
|
+
Events use `adType: 'rewarded_interstitial'` and the shared [fullscreen payloads](fullscreen.md#events). Only `rewarded` grants a reward; closing, clicking or recording an impression does not. A repeated native reward callback emits at most one reward per presentation. The event preserves the presented ad's unit ID and SDK reward even if the next ad starts loading before that callback arrives.
|
|
80
|
+
|
|
81
|
+
The native ad is single-use. Load again after dismissal, or use `autoReload: true` to reload after `closed`. That option never presents the next ad. Handle load/show failures at a later opportunity rather than creating an immediate retry loop.
|
|
82
|
+
|
|
83
|
+
## Use the integration library
|
|
84
|
+
|
|
85
|
+
`ads.createRewardedInterstitial(options)` returns the same manager as `ads.createRewarded(options)`, including `load()`, `show()`, `retry()`, `isLoaded()` and `destroy()`. Pass the same callbacks, including `onRewarded`. Call the manager's `show()` only after your introduction permits presentation.
|
|
86
|
+
|
|
87
|
+
The [raw demo](../assets/example/app.js) includes a skip option and uses Google test traffic. The [library example](../assets/example/library-usage.js) shows the equivalent manager integration.
|
|
88
|
+
|
|
89
|
+
## Cleanup and validation
|
|
90
|
+
|
|
91
|
+
Remove named listeners before disposal:
|
|
92
|
+
|
|
93
|
+
```js
|
|
94
|
+
ad.removeEventListener(Admob.AD_REWARDED, onReward)
|
|
95
|
+
ad.removeEventListener(Admob.AD_LOADED, onLoaded)
|
|
96
|
+
ad.destroy()
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Destroy is idempotent and terminal. Late native callbacks cannot revive the proxy or grant another reward.
|
|
100
|
+
|
|
101
|
+
Native builds, wrapper tests and an iPadOS 18.6 simulator pass cover this implementation. The simulator pass verified visible Watch/Skip buttons, presentation, a single SDK reward, dismissal, reload and the Show-time orientation guard. Physical tests on an iPad (9th generation, iPadOS 27.0.1) and an OPPO CPH2639 (Android 16) confirmed visible test content, presentation, one SDK reward per completed ad, dismissal and reload. Android also verified that one Show tap after rotation replaces the stale ad and presents its replacement. The current playground introduces the reward in a dialog with visible Watch test ad and Skip buttons. See [release readiness](roadmap.md#release-readiness) for remaining coverage. Provider delivery requires its own mappings and single-source tests.
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
> Source snapshot: official ti.admob.unified 1.0.0 documentation at release source commit `034843299b2984253988e0ffccb9f6a1c91682c9` (copied 2026-10-24). Relative links rebased for this skill.
|
|
2
|
+
|
|
3
|
+
# ti.admob.unified roadmap
|
|
4
|
+
|
|
5
|
+
## Current baseline
|
|
6
|
+
|
|
7
|
+
Version `1.0.0` includes the core and five optional mediation complements:
|
|
8
|
+
|
|
9
|
+
- Titanium `13.4.1.GA`
|
|
10
|
+
- iOS `15.0+`
|
|
11
|
+
- Google Mobile Ads iOS `13.11.0`
|
|
12
|
+
- User Messaging Platform iOS `3.1.0`
|
|
13
|
+
- Android API `24+`, required by the pinned Google Mobile Ads SDK
|
|
14
|
+
- Google Mobile Ads Android `25.5.0`
|
|
15
|
+
- User Messaging Platform Android `4.0.0`
|
|
16
|
+
- five optional per-network modules on iOS and Android; ironSource mediation deferred
|
|
17
|
+
- idempotent initialization and consent
|
|
18
|
+
- terminal `consent_complete` events
|
|
19
|
+
- one standard/adaptive banner lifecycle on both platforms
|
|
20
|
+
- one interstitial/rewarded/rewarded interstitial/app open lifecycle and event contract on both platforms
|
|
21
|
+
- portable tracking-authorization completion on both platforms
|
|
22
|
+
- no manual TCF personalization inference
|
|
23
|
+
|
|
24
|
+
## Implemented for 1.0.0
|
|
25
|
+
|
|
26
|
+
### Privacy contract
|
|
27
|
+
|
|
28
|
+
- `canRequestAds()` is the only supported ad-request gate.
|
|
29
|
+
- UMP updates and form presentation use the current native flow.
|
|
30
|
+
- `initialize()` and `requestConsent()` serialize concurrent calls.
|
|
31
|
+
- Every accepted consent operation reaches `consent_complete`.
|
|
32
|
+
- Privacy-options dismissal always emits a terminal event.
|
|
33
|
+
- `requestTrackingAuthorization()` is portable after UMP: iOS resolves ATT and Android completes immediately with `applicable: false`.
|
|
34
|
+
- Legacy privacy aliases remain documented but are not used by new examples.
|
|
35
|
+
|
|
36
|
+
### Banner contract
|
|
37
|
+
|
|
38
|
+
- Standard and adaptive banners expose `load()`, `pause()`, `resume()`, `destroy()`, `isLoaded()`, and both historical destruction aliases.
|
|
39
|
+
- Common event names and payload keys are aligned.
|
|
40
|
+
- Adaptive `loaded` reports the real height in density-independent units.
|
|
41
|
+
- `destroy()` is terminal, repeatable, and emits `destroyed` once.
|
|
42
|
+
- Native delegates, listeners, controllers, views, and activity lifecycle registrations are released.
|
|
43
|
+
- Each proxy accepts one active load and at most one deferred load while paused.
|
|
44
|
+
- Application and host-lifecycle pauses are independent causes; neither resumes the other's pause.
|
|
45
|
+
- Inline adaptive banners, `maxHeight`, `customAdSize`, `contentUrl`, and `requestAgent` work on both platforms.
|
|
46
|
+
- `smart_banner` resolves to `BANNER` on both platforms, with no special case on either.
|
|
47
|
+
|
|
48
|
+
### Fullscreen contract
|
|
49
|
+
|
|
50
|
+
- Interstitial, rewarded, rewarded interstitial, and app open proxies expose the same `load()`, `show()`, `isLoaded()`, and terminal `destroy()` lifecycle.
|
|
51
|
+
- Duplicate loads are serialized and native fullscreen references are single-use.
|
|
52
|
+
- Common events include `adUnitId` and `adType`; failures add `error`, `message`, and `code`.
|
|
53
|
+
- Both platforms emit `opened`, `showed_fullscreen_content`, and `destroyed`.
|
|
54
|
+
- Native callbacks/delegates are detached during explicit destruction and Titanium proxy release.
|
|
55
|
+
- App open checks Google's four-hour validity window on demand, without a timer.
|
|
56
|
+
|
|
57
|
+
### Distribution
|
|
58
|
+
|
|
59
|
+
- The `ti.admob.unified` artifacts contain Google Mobile Ads and UMP, with no third-party ad-network SDK in the core POM. Android resolves its SDK dependencies during the consumer build, so module ZIP size is not the installed app size.
|
|
60
|
+
- Five dependency modules under `mediation/<network>/` distribute eleven iOS frameworks and five Android adapter coordinates. Unity includes Coherence; neither AdQuality nor ironSource advertising is included.
|
|
61
|
+
- Dependency versions and iOS checksums are reproducible.
|
|
62
|
+
- Static contract tests cover privacy, lifecycle, payload, cleanup, artifacts, and documentation.
|
|
63
|
+
|
|
64
|
+
## Release readiness
|
|
65
|
+
|
|
66
|
+
Rewarded Interstitial was added after the earlier five-format checks. Its native builds and wrapper tests pass. Physical iPadOS 27.0.1 and Android 16 tests confirmed visible Google test content, presentation, SDK rewards, dismissal and reload. The playground explains the reward in its card and offers Watch test ad or Skip in a dialog after Show. Selected-provider delivery still needs configured accounts and mappings.
|
|
67
|
+
|
|
68
|
+
The maintainer reported a store-published app using the module on October 23, 2026. The checks below record what has been verified. Open issues remain listed until a retest confirms a fix.
|
|
69
|
+
|
|
70
|
+
The September checks covered both native bridges, wrapper Boolean types, all five implemented formats, library cleanup/reentrant retries, consent reset ordering, volume validation and delayed reward callbacks. Core-only and selected-complement consumers were compiled on both platforms. Physical-iPad tests confirmed fullscreen presentation, dismissal and Rewarded delivery; a previous rotation-specific size failure recovered after reloading. The current library instead checks dimensions at Show and reloads only the incompatible ad. That revised policy has build/test coverage. An October 24 physical Android test also verified a single Show tap replacing a stale ad after rotation and presenting the replacement when loaded.
|
|
71
|
+
|
|
72
|
+
The October 23 dependency refresh passed physical Android 16 load checks for standard and adaptive banners, interstitial, rewarded and app open ads. An isolated consumer build completed consent and initialization, loaded test interstitial/rewarded ads and resumed from the background. SDK startup now runs outside the Android UI thread. These checks do not establish third-party provider delivery.
|
|
73
|
+
|
|
74
|
+
Earlier simulator runs had fullscreen request timeouts. A later October 23 iPadOS 18.6 simulator pass rendered fixed/adaptive banners and all four fullscreen formats, verified one reward per completed Rewarded and Rewarded Interstitial presentation, and exercised the rotation guard and background recovery. It also covered ATT denial, a permission change in Settings, UMP rejection and privacy reopening. The demo now keeps its Skip button visible on iPad. These simulator results do not close the physical-iPad issues below; the updated binaries still need physical-iOS acceptance.
|
|
75
|
+
|
|
76
|
+
Known issues and remaining coverage:
|
|
77
|
+
|
|
78
|
+
- A physical iPad intermittently renders Google's Interstitial demo creative with a zero-width image-bearing SVG inside a correctly sized WebView. Independent UIKit, React Native and Flutter integrations also reproduce it with GMA 13.11.0. A bounded native workaround restores the collapsed column-flex Google SVG on iPad, based on the detected markup rather than the unit ID. Integrated demo presentations confirmed a visible image. The workaround uses internal creative markup and is not an official SDK fix. It also checks publisher-owned Interstitial units for that exact failure; production creative rendering remains unverified.
|
|
79
|
+
- Intermittent iOS Rewarded timeouts, including a reproduction without Titanium, remain unresolved; a public error alone does not establish the cause.
|
|
80
|
+
- An earlier physical-iPad adaptive startup sizing failure did not recur in repeated launches; its cause was not isolated.
|
|
81
|
+
- Complete the consent acceptance/rejection, privacy reopening, ATT, banner orientation and lifecycle matrix on available physical devices. Complete physical-iPad coverage of the final example's Show-time dimension policy; the Android replacement-and-show path has passed.
|
|
82
|
+
- Provider delivery and consent propagation require configured publisher accounts, mappings and Ad Inspector single-source tests. Google test ads and adapter initialization do not establish them. Core-only apps need no provider accounts.
|
|
83
|
+
- The declared minimum OS versions have not been validated across a complete physical-device matrix. Keep simulator/emulator results separate from hardware results.
|
|
84
|
+
|
|
85
|
+
The pinned dependency matrix is recorded in [Dependencies](dependencies.md), including the dated transitive security review. Follow [Shipping an application](production.md) for the publisher's production checks. A module build, app-store approval or a version scan does not establish every runtime scenario.
|
|
86
|
+
|
|
87
|
+
## Next contract work
|
|
88
|
+
|
|
89
|
+
These improvements can be developed without changing the established banner and privacy APIs:
|
|
90
|
+
|
|
91
|
+
- keep the shared percentage-based `setAppVolume()` behavior covered on both platforms;
|
|
92
|
+
- remove deprecated privacy and destruction aliases in a future major release.
|
|
93
|
+
|
|
94
|
+
## Declared parity debt
|
|
95
|
+
|
|
96
|
+
Both platforms must expose an identical JavaScript surface. An audit against the native sources on 5 August 2026 found the divergences below. That pass recorded them as closed based largely on static tests. The September audit reopened cases involving bridge types and accidental exports. The table below is historical context; release acceptance requires runtime verification of the current binaries.
|
|
97
|
+
|
|
98
|
+
| Divergence closed | How |
|
|
99
|
+
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
100
|
+
| `requestAgent` was dropped by the three iOS fullscreen proxies. Their manual configuration from the creation dictionary bypasses `_initWithProperties:` and never read the key. | `configureWithDict:` reads it in all three, mirroring Android. |
|
|
101
|
+
| `extras` was a live property on iOS and creation-only on Android: assigning it after creating a banner did nothing, and reading it back returned `undefined`. | Both Android banner proxies gained the property. It takes effect on the next `load()`, the same as `requestAgent`. |
|
|
102
|
+
| The adaptive `loaded` payload carried seven size keys on Android and two on iOS. | iOS reports the same seven. `density` comes from `UIScreen.scale`, which yields only 1, 2 or 3 where Android reports fractional values. |
|
|
103
|
+
| Fullscreen proxies exposed their internal state as readable properties on iOS and none on Android, including the native handle (`GADInterstitialAd`). | Plain ivars with `accessInstanceVariablesDirectly` disabled, so Kroll's key lookup finds nothing. |
|
|
104
|
+
| `collapsible` was Android-only. | Implemented on iOS through `GADExtras`, the same key and values Android puts in its Bundle. |
|
|
105
|
+
| iOS filtered banner events by whether a listener was registered; Android emitted them unconditionally. | The guard is gone. Banner proxies descend from `TiViewProxy`, so the events reach a listener on the container. |
|
|
106
|
+
| `DEBUG_GEOGRAPHY_OTHER` returned the deprecated `UMPDebugGeographyNotEEA` (2) on iOS while Android sent 4, and `DEBUG_GEOGRAPHY_REGULATED_US_STATE` was the literal `@(3)`. | Both return the SDK enum, which matches Android's values. |
|
|
107
|
+
| iOS substituted `"coins"` and `1` when the SDK granted a reward with no values, so an application could credit a reward Android never granted. | The payload carries what the SDK reported, or `null`. |
|
|
108
|
+
| iOS `resume()` never restored `autoloadEnabled` after `pause()` turned it off, so one trip to the background left the banner opted out for the rest of the session. | `pause()` remembers the value and `resume()` restores it, once every pause cause is clear. |
|
|
109
|
+
| Adaptive events fired by the Android proxy itself omitted `adaptiveType`, which the view included. | Every adaptive event carries it. |
|
|
110
|
+
|
|
111
|
+
## Differences that stay
|
|
112
|
+
|
|
113
|
+
These differences come from the native SDKs and remain documented limitations:
|
|
114
|
+
|
|
115
|
+
| Difference | Why it stays |
|
|
116
|
+
| -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
117
|
+
| `module_initialized` describes core completion | Native initialization callbacks provide status rather than a unified error contract. Android can emit `success: false` before startup when no Activity exists. Adapter failures belong in `getMediationStatus()`; synchronous native exceptions may also propagate. |
|
|
118
|
+
| `applicable` in `tracking_authorization_completed` | App Tracking Transparency does not exist on Android. |
|
|
119
|
+
| The values of the `TEST_*` constants | They are Google's own demo unit IDs, deliberately different per platform. |
|
|
120
|
+
| `isTestDevice()` | iOS answers "are any test devices registered?", Android "is this device one?". The iOS SDK does not offer the second. |
|
|
121
|
+
| The text of error messages | Load errors use shared constants; presentation/UMP/Inspector codes retain their native domains. The text describes the platform failure ("no view controller" against "no activity"). |
|
|
122
|
+
|
|
123
|
+
## Deferred formats
|
|
124
|
+
|
|
125
|
+
Native remains reserved without a factory. Its constants are already exported. Implementation requires a complete native proxy, shared events and payloads, cleanup semantics, tests, examples, and physical validation.
|
|
126
|
+
|
|
127
|
+
Each new format must meet the existing contract: a complete native proxy on both platforms, shared events and payloads, terminal cleanup, contract tests, an example, and physical validation.
|
|
128
|
+
|
|
129
|
+
## Mediation maintenance
|
|
130
|
+
|
|
131
|
+
The five complements live in `mediation/<network>/` and target the same first release, `1.0.0`, as the core.
|
|
132
|
+
|
|
133
|
+
- Keep the five shared adapter/network SDK pairs current on both platforms.
|
|
134
|
+
- Keep every adapter out of the core module. A contract test fails if `ios/module.xcconfig`, `android/build.gradle`, the core `pbxproj`, or the core checksum inventory names a network again.
|
|
135
|
+
- Preserve one JavaScript ad contract and portable diagnostics.
|
|
136
|
+
- Track privacy manifests, resource bundles, SKAdNetwork guidance, and store disclosure changes with every dependency update.
|
|
137
|
+
- Require the consumer build matrix described in the mediation guide before release. Record physical-device coverage separately from simulator integration. Validate each provider with configured accounts, mappings and Ad Inspector before claiming its ad delivery; Google test ads establish integration only.
|
|
138
|
+
- Evaluate additional networks only when they can be supported on both platforms without fragmenting the public API.
|
|
139
|
+
|
|
140
|
+
## Longer-term platform work
|
|
141
|
+
|
|
142
|
+
- Evaluate Android Google Mobile Ads Next-Gen only after its migration cost and Titanium compatibility are understood.
|
|
143
|
+
- Revisit the minimum Titanium, iOS, and Android baselines as upstream SDK requirements change.
|