react-native-google-mobile-ads 4.1.2 → 5.0.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/RNGoogleMobileAds.podspec +16 -9
- package/__tests__/consent.test.ts +17 -9
- package/android/build.gradle +4 -3
- package/android/src/main/java/io/invertase/googlemobileads/ReactNativeGoogleMobileAdsAppOpenModule.java +2 -5
- package/android/src/main/java/io/invertase/googlemobileads/ReactNativeGoogleMobileAdsCommon.java +13 -2
- package/android/src/main/java/io/invertase/googlemobileads/ReactNativeGoogleMobileAdsConsentModule.java +100 -171
- package/android/src/main/java/io/invertase/googlemobileads/ReactNativeGoogleMobileAdsInterstitialModule.java +6 -5
- package/android/src/main/java/io/invertase/googlemobileads/ReactNativeGoogleMobileAdsRewardedModule.java +3 -4
- package/docs/common-reasons-for-ads-not-showing.mdx +92 -0
- package/docs/european-user-consent.mdx +43 -161
- package/docs/index.mdx +3 -3
- package/docs/migrating-to-v5.mdx +54 -0
- package/docs.json +3 -1
- package/ios/RNGoogleMobileAds/RNGoogleMobileAdsAppOpenModule.m +1 -2
- package/ios/RNGoogleMobileAds/RNGoogleMobileAdsCommon.h +1 -1
- package/ios/RNGoogleMobileAds/RNGoogleMobileAdsCommon.m +6 -2
- package/ios/RNGoogleMobileAds/RNGoogleMobileAdsConsentModule.h +0 -1
- package/ios/RNGoogleMobileAds/RNGoogleMobileAdsConsentModule.m +71 -126
- package/ios/RNGoogleMobileAds/RNGoogleMobileAdsInterstitialModule.m +1 -2
- package/ios/RNGoogleMobileAds/RNGoogleMobileAdsRewardedModule.m +1 -2
- package/ios_config.sh +8 -0
- package/lib/commonjs/AdsConsent.js +29 -100
- package/lib/commonjs/AdsConsent.js.map +1 -1
- package/lib/commonjs/AdsConsentStatus.js +4 -3
- package/lib/commonjs/AdsConsentStatus.js.map +1 -1
- package/lib/commonjs/validateAdRequestOptions.js +8 -0
- package/lib/commonjs/validateAdRequestOptions.js.map +1 -1
- package/lib/commonjs/version.js +1 -1
- package/lib/commonjs/version.js.map +1 -1
- package/lib/module/AdsConsent.js +30 -100
- package/lib/module/AdsConsent.js.map +1 -1
- package/lib/module/AdsConsentStatus.js +4 -3
- package/lib/module/AdsConsentStatus.js.map +1 -1
- package/lib/module/validateAdRequestOptions.js +8 -0
- package/lib/module/validateAdRequestOptions.js.map +1 -1
- package/lib/module/version.js +1 -1
- package/lib/module/version.js.map +1 -1
- package/lib/typescript/AdsConsentStatus.d.ts +10 -6
- package/lib/typescript/index.d.ts +1 -1
- package/lib/typescript/types/AdsConsent.interface.d.ts +37 -204
- package/lib/typescript/types/RequestOptions.d.ts +8 -0
- package/lib/typescript/version.d.ts +1 -1
- package/package.json +5 -3
- package/src/AdsConsent.ts +36 -135
- package/src/AdsConsentStatus.ts +11 -6
- package/src/types/AdsConsent.interface.ts +37 -214
- package/src/types/RequestOptions.ts +7 -0
- package/src/validateAdRequestOptions.ts +7 -0
- package/src/version.ts +1 -1
|
@@ -6,16 +6,9 @@ other local storage, where legally required, and to use personal data (such as `
|
|
|
6
6
|
reflects the requirements of the `EU ePrivacy Directive` and the `General Data Protection Regulation` (GDPR).
|
|
7
7
|
|
|
8
8
|
The React Native Google Mobile Ads module provides out of the box support for helping to manage your users consent
|
|
9
|
-
within your application. The `AdsConsent` helper which comes with the module wraps the Google
|
|
9
|
+
within your application. The `AdsConsent` helper which comes with the module wraps the Google UMP SDK for both
|
|
10
10
|
Android & iOS, and provides a single JavaScript interface for both platforms.
|
|
11
11
|
|
|
12
|
-
The `AdsConsent` helper & AdMob module provides out of the box support for:
|
|
13
|
-
|
|
14
|
-
- Requesting consent for multiple publisher IDs.
|
|
15
|
-
- Showing a Google-rendered consent form, listing all providers on your enabled mediation networks.
|
|
16
|
-
- Manually handling user consent if you prefer not to use the Google-rendered consent form.
|
|
17
|
-
- Forwarding your user consent status onto AdMob ad requests.
|
|
18
|
-
|
|
19
12
|
## Understanding AdMob Ads
|
|
20
13
|
|
|
21
14
|
Ads served by Google can be categorized as personalized or non-personalized, both requiring consent from users in the EEA. By default,
|
|
@@ -25,8 +18,6 @@ ad requests to Google serve personalized ads, with ad selection based on the use
|
|
|
25
18
|
|
|
26
19
|
## Handling consent
|
|
27
20
|
|
|
28
|
-
The following steps show the various methods and ways of handling consent within your app.
|
|
29
|
-
|
|
30
21
|
### Delaying app measurement
|
|
31
22
|
|
|
32
23
|
By default, the Google Mobile Ads SDK initializes app measurement and begins sending user-level event data to Google immediately when the app starts.
|
|
@@ -54,156 +45,76 @@ npx react-native run-ios
|
|
|
54
45
|
npx react-native run-android
|
|
55
46
|
```
|
|
56
47
|
|
|
57
|
-
###
|
|
48
|
+
### App Tracking Transparency
|
|
58
49
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
50
|
+
If you want to handle Apple's App Tracking Transparency requirements, ensure you've configured and published your [ATT message](https://developers.google.com/admob/ump/ios/quick-start#app_tracking_transparency) in your Google AdMob account.
|
|
51
|
+
Also, within your projects `app.json` file, you have to use the `user_tracking_usage_description` to to describe your usage:
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"react-native-google-mobile-ads": {
|
|
56
|
+
"android_app_id": "ca-app-pub-xxxxxxxx~xxxxxxxx",
|
|
57
|
+
"ios_app_id": "ca-app-pub-xxxxxxxx~xxxxxxxx",
|
|
58
|
+
"user_tracking_usage_description": "This identifier will be used to deliver personalized ads to you."
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
accepts an array of publisher IDs, which are used by the Consent SDK to determine the users consent status.
|
|
63
|
+
Once set, rebuild your application.
|
|
65
64
|
|
|
66
|
-
|
|
65
|
+
### Requesting consent information
|
|
67
66
|
|
|
68
|
-
|
|
67
|
+
It is recommended you request consent information each time your application starts to determine if the consent modal should be shown, e.g. due to provider changes.
|
|
69
68
|
|
|
70
|
-
|
|
69
|
+
The `AdsConsent` helper provides a promise based method to return the users consent status called `requestInfoUpdate`.
|
|
71
70
|
|
|
72
71
|
```js
|
|
73
72
|
import { AdsConsent } from 'react-native-google-mobile-ads';
|
|
74
73
|
|
|
75
|
-
const consentInfo = await AdsConsent.requestInfoUpdate(
|
|
74
|
+
const consentInfo = await AdsConsent.requestInfoUpdate();
|
|
76
75
|
```
|
|
77
76
|
|
|
78
|
-
The
|
|
79
|
-
|
|
80
|
-
- **status**: The status can be one of 3 outcomes:
|
|
81
|
-
- `UNKNOWN`: The user has not yet given consent, or has been invalidated since the last time they gave consent.
|
|
82
|
-
- `NON_PERSONALIZED`: The user gave consent to see non-personalized ads only.
|
|
83
|
-
- `PERSONALIZED`: The user gave consent to see personalized ads.
|
|
84
|
-
- **isRequestLocationInEeaOrUnknown**: A boolean value. If `true` the users location is within the EEA or is unknown.
|
|
85
|
-
|
|
86
|
-
The combination of status and location allows you to handle the next steps for requesting consent, if required:
|
|
77
|
+
The method returns an `AdsConsentInfo` interface, which provides information about consent form availability and the users consent status:
|
|
87
78
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
79
|
+
- **status**: The status can be one of 4 outcomes:
|
|
80
|
+
- `UNKNOWN`: Unknown consent status.
|
|
81
|
+
- `REQUIRED`: User consent required but not yet obtained.
|
|
82
|
+
- `NOT_REQUIRED`: User consent not required.
|
|
83
|
+
- `OBTAINED`: User consent already obtained.
|
|
84
|
+
- **isConsentFormAvailable**: A boolean value. If `true` a consent form is available.
|
|
91
85
|
|
|
92
86
|
### Gathering user consent
|
|
93
87
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
1. Using a Google-rendered consent form.
|
|
97
|
-
2. Using a custom method.
|
|
98
|
-
|
|
99
|
-
If you are aware that users are under the age of consent in Europe, it is possible to set this using the `setTagForUnderAgeOfConsent`
|
|
100
|
-
method (TFUA). Once the setting is enabled, the Google-rendered consent form will fail to load. All ad requests that include
|
|
101
|
-
TFUA will be made ineligible for personalized advertising and remarketing. TFUA disables requests to third-party ad technology
|
|
102
|
-
providers, such as ad measurement pixels and third-party ad servers.
|
|
103
|
-
|
|
104
|
-
To remove this setting, pass `false` to the method.
|
|
105
|
-
|
|
106
|
-
#### 1. Google-rendered consent form
|
|
107
|
-
|
|
108
|
-
The Google-rendered consent form is a full-screen configurable form that displays over your app content.
|
|
109
|
-
You can configure the form to present the user with combinations of the following options:
|
|
110
|
-
|
|
111
|
-
- Consent to view personalized ads
|
|
112
|
-
- Consent to view non-personalized ads
|
|
113
|
-
- Use a paid version of the app instead of viewing ads
|
|
114
|
-
|
|
115
|
-
You should review the consent text carefully: what appears by default is a message that might be appropriate if you use
|
|
116
|
-
Google to monetize your app; but Google cannot provide legal advice on the consent text that is appropriate for you.
|
|
117
|
-
To update consent text of the Google-rendered consent form, modify the `consentform.html` file included in the Consent SDK as required.
|
|
118
|
-
|
|
119
|
-
> An [example of a Google-rendered](https://developers.google.com/admob/images/android_eu_consent_form.png) consent form.
|
|
120
|
-
|
|
121
|
-
To show the consent form, the `AdsConsent` helper provides a `showForm` method, which takes options to configure the form.
|
|
122
|
-
You must provide a privacy policy URL.
|
|
88
|
+
To request consent, call these methods as early as possible within your app before presenting any ads.
|
|
89
|
+
Once the user has selected their preference, the `showForm` method returns an `AdsConsentFormResult` interface, containing the updated consent status:
|
|
123
90
|
|
|
124
91
|
```js
|
|
125
92
|
import { AdsConsent, AdsConsentStatus } from 'react-native-google-mobile-ads';
|
|
126
93
|
|
|
127
|
-
const consentInfo = await AdsConsent.requestInfoUpdate(
|
|
94
|
+
const consentInfo = await AdsConsent.requestInfoUpdate();
|
|
128
95
|
|
|
129
96
|
if (
|
|
130
|
-
consentInfo.
|
|
131
|
-
consentInfo.status === AdsConsentStatus.
|
|
97
|
+
consentInfo.isConsentFormAvailable &&
|
|
98
|
+
consentInfo.status === AdsConsentStatus.REQUIRED
|
|
132
99
|
) {
|
|
133
|
-
const
|
|
134
|
-
privacyPolicy: 'https://invertase.io/privacy-policy',
|
|
135
|
-
withPersonalizedAds: true,
|
|
136
|
-
withNonPersonalizedAds: true,
|
|
137
|
-
withAdFree: true,
|
|
138
|
-
});
|
|
100
|
+
const { status } = await AdsConsent.showForm();
|
|
139
101
|
}
|
|
140
102
|
```
|
|
141
103
|
|
|
142
|
-
Once the user has selected their preference, the `formResult` contains their status and whether or not they prefer an
|
|
143
|
-
ad-free option of your application (if enabled):
|
|
144
|
-
|
|
145
|
-
```js
|
|
146
|
-
const formResult = await AdsConsent.showForm({
|
|
147
|
-
privacyPolicy: 'https://invertase.io/privacy-policy',
|
|
148
|
-
withPersonalizedAds: true,
|
|
149
|
-
withNonPersonalizedAds: true,
|
|
150
|
-
withAdFree: true,
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
if (formResult.userPrefersAdFree) {
|
|
154
|
-
// Handle the users request, e.g. redirect to a paid for version of the app
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// The user requested non-personalized or personalized ads
|
|
158
|
-
const status = formResult.status;
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
The `formResult.status` provides feedback on whether the user consented to personalized ads, or non-personalized ads.
|
|
162
|
-
It is important that you forward this status onto all ad requests (see below).
|
|
163
|
-
|
|
164
104
|
> Do not persist the status. You could however store this locally in application state (e.g. React Context) and update the status on every app launch as it may change.
|
|
165
105
|
|
|
166
|
-
#### 2. Custom consent method
|
|
167
|
-
|
|
168
|
-
If you wish to implement your own consent flow, the `AdsConsent` helper provides the tools needed to accomplish this.
|
|
169
|
-
Depending on your requirements, a list of the enabled network mediation providers for the AdMob App ID can be returned and shown
|
|
170
|
-
to the user via the `getAdProviders` method:
|
|
171
|
-
|
|
172
|
-
```js
|
|
173
|
-
import { AdsConsent } from 'react-native-google-mobile-ads';
|
|
174
|
-
|
|
175
|
-
const providers = await AdsConsent.getAdProviders();
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
Each provider is an interface of `AdProvider`, containing information such as their company ID, company name and privacy policy URL.
|
|
179
|
-
Using this information you can request consent from your users.
|
|
180
|
-
|
|
181
|
-
Once consent has been returned, the Consent SDK needs to be aware of the custom user consent status. This can be forwarded on using the
|
|
182
|
-
`setStatus` method, for example:
|
|
183
|
-
|
|
184
|
-
```js
|
|
185
|
-
import { AdsConsent, AdsConsentStatus } from 'react-native-google-mobile-ads';
|
|
186
|
-
|
|
187
|
-
// After getting user consent...
|
|
188
|
-
await AdsConsent.setStatus(AdsConsentStatus.PERSONALIZED);
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
To invalidate the users consent status (e.g. if the providers list changes since their last consent), set the status back to
|
|
192
|
-
`UNKNOWN`. When your application next boots, you can get the users previous consent status using the `getStatus` method.
|
|
193
|
-
|
|
194
106
|
### Testing
|
|
195
107
|
|
|
196
108
|
When developing the consent flow, the behavior of the `AdsConsent` responses may not be reliable due to the environment
|
|
197
109
|
(e.g. using an emulator vs real device). It is possible to set a debug location to test the various responses from the
|
|
198
|
-
|
|
110
|
+
UMP SDK.
|
|
199
111
|
|
|
200
|
-
If using a real device, ensure you
|
|
201
|
-
such as [react-native-device-info](https://github.com/react-native-community/react-native-device-info)
|
|
202
|
-
call the `addTestDevice(deviceId)` method.
|
|
112
|
+
If using a real device, ensure you add it to the list of allowed devices by passing the device ID, which can be obtained from native logs or using a library
|
|
113
|
+
such as [react-native-device-info](https://github.com/react-native-community/react-native-device-info), to `testDeviceIdentifiers`.
|
|
203
114
|
|
|
204
115
|
> Emulators are automatically whitelisted.
|
|
205
116
|
|
|
206
|
-
To set a debug location, use the `
|
|
117
|
+
To set a debug location, use the `debugGeography` key. It accepts 3 values:
|
|
207
118
|
|
|
208
119
|
- **DISABLED**: Removes any previous debug locations.
|
|
209
120
|
- **EEA**: Set the test device to be within the EEA.
|
|
@@ -214,45 +125,16 @@ For example:
|
|
|
214
125
|
```js
|
|
215
126
|
import { AdsConsent, AdsConsentDebugGeography } from 'react-native-google-mobile-ads';
|
|
216
127
|
|
|
217
|
-
await AdsConsent.
|
|
128
|
+
const consentInfo = await AdsConsent.requestInfoUpdate({
|
|
129
|
+
debugGeography: AdsConsentDebugGeography.EEA,
|
|
130
|
+
testDeviceIdentifiers: ['TEST-DEVICE-HASHED-ID'],
|
|
131
|
+
});
|
|
218
132
|
```
|
|
219
133
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
### Forwarding the consent status to ads
|
|
223
|
-
|
|
224
|
-
Assuming the user is within the EEA and has provided consent, their status needs to be forwarded to every ad request we
|
|
225
|
-
make in our application.
|
|
226
|
-
|
|
227
|
-
> If the user is within the EEA and has not given consent, do not display AdMob ads (even non-personalized).
|
|
228
|
-
|
|
229
|
-
Taking a Rewarded Video as an example, we can apply the users consent when our ad is loaded via the `RequestOptions`. For example:
|
|
134
|
+
It is possible to reset the UMP state for test devices. To reset the ATT state you have to delete and reinstall your app though.
|
|
230
135
|
|
|
231
136
|
```js
|
|
232
|
-
import { AdsConsent
|
|
233
|
-
|
|
234
|
-
const status = await AdsConsent.getStatus();
|
|
137
|
+
import { AdsConsent } from 'react-native-google-mobile-ads';
|
|
235
138
|
|
|
236
|
-
|
|
237
|
-
requestNonPersonalizedAdsOnly: status === AdsConsentStatus.NON_PERSONALIZED,
|
|
238
|
-
});
|
|
139
|
+
AdsConsent.reset()
|
|
239
140
|
```
|
|
240
|
-
|
|
241
|
-
The requested ad URL via the SDK will send a request with an additional parameter `&npa=1`, which will return a
|
|
242
|
-
non-personalized ad.
|
|
243
|
-
|
|
244
|
-
### Troubleshooting
|
|
245
|
-
|
|
246
|
-
#### "Could not parse Event FE preflight response."
|
|
247
|
-
|
|
248
|
-
This is a common error which occurs on both Android & iOS when making a request to display a Google-rendered consent form. Unfortunately the reasoning for this error is generic, making it hard to debug. There are a number of steps to check which are usually the cause for this error:
|
|
249
|
-
|
|
250
|
-
- The AdMob App ID is incorrect: Ensure you have entered the correct ID into the `app.json` file under the `android_app_id` or `ios_app_id` key in the `react-native-google-mobile-ads` config.
|
|
251
|
-
- A publisher ID is incorrect: Ensure your entered publisher IDs are correct.
|
|
252
|
-
- The publisher ID needs to be available on the same account as your AdMob App ID.
|
|
253
|
-
- The user is outside of the EEA: If a user does not need to provide consent, the form request will error. Ensure you have checked the users status via `requestInfoUpdate`. If using an emulator, ensure you set a debug location via `setDebugGeography`.
|
|
254
|
-
- Your AdMob account is not valid:
|
|
255
|
-
- Your account is disabled: This can occur if Google notices you have duplicate accounts. They will email you about this, and block you from entering the dashboard.
|
|
256
|
-
- You have provided invalid payment information: If your account has no payment information set up, this seems to cause this error to trigger.
|
|
257
|
-
|
|
258
|
-
If you are still struggling to present the consent form, reach out to AdMob support to investigate your account status via https://support.google.com/admob
|
package/docs/index.mdx
CHANGED
|
@@ -93,7 +93,7 @@ mobileAds()
|
|
|
93
93
|
// manner suitable for users under the age of consent.
|
|
94
94
|
tagForUnderAgeOfConsent: true,
|
|
95
95
|
|
|
96
|
-
// An array of test device IDs to
|
|
96
|
+
// An array of test device IDs to allow.
|
|
97
97
|
testDeviceIdentifiers: ["EMULATOR"]
|
|
98
98
|
})
|
|
99
99
|
.then(() => {
|
|
@@ -128,9 +128,9 @@ If you are using mediation, you may wish to wait until the promise is settled be
|
|
|
128
128
|
Out of the box, AdMob does not handle any related regulations which you may need to enforce on your application.
|
|
129
129
|
It is up to the developer to implement and handle this on a user-by-user basis. You must consent to EEA users
|
|
130
130
|
being served both personalized and non-personalized adverts before showing them. For more information, see
|
|
131
|
-
[
|
|
131
|
+
[Obtaining Consent with the User Messaging Platform](https://developers.google.com/admob/ump/android/quick-start).
|
|
132
132
|
|
|
133
|
-
The AdMob module provides a `
|
|
133
|
+
The AdMob module provides a `AdsConsent` helper to help developers quickly implement consent flows within their application.
|
|
134
134
|
See the [European User Consent page](/european-user-consent) for full examples of how to integrate the helper into your application.
|
|
135
135
|
|
|
136
136
|
## Test ads
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Migrating to v5
|
|
2
|
+
|
|
3
|
+
## AdsConsent
|
|
4
|
+
|
|
5
|
+
Google replaced their Consent SDK with the User Messaging Platform (UMP) SDK, which supports the latest IAB standards and Apple's App Tracking Transparency requirements.
|
|
6
|
+
|
|
7
|
+
Please refer to the following links for more information:
|
|
8
|
+
|
|
9
|
+
- [User Messaging Platform](https://developers.google.com/admob/ump/ios/quick-start)
|
|
10
|
+
- [EU User Consent Policy](https://www.google.com/about/company/user-consent-policy/)
|
|
11
|
+
- [IAB Europe Transparency](https://iabeurope.eu/iab-europe-transparency-consent-framework-policies/)
|
|
12
|
+
- [App Tracking Transparency](https://developer.apple.com/documentation/apptrackingtransparency)
|
|
13
|
+
|
|
14
|
+
Previously it was possible to request the ad providers and to update the consent status. This is no longer the case.
|
|
15
|
+
Also, while the old Consent SDK provided information about user preferences, the new `AdsConsentStatus` only tells you if you should show the modal to a user or not.
|
|
16
|
+
You can read the `IABTCF_TCString` key from standardUserDefaults / SharedPreferences and decode it with a library like [@iabtcf/core](https://github.com/InteractiveAdvertisingBureau/iabtcf-es/tree/master/modules/core#iabtcfcore) though.
|
|
17
|
+
|
|
18
|
+
* `requestInfoUpdate` does now accept an optional `AdsConsentInfoOptions` object instead of expecting `publisherIds` and returns a changed `AdsConsentInfo` interface
|
|
19
|
+
* `showForm` does not expect any parameters any longer and now only returns the changed `AdsConsentStatus` as part of it's `AdsConsentFormResult` interface
|
|
20
|
+
* `getAdProviders`, `getStatus` and `setStatus` methods were removed without replacements
|
|
21
|
+
* `addTestDevices`, `setDebugGeography` and `setTagForUnderAgeOfConsent` methods were removed, but their functionality is available via `AdsConsentInfoOptions`
|
|
22
|
+
* the `user_tracking_usage_description` key is needed in your project's `app.json` if you want to handle Apple's App Tracking Transparency
|
|
23
|
+
|
|
24
|
+
```diff
|
|
25
|
+
{
|
|
26
|
+
"react-native-google-mobile-ads": {
|
|
27
|
+
"android_app_id": "ca-app-pub-xxxxxxxx~xxxxxxxx",
|
|
28
|
+
"ios_app_id": "ca-app-pub-xxxxxxxx~xxxxxxxx",
|
|
29
|
+
+ "user_tracking_usage_description": "This identifier will be used to deliver personalized ads to you."
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```diff
|
|
35
|
+
import { AdsConsent, AdsConsentStatus } from 'react-native-google-mobile-ads';
|
|
36
|
+
|
|
37
|
+
-const consentInfo = await AdsConsent.requestInfoUpdate(['pub-6189033257628123']);
|
|
38
|
+
+const consentInfo = await AdsConsent.requestInfoUpdate();
|
|
39
|
+
|
|
40
|
+
if (
|
|
41
|
+
- consentInfo.isRequestLocationInEeaOrUnknown &&
|
|
42
|
+
+ consentInfo.isConsentFormAvailable &&
|
|
43
|
+
- consentInfo.status === AdsConsentStatus.UNKNOWN
|
|
44
|
+
+ consentInfo.status === AdsConsentStatus.REQUIRED
|
|
45
|
+
) {
|
|
46
|
+
- const formResult = await AdsConsent.showForm({
|
|
47
|
+
- privacyPolicy: 'https://invertase.io/privacy-policy',
|
|
48
|
+
- withPersonalizedAds: true,
|
|
49
|
+
- withNonPersonalizedAds: true,
|
|
50
|
+
- withAdFree: true,
|
|
51
|
+
- });
|
|
52
|
+
+ const formResult = await AdsConsent.showForm()
|
|
53
|
+
}
|
|
54
|
+
```
|
package/docs.json
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
"sidebar": [
|
|
5
5
|
["Installation", "/"],
|
|
6
6
|
["Displaying Ads", "/displaying-ads"],
|
|
7
|
-
["European User Consent", "/european-user-consent"]
|
|
7
|
+
["European User Consent", "/european-user-consent"],
|
|
8
|
+
["Common Reasons For Ads Not Showing", "/common-reasons-for-ads-not-showing"],
|
|
9
|
+
["Migrating to v5", "/migrating-to-v5"]
|
|
8
10
|
]
|
|
9
11
|
}
|
|
@@ -43,9 +43,8 @@ RCT_EXPORT_METHOD(appOpenLoad
|
|
|
43
43
|
: (NSString *)adUnitId
|
|
44
44
|
: (NSDictionary *)adRequestOptions) {
|
|
45
45
|
self.appOpenAd = nil;
|
|
46
|
-
GADRequest *request = [GADRequest request];
|
|
47
46
|
[GADAppOpenAd loadWithAdUnitID:adUnitId
|
|
48
|
-
request:
|
|
47
|
+
request:[RNGoogleMobileAdsCommon buildAdRequest:adRequestOptions]
|
|
49
48
|
orientation:UIInterfaceOrientationPortrait
|
|
50
49
|
completionHandler:^(GADAppOpenAd *_Nullable appOpenAd, NSError *_Nullable error) {
|
|
51
50
|
if (error) {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
@interface RNGoogleMobileAdsCommon : NSObject
|
|
23
23
|
|
|
24
|
-
+ (
|
|
24
|
+
+ (GAMRequest *)buildAdRequest:(NSDictionary *)adRequestOptions;
|
|
25
25
|
|
|
26
26
|
+ (NSDictionary *)getCodeAndMessageFromAdError:(NSError *)error;
|
|
27
27
|
|
|
@@ -44,8 +44,8 @@ NSString *const GOOGLE_MOBILE_ADS_EVENT_REWARDED_EARNED_REWARD = @"rewarded_earn
|
|
|
44
44
|
|
|
45
45
|
@implementation RNGoogleMobileAdsCommon
|
|
46
46
|
|
|
47
|
-
+ (
|
|
48
|
-
|
|
47
|
+
+ (GAMRequest *)buildAdRequest:(NSDictionary *)adRequestOptions {
|
|
48
|
+
GAMRequest *request = [GAMRequest request];
|
|
49
49
|
NSMutableDictionary *extras = [@{} mutableCopy];
|
|
50
50
|
|
|
51
51
|
if (adRequestOptions[@"requestNonPersonalizedAdsOnly"] &&
|
|
@@ -83,6 +83,10 @@ NSString *const GOOGLE_MOBILE_ADS_EVENT_REWARDED_EARNED_REWARD = @"rewarded_earn
|
|
|
83
83
|
request.requestAgent = adRequestOptions[@"requestAgent"];
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
if (adRequestOptions[@"customTargeting"]) {
|
|
87
|
+
request.customTargeting = adRequestOptions[@"customTargeting"];
|
|
88
|
+
}
|
|
89
|
+
|
|
86
90
|
return request;
|
|
87
91
|
}
|
|
88
92
|
|
|
@@ -18,6 +18,9 @@
|
|
|
18
18
|
|
|
19
19
|
#import <React/RCTUtils.h>
|
|
20
20
|
|
|
21
|
+
#import <React/RCTConvert.h>
|
|
22
|
+
#include <UserMessagingPlatform/UserMessagingPlatform.h>
|
|
23
|
+
#import "RCTBridgeModule.h"
|
|
21
24
|
#import "RNGoogleMobileAdsConsentModule.h"
|
|
22
25
|
#import "common/RNSharedUtils.h"
|
|
23
26
|
|
|
@@ -34,146 +37,88 @@ RCT_EXPORT_MODULE();
|
|
|
34
37
|
#pragma mark -
|
|
35
38
|
#pragma mark Google Mobile Ads Methods
|
|
36
39
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
} mutableCopy]];
|
|
50
|
-
} else {
|
|
51
|
-
resolve(@{
|
|
52
|
-
@"status" : @(consentInformation.consentStatus),
|
|
53
|
-
@"isRequestLocationInEeaOrUnknown" : @(consentInformation.requestLocationInEEAOrUnknown),
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
[consentInformation requestConsentInfoUpdateForPublisherIdentifiers:publisherIds
|
|
59
|
-
completionHandler:completionHandler];
|
|
40
|
+
- (NSString *)getConsentStatusString:(UMPConsentStatus)consentStatus {
|
|
41
|
+
switch (consentStatus) {
|
|
42
|
+
case UMPConsentStatusRequired:
|
|
43
|
+
return @"REQUIRED";
|
|
44
|
+
case UMPConsentStatusNotRequired:
|
|
45
|
+
return @"NOT_REQUIRED";
|
|
46
|
+
case UMPConsentStatusObtained:
|
|
47
|
+
return @"OBTAINED";
|
|
48
|
+
case UMPConsentStatusUnknown:
|
|
49
|
+
default:
|
|
50
|
+
return @"UNKNOWN";
|
|
51
|
+
}
|
|
60
52
|
}
|
|
61
53
|
|
|
62
|
-
RCT_EXPORT_METHOD(
|
|
54
|
+
RCT_EXPORT_METHOD(requestInfoUpdate
|
|
63
55
|
: (NSDictionary *)options
|
|
64
56
|
: (RCTPromiseResolveBlock)resolve
|
|
65
57
|
: (RCTPromiseRejectBlock)reject) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
58
|
+
UMPRequestParameters *parameters = [[UMPRequestParameters alloc] init];
|
|
59
|
+
UMPDebugSettings *debugSettings = [[UMPDebugSettings alloc] init];
|
|
60
|
+
|
|
61
|
+
debugSettings.geography = [options[@"debugGeography"] integerValue] ?: UMPDebugGeographyDisabled;
|
|
62
|
+
debugSettings.testDeviceIdentifiers =
|
|
63
|
+
[options valueForKeyPath:@"testDeviceIdentifiers"] ?: [[NSMutableArray alloc] init];
|
|
64
|
+
|
|
65
|
+
parameters.debugSettings = debugSettings;
|
|
66
|
+
parameters.tagForUnderAgeOfConsent = [options[@"tagForUnderAgeOfConsent"] boolValue] ?: FALSE;
|
|
67
|
+
|
|
68
|
+
[UMPConsentInformation.sharedInstance
|
|
69
|
+
requestConsentInfoUpdateWithParameters:parameters
|
|
70
|
+
completionHandler:^(NSError *_Nullable error) {
|
|
71
|
+
if (error) {
|
|
72
|
+
[RNSharedUtils
|
|
73
|
+
rejectPromiseWithUserInfo:reject
|
|
74
|
+
userInfo:[@{
|
|
75
|
+
@"code" : @"consent-update-failed",
|
|
76
|
+
@"message" : error.localizedDescription,
|
|
77
|
+
} mutableCopy]];
|
|
78
|
+
} else {
|
|
79
|
+
resolve(@{
|
|
80
|
+
@"status" : [self
|
|
81
|
+
getConsentStatusString:UMPConsentInformation.sharedInstance
|
|
82
|
+
.consentStatus],
|
|
83
|
+
@"isConsentFormAvailable" :
|
|
84
|
+
@(UMPConsentInformation.sharedInstance.formStatus ==
|
|
85
|
+
UMPFormStatusAvailable)
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}];
|
|
89
|
+
}
|
|
88
90
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
+
RCT_EXPORT_METHOD(showForm : (RCTPromiseResolveBlock)resolve : (RCTPromiseRejectBlock)reject) {
|
|
92
|
+
[UMPConsentForm loadWithCompletionHandler:^(UMPConsentForm *form, NSError *loadError) {
|
|
93
|
+
if (loadError) {
|
|
91
94
|
[RNSharedUtils rejectPromiseWithUserInfo:reject
|
|
92
95
|
userInfo:[@{
|
|
93
96
|
@"code" : @"consent-form-error",
|
|
94
|
-
@"message" :
|
|
97
|
+
@"message" : loadError.localizedDescription,
|
|
95
98
|
} mutableCopy]];
|
|
96
99
|
} else {
|
|
97
|
-
[form
|
|
98
|
-
|
|
99
|
-
|
|
100
|
+
[form
|
|
101
|
+
presentFromViewController:[UIApplication sharedApplication]
|
|
102
|
+
.delegate.window.rootViewController
|
|
103
|
+
completionHandler:^(NSError *_Nullable dismissError) {
|
|
104
|
+
if (dismissError) {
|
|
105
|
+
[RNSharedUtils
|
|
106
|
+
rejectPromiseWithUserInfo:reject
|
|
107
|
+
userInfo:[@{
|
|
108
|
+
@"code" : @"consent-form-error",
|
|
109
|
+
@"message" : dismissError.localizedDescription,
|
|
110
|
+
} mutableCopy]];
|
|
111
|
+
} else {
|
|
112
|
+
resolve(@{
|
|
113
|
+
@"status" : [self getConsentStatusString:UMPConsentInformation
|
|
114
|
+
.sharedInstance.consentStatus],
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}];
|
|
100
118
|
}
|
|
101
119
|
}];
|
|
102
120
|
}
|
|
103
121
|
|
|
104
|
-
RCT_EXPORT_METHOD(
|
|
105
|
-
PACConsentInformation *consentInformation = [PACConsentInformation sharedInstance];
|
|
106
|
-
resolve(@(consentInformation.consentStatus));
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
RCT_EXPORT_METHOD(setStatus
|
|
110
|
-
: (nonnull NSNumber *)status
|
|
111
|
-
: (RCTPromiseResolveBlock)resolve
|
|
112
|
-
: (RCTPromiseRejectBlock)reject) {
|
|
113
|
-
PACConsentStatus consentStatus = PACConsentStatusUnknown;
|
|
114
|
-
|
|
115
|
-
if ([status integerValue] == [@0 integerValue]) {
|
|
116
|
-
consentStatus = PACConsentStatusUnknown;
|
|
117
|
-
} else if ([status integerValue] == [@1 integerValue]) {
|
|
118
|
-
consentStatus = PACConsentStatusNonPersonalized;
|
|
119
|
-
} else if ([status integerValue] == [@2 integerValue]) {
|
|
120
|
-
consentStatus = PACConsentStatusPersonalized;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
PACConsentInformation.sharedInstance.consentStatus = consentStatus;
|
|
124
|
-
resolve([NSNull null]);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
RCT_EXPORT_METHOD(getAdProviders
|
|
128
|
-
: (RCTPromiseResolveBlock)resolve
|
|
129
|
-
: (RCTPromiseRejectBlock)reject) {
|
|
130
|
-
NSArray *providers = PACConsentInformation.sharedInstance.adProviders;
|
|
131
|
-
NSMutableArray *formattedProviders = [[NSMutableArray alloc] init];
|
|
132
|
-
|
|
133
|
-
for (PACAdProvider *provider in providers) {
|
|
134
|
-
NSMutableDictionary *formattedProvider = [[NSMutableDictionary alloc] init];
|
|
135
|
-
formattedProvider[@"companyName"] = provider.name;
|
|
136
|
-
formattedProvider[@"companyId"] = [provider.identifier stringValue];
|
|
137
|
-
formattedProvider[@"privacyPolicyUrl"] = provider.privacyPolicyURL.absoluteString;
|
|
138
|
-
[formattedProviders addObject:formattedProvider];
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
resolve(formattedProviders);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
RCT_EXPORT_METHOD(setTagForUnderAgeOfConsent
|
|
145
|
-
: (BOOL)tag
|
|
146
|
-
: (RCTPromiseResolveBlock)resolve
|
|
147
|
-
: (RCTPromiseRejectBlock)reject) {
|
|
148
|
-
PACConsentInformation *consentInformation = [PACConsentInformation sharedInstance];
|
|
149
|
-
consentInformation.tagForUnderAgeOfConsent = tag;
|
|
150
|
-
resolve([NSNull null]);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
RCT_EXPORT_METHOD(setDebugGeography
|
|
154
|
-
: (nonnull NSNumber *)geography
|
|
155
|
-
: (RCTPromiseResolveBlock)resolve
|
|
156
|
-
: (RCTPromiseRejectBlock)reject) {
|
|
157
|
-
PACDebugGeography debugGeography = PACDebugGeographyDisabled;
|
|
158
|
-
|
|
159
|
-
if ([geography integerValue] == [@0 integerValue]) {
|
|
160
|
-
debugGeography = PACDebugGeographyDisabled;
|
|
161
|
-
} else if ([geography integerValue] == [@1 integerValue]) {
|
|
162
|
-
debugGeography = PACDebugGeographyEEA;
|
|
163
|
-
} else if ([geography integerValue] == [@2 integerValue]) {
|
|
164
|
-
debugGeography = PACDebugGeographyNotEEA;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
PACConsentInformation.sharedInstance.debugGeography = debugGeography;
|
|
168
|
-
resolve([NSNull null]);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
RCT_EXPORT_METHOD(addTestDevices
|
|
172
|
-
: (NSArray *)deviceIds
|
|
173
|
-
: (RCTPromiseResolveBlock)resolve
|
|
174
|
-
: (RCTPromiseRejectBlock)reject) {
|
|
175
|
-
PACConsentInformation.sharedInstance.debugIdentifiers = deviceIds;
|
|
176
|
-
resolve([NSNull null]);
|
|
177
|
-
}
|
|
122
|
+
RCT_EXPORT_METHOD(reset) { [UMPConsentInformation.sharedInstance reset]; }
|
|
178
123
|
|
|
179
124
|
@end
|
|
@@ -68,9 +68,8 @@ RCT_EXPORT_METHOD(interstitialLoad
|
|
|
68
68
|
: (nonnull NSNumber *)requestId
|
|
69
69
|
: (NSString *)adUnitId
|
|
70
70
|
: (NSDictionary *)adRequestOptions) {
|
|
71
|
-
GADRequest *request = [GADRequest request];
|
|
72
71
|
[GADInterstitialAd loadWithAdUnitID:adUnitId
|
|
73
|
-
request:
|
|
72
|
+
request:[RNGoogleMobileAdsCommon buildAdRequest:adRequestOptions]
|
|
74
73
|
completionHandler:^(GADInterstitialAd *ad, NSError *error) {
|
|
75
74
|
if (error) {
|
|
76
75
|
NSDictionary *codeAndMessage =
|