From 19e9379a80a3cfe597aacf7ce2e6bad49e82dcb9 Mon Sep 17 00:00:00 2001 From: Dylan Date: Wed, 4 Dec 2024 12:44:40 +0100 Subject: [PATCH] feat(ios): UMP consent SDK on new arch --- README.md | 2 +- .../RNGoogleMobileAdsConsentModule.h | 15 +- ...le.m => RNGoogleMobileAdsConsentModule.mm} | 141 ++++-- src/AdsConsent.ts | 38 +- src/AdsConsentDebugGeography.ts | 38 -- ...sConsentPrivacyOptionsRequirementStatus.ts | 36 -- src/AdsConsentStatus.ts | 41 -- src/index.ts | 12 +- .../modules/NativeConsentModule.ts} | 436 +++++++++++------- src/types/index.ts | 1 - 10 files changed, 416 insertions(+), 344 deletions(-) rename ios/RNGoogleMobileAds/{RNGoogleMobileAdsConsentModule.m => RNGoogleMobileAdsConsentModule.mm} (74%) delete mode 100644 src/AdsConsentDebugGeography.ts delete mode 100644 src/AdsConsentPrivacyOptionsRequirementStatus.ts delete mode 100644 src/AdsConsentStatus.ts rename src/{types/AdsConsent.interface.ts => specs/modules/NativeConsentModule.ts} (85%) diff --git a/README.md b/README.md index 3b8929b9..09245c8c 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ When using The New Architecture, some legacy code will still be used though. See | iOS | Mobile Ads SDK Methods (Turbo Native Module) | ✅ Complete | | iOS | Banners (Fabric Native Component) | ✅ Complete | | iOS | Full Screen Ads (Turbo Native Module) | ✅ Complete | -| iOS | User Messaging Platform (Turbo Native Module) | ⏳ To-Do | +| iOS | User Messaging Platform (Turbo Native Module) | ✅ Complete | | iOS | EventEmitter (Turbo Native Module) | ⏳ To-Do | | iOS | Revenue Precision Constants (Turbo Native Module) | ✅ Complete | | Android | Mobile Ads SDK Methods (Turbo Native Module) | ⏳ To-Do | diff --git a/ios/RNGoogleMobileAds/RNGoogleMobileAdsConsentModule.h b/ios/RNGoogleMobileAds/RNGoogleMobileAdsConsentModule.h index 365f60e8..f7bf1700 100644 --- a/ios/RNGoogleMobileAds/RNGoogleMobileAdsConsentModule.h +++ b/ios/RNGoogleMobileAds/RNGoogleMobileAdsConsentModule.h @@ -1,4 +1,3 @@ -// /** * Copyright (c) 2016-present Invertase Limited & Contributors * @@ -16,10 +15,22 @@ * */ +#if !TARGET_OS_MACCATALYST + #import -#import +#ifdef RCT_NEW_ARCH_ENABLED + +#import +@interface RNGoogleMobileAdsConsentModule : NSObject + +#else +#import @interface RNGoogleMobileAdsConsentModule : NSObject +#endif + @end + +#endif diff --git a/ios/RNGoogleMobileAds/RNGoogleMobileAdsConsentModule.m b/ios/RNGoogleMobileAds/RNGoogleMobileAdsConsentModule.mm similarity index 74% rename from ios/RNGoogleMobileAds/RNGoogleMobileAdsConsentModule.m rename to ios/RNGoogleMobileAds/RNGoogleMobileAdsConsentModule.mm index fb21793a..cb56dbf9 100644 --- a/ios/RNGoogleMobileAds/RNGoogleMobileAdsConsentModule.m +++ b/ios/RNGoogleMobileAds/RNGoogleMobileAdsConsentModule.mm @@ -22,7 +22,9 @@ #if !TARGET_OS_MACCATALYST #include #endif -#import "RCTBridgeModule.h" +#ifdef RCT_NEW_ARCH_ENABLED +#import "RNGoogleMobileAdsSpec.h" +#endif #import "RNGoogleMobileAdsConsentModule.h" #import "common/RNSharedUtils.h" @@ -92,7 +94,8 @@ - (NSDictionary *)getConsentInformation { UMPRequestParameters *parameters = [[UMPRequestParameters alloc] init]; UMPDebugSettings *debugSettings = [[UMPDebugSettings alloc] init]; - debugSettings.geography = [options[@"debugGeography"] integerValue] ?: UMPDebugGeographyDisabled; + debugSettings.geography = + (UMPDebugGeography)([options[@"debugGeography"] integerValue] ?: UMPDebugGeographyDisabled); debugSettings.testDeviceIdentifiers = [options valueForKeyPath:@"testDeviceIdentifiers"] ?: [[NSMutableArray alloc] init]; @@ -117,6 +120,105 @@ - (NSDictionary *)getConsentInformation { } RCT_EXPORT_METHOD(showForm : (RCTPromiseResolveBlock)resolve : (RCTPromiseRejectBlock)reject) { + [self showForm:resolve reject:reject]; +} + +RCT_EXPORT_METHOD(showPrivacyOptionsForm + : (RCTPromiseResolveBlock)resolve + : (RCTPromiseRejectBlock)reject) { + [self showPrivacyOptionsForm:resolve reject:reject]; +} + +RCT_EXPORT_METHOD(loadAndShowConsentFormIfRequired + : (RCTPromiseResolveBlock)resolve + : (RCTPromiseRejectBlock)reject) { + [self loadAndShowConsentFormIfRequired:resolve reject:reject]; +} + +RCT_EXPORT_METHOD(reset) { +#if !TARGET_OS_MACCATALYST + [UMPConsentInformation.sharedInstance reset]; +#endif +} + +RCT_EXPORT_METHOD(getConsentInfo + : (RCTPromiseResolveBlock)resolve + : (RCTPromiseRejectBlock)reject) { + [self getConsentInfo:resolve reject:reject]; +} + +RCT_EXPORT_METHOD(getTCString : (RCTPromiseResolveBlock)resolve : (RCTPromiseRejectBlock)reject) { + [self getTCString:resolve reject:reject]; +} + +RCT_EXPORT_METHOD(getGdprApplies + : (RCTPromiseResolveBlock)resolve + : (RCTPromiseRejectBlock)reject) { + [self getGdprApplies:resolve reject:reject]; +} + +RCT_EXPORT_METHOD(getPurposeConsents + : (RCTPromiseResolveBlock)resolve + : (RCTPromiseRejectBlock)reject) { + [self getPurposeConsents:resolve reject:reject]; +} + +RCT_EXPORT_METHOD(getPurposeLegitimateInterests + : (RCTPromiseResolveBlock)resolve + : (RCTPromiseRejectBlock)reject) { + [self getPurposeLegitimateInterests:resolve reject:reject]; +} + +#ifdef RCT_NEW_ARCH_ENABLED +- (std::shared_ptr)getTurboModule: + (const facebook::react::ObjCTurboModule::InitParams &)params { + return std::make_shared(params); +} +#endif + +#ifdef RCT_NEW_ARCH_ENABLED +- (void)requestInfoUpdate:(JS::NativeConsentModule::AdsConsentInfoOptions &)options + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject { +#if !TARGET_OS_MACCATALYST + UMPRequestParameters *parameters = [[UMPRequestParameters alloc] init]; + UMPDebugSettings *debugSettings = [[UMPDebugSettings alloc] init]; + + debugSettings.geography = + static_cast(options.debugGeography().value_or(UMPDebugGeographyDisabled)); + debugSettings.testDeviceIdentifiers = options.testDeviceIdentifiers().has_value() + ? ^{ + NSMutableArray *array = [[NSMutableArray alloc] init]; + FB::LazyVector identifiers = options.testDeviceIdentifiers().value(); + for (NSUInteger i = 0; i < identifiers.size(); i++) { + [array addObject:identifiers[i]]; // Direct access by index + } + return array; + }() + : [[NSMutableArray alloc] init]; + + parameters.debugSettings = debugSettings; + parameters.tagForUnderAgeOfConsent = options.tagForUnderAgeOfConsent().value_or(FALSE); + + [UMPConsentInformation.sharedInstance + requestConsentInfoUpdateWithParameters:parameters + completionHandler:^(NSError *_Nullable error) { + if (error) { + [RNSharedUtils + rejectPromiseWithUserInfo:reject + userInfo:[@{ + @"code" : @"consent-update-failed", + @"message" : error.localizedDescription, + } mutableCopy]]; + } else { + resolve([self getConsentInformation]); + } + }]; +#endif +} +#endif + +- (void)showForm:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { #if !TARGET_OS_MACCATALYST [UMPConsentForm loadWithCompletionHandler:^(UMPConsentForm *form, NSError *loadError) { if (loadError) { @@ -145,9 +247,8 @@ - (NSDictionary *)getConsentInformation { #endif } -RCT_EXPORT_METHOD(showPrivacyOptionsForm - : (RCTPromiseResolveBlock)resolve - : (RCTPromiseRejectBlock)reject) { +- (void)showPrivacyOptionsForm:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject { #if !TARGET_OS_MACCATALYST [UMPConsentForm presentPrivacyOptionsFormFromViewController:[UIApplication sharedApplication] @@ -168,9 +269,8 @@ - (NSDictionary *)getConsentInformation { #endif } -RCT_EXPORT_METHOD(loadAndShowConsentFormIfRequired - : (RCTPromiseResolveBlock)resolve - : (RCTPromiseRejectBlock)reject) { +- (void)loadAndShowConsentFormIfRequired:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject { #if !TARGET_OS_MACCATALYST [UMPConsentForm loadAndPresentIfRequiredFromViewController:[UIApplication sharedApplication] @@ -191,21 +291,13 @@ - (NSDictionary *)getConsentInformation { #endif } -RCT_EXPORT_METHOD(reset) { -#if !TARGET_OS_MACCATALYST - [UMPConsentInformation.sharedInstance reset]; -#endif -} - -RCT_EXPORT_METHOD(getConsentInfo - : (RCTPromiseResolveBlock)resolve - : (RCTPromiseRejectBlock)reject) { +- (void)getConsentInfo:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { #if !TARGET_OS_MACCATALYST resolve([self getConsentInformation]); #endif } -RCT_EXPORT_METHOD(getTCString : (RCTPromiseResolveBlock)resolve : (RCTPromiseRejectBlock)reject) { +- (void)getTCString:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { @try { // https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#in-app-details NSString *tcString = [[NSUserDefaults standardUserDefaults] objectForKey:@"IABTCF_TCString"]; @@ -219,9 +311,7 @@ - (NSDictionary *)getConsentInformation { } } -RCT_EXPORT_METHOD(getGdprApplies - : (RCTPromiseResolveBlock)resolve - : (RCTPromiseRejectBlock)reject) { +- (void)getGdprApplies:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { @try { BOOL gdprApplies = [[NSUserDefaults standardUserDefaults] boolForKey:@"IABTCF_gdprApplies"]; resolve(@(gdprApplies)); @@ -234,9 +324,7 @@ - (NSDictionary *)getConsentInformation { } } -RCT_EXPORT_METHOD(getPurposeConsents - : (RCTPromiseResolveBlock)resolve - : (RCTPromiseRejectBlock)reject) { +- (void)getPurposeConsents:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { @try { NSString *purposeConsents = [[NSUserDefaults standardUserDefaults] stringForKey:@"IABTCF_PurposeConsents"]; @@ -250,9 +338,8 @@ - (NSDictionary *)getConsentInformation { } } -RCT_EXPORT_METHOD(getPurposeLegitimateInterests - : (RCTPromiseResolveBlock)resolve - : (RCTPromiseRejectBlock)reject) { +- (void)getPurposeLegitimateInterests:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject { @try { NSString *purposeLegitimateInterests = [[NSUserDefaults standardUserDefaults] stringForKey:@"IABTCF_PurposeLegitimateInterests"]; diff --git a/src/AdsConsent.ts b/src/AdsConsent.ts index 87ccfb09..869aa045 100644 --- a/src/AdsConsent.ts +++ b/src/AdsConsent.ts @@ -16,22 +16,18 @@ */ import { TCModel, TCString } from '@iabtcf/core'; -import { NativeModules } from 'react-native'; -import { AdsConsentDebugGeography } from './AdsConsentDebugGeography'; import { AdsConsentPurposes } from './AdsConsentPurposes'; import { AdsConsentSpecialFeatures } from './AdsConsentSpecialFeatures'; import { isPropertySet, isArray, isBoolean, isObject, isString } from './common'; import { - AdsConsentInfo, - AdsConsentInfoOptions, AdsConsentInterface, - AdsConsentUserChoices, -} from './types/AdsConsent.interface'; - -const native = NativeModules.RNGoogleMobileAdsConsentModule; + AdsConsentDebugGeography, + AdsConsentInfoOptions, +} from './specs/modules/NativeConsentModule'; +import native from './specs/modules/NativeConsentModule'; export const AdsConsent: AdsConsentInterface = { - requestInfoUpdate(options: AdsConsentInfoOptions = {}): Promise { + requestInfoUpdate(options: AdsConsentInfoOptions = {}) { if (!isObject(options)) { throw new Error("AdsConsent.requestInfoUpdate(*) 'options' expected an object value."); } @@ -75,53 +71,53 @@ export const AdsConsent: AdsConsentInterface = { return native.requestInfoUpdate(options); }, - showForm(): Promise { + showForm() { return native.showForm(); }, - showPrivacyOptionsForm(): Promise { + showPrivacyOptionsForm() { return native.showPrivacyOptionsForm(); }, - loadAndShowConsentFormIfRequired(): Promise { + loadAndShowConsentFormIfRequired() { return native.loadAndShowConsentFormIfRequired(); }, - getConsentInfo(): Promise { + getConsentInfo() { return native.getConsentInfo(); }, - async gatherConsent(options: AdsConsentInfoOptions = {}): Promise { + async gatherConsent(options: AdsConsentInfoOptions = {}) { await this.requestInfoUpdate(options); return this.loadAndShowConsentFormIfRequired(); }, - reset(): void { + reset() { return native.reset(); }, - getTCString(): Promise { + getTCString() { return native.getTCString(); }, - async getTCModel(): Promise { + async getTCModel() { const tcString = await native.getTCString(); return TCString.decode(tcString); }, - getGdprApplies(): Promise { + getGdprApplies() { return native.getGdprApplies(); }, - getPurposeConsents(): Promise { + getPurposeConsents() { return native.getPurposeConsents(); }, - getPurposeLegitimateInterests(): Promise { + getPurposeLegitimateInterests() { return native.getPurposeLegitimateInterests(); }, - async getUserChoices(): Promise { + async getUserChoices() { const tcString = await native.getTCString(); let tcModel: TCModel; diff --git a/src/AdsConsentDebugGeography.ts b/src/AdsConsentDebugGeography.ts deleted file mode 100644 index 0417a1a2..00000000 --- a/src/AdsConsentDebugGeography.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2016-present Invertase Limited & Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this library except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/** - * AdsConsentDebugGeography enum. - * - * Used to set a mock location when testing the `AdsConsent` helper. - */ -export enum AdsConsentDebugGeography { - /** - * Disable any debug geography. - */ - DISABLED = 0, - - /** - * Sets the location to within the EEA. - */ - EEA = 1, - - /** - * Sets the location to outside of the EEA. - */ - NOT_EEA = 2, -} diff --git a/src/AdsConsentPrivacyOptionsRequirementStatus.ts b/src/AdsConsentPrivacyOptionsRequirementStatus.ts deleted file mode 100644 index 98e75211..00000000 --- a/src/AdsConsentPrivacyOptionsRequirementStatus.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2016-present Invertase Limited & Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this library except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/** - * AdsConsentPrivacyOptionsRequirementStatus enum. - */ -export enum AdsConsentPrivacyOptionsRequirementStatus { - /** - * Unknown consent status, AdsConsent.requestInfoUpdate needs to be called to update it. - */ - UNKNOWN = 'UNKNOWN', - - /** - * User consent required but not yet obtained. - */ - REQUIRED = 'REQUIRED', - - /** - * User consent not required. - */ - NOT_REQUIRED = 'NOT_REQUIRED', -} diff --git a/src/AdsConsentStatus.ts b/src/AdsConsentStatus.ts deleted file mode 100644 index a404a53a..00000000 --- a/src/AdsConsentStatus.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2016-present Invertase Limited & Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this library except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/** - * AdsConsentStatus enum. - */ -export enum AdsConsentStatus { - /** - * Unknown consent status, AdsConsent.requestInfoUpdate needs to be called to update it. - */ - UNKNOWN = 'UNKNOWN', - - /** - * User consent required but not yet obtained. - */ - REQUIRED = 'REQUIRED', - - /** - * User consent not required. - */ - NOT_REQUIRED = 'NOT_REQUIRED', - - /** - * User consent already obtained. - */ - OBTAINED = 'OBTAINED', -} diff --git a/src/index.ts b/src/index.ts index 22aca3f2..f1580c03 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,11 +21,17 @@ import { version } from './version'; export const SDK_VERSION = version; export { default, MobileAds } from './MobileAds'; -export { AdsConsentDebugGeography } from './AdsConsentDebugGeography'; +export { + AdsConsentDebugGeography, + AdsConsentInfo, + AdsConsentInfoOptions, + AdsConsentInterface, + AdsConsentPrivacyOptionsRequirementStatus, + AdsConsentStatus, + AdsConsentUserChoices, +} from './specs/modules/NativeConsentModule'; export { AdsConsentPurposes } from './AdsConsentPurposes'; export { AdsConsentSpecialFeatures } from './AdsConsentSpecialFeatures'; -export { AdsConsentStatus } from './AdsConsentStatus'; -export { AdsConsentPrivacyOptionsRequirementStatus } from './AdsConsentPrivacyOptionsRequirementStatus'; export { MaxAdContentRating } from './MaxAdContentRating'; export { TestIds } from './TestIds'; export { AdEventType } from './AdEventType'; diff --git a/src/types/AdsConsent.interface.ts b/src/specs/modules/NativeConsentModule.ts similarity index 85% rename from src/types/AdsConsent.interface.ts rename to src/specs/modules/NativeConsentModule.ts index bf9f762e..7a01a477 100644 --- a/src/types/AdsConsent.interface.ts +++ b/src/specs/modules/NativeConsentModule.ts @@ -1,212 +1,88 @@ -import { TCModel } from '@iabtcf/core'; -import { AdsConsentDebugGeography } from '../AdsConsentDebugGeography'; -import { AdsConsentStatus } from '../AdsConsentStatus'; -import { AdsConsentPrivacyOptionsRequirementStatus } from '../AdsConsentPrivacyOptionsRequirementStatus'; - /** - * Under the Google [EU User Consent Policy](https://www.google.com/about/company/consentstaging.html), you must make certain disclosures to your users in the European Economic Area (EEA) - * and obtain their consent to use cookies or other local storage, where legally required, and to use personal data - * (such as AdID) to serve ads. This policy reflects the requirements of the EU ePrivacy Directive and the - * General Data Protection Regulation (GDPR). + * Copyright (c) 2016-present Invertase Limited & Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this library except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 * - * It is recommended that you determine the status of a user's consent at every app launch. The user consent status is held - * on the device until a condition changes which requires the user to consent again, such as a change in publishers. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. * - * For more information, see [here](https://developers.google.com/admob/ump/android/quick-start#delay_app_measurement_optional). */ -export interface AdsConsentInterface { - /** - * Requests user consent information. - * - * The response from this method provides information about consent form availability and consent status. - * - * #### Example - * - * ```js - * import { AdsConsent } from 'react-native-google-mobile-ads'; - * - * const consentInfo = await AdsConsent.requestInfoUpdate(); - * console.log('A consent form is available:', consentInfo.isConsentFormAvailable); - * console.log('User consent status:', consentInfo.status); - * ``` - * @param options An AdsConsentInfoOptions interface. - */ - requestInfoUpdate(options?: AdsConsentInfoOptions): Promise; +import { TurboModule, TurboModuleRegistry } from 'react-native'; +import { TCModel } from '@iabtcf/core'; +/** + * AdsConsentDebugGeography enum. + * + * Used to set a mock location when testing the `AdsConsent` helper. + */ +export enum AdsConsentDebugGeography { /** - * Shows a Google-rendered user consent form. - * - * #### Example - * - * ```js - * import { AdsConsent, AdsConsentStatus } from 'react-native-google-mobile-ads'; - * - * async function requestConsent() { - * const consentInfo = await AdsConsent.requestInfoUpdate(); - * - * // Check if user requires consent - * if ( - * consentInfo.isConsentFormAvailable && - * (consentInfo.status === AdsConsentStatus.UNKNOWN || - * consentInfo.status === AdsConsentStatus.REQUIRED)) { - * // Show a Google-rendered form - * const formResult = await AdsConsent.showForm(); - * - * console.log('User consent obtained: ', formResult.status === AdsConsentStatus.OBTAINED); - * } - * } - * - * ``` + * Disable any debug geography. */ - showForm(): Promise; + DISABLED = 0, /** - * Presents a privacy options form if privacyOptionsRequirementStatus is required. + * Sets the location to within the EEA. */ - showPrivacyOptionsForm(): Promise; + EEA = 1, /** - * Loads a consent form and immediately presents it if consentStatus is required. - * - * This method is intended for the use case of showing a form if needed when the app starts. + * Sets the location to outside of the EEA. */ - loadAndShowConsentFormIfRequired(): Promise; + NOT_EEA = 2, +} +/** + * AdsConsentStatus enum. + */ +export enum AdsConsentStatus { /** - * Returns the UMP Consent Information from the last known session. - * - * #### Example - * - * ```js - * import { AdsConsent } from '@invertase/react-native-google-ads'; - * - * const consentInfo = await AdsConsent.getConsentInfo(); - * ``` + * Unknown consent status, AdsConsent.requestInfoUpdate needs to be called to update it. */ - getConsentInfo(): Promise; + UNKNOWN = 'UNKNOWN', /** - * Helper method to call the UMP SDK methods to request consent information and load/present a - * consent form if necessary. + * User consent required but not yet obtained. */ - gatherConsent(): Promise; + REQUIRED = 'REQUIRED', /** - * Returns the value stored under the `IABTCF_TCString` key - * in NSUserDefaults (iOS) / SharedPreferences (Android) as - * defined by the IAB Europe Transparency & Consent Framework. - * - * More information available here: - * https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#in-app-details - * - * #### Example - * - * ```js - * import { AdsConsent } from '@invertase/react-native-google-ads'; - * - * const tcString = await AdsConsent.getTCString(); - * ``` + * User consent not required. */ - getTCString(): Promise; + NOT_REQUIRED = 'NOT_REQUIRED', /** - * Returns the TC Model of the saved IAB TCF 2.0 String. - * - * #### Example - * - * ```js - * import { AdsConsent } from '@invertase/react-native-google-ads'; - * - * const tcModel = await AdsConsent.getTCModel(); - * ``` + * User consent already obtained. */ - getTCModel(): Promise; - - /** - * Returns the value stored under the `IABTCF_gdprApplies` key - * in NSUserDefaults (iOS) / SharedPreferences (Android) as - * defined by the IAB Europe Transparency & Consent Framework. - * - * More information available here: - * https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#in-app-details - * - * #### Example - * - * ```js - * import { AdsConsent } from '@invertase/react-native-google-ads'; - * - * await AdsConsent.requestInfoUpdate(); - * const gdprApplies = await AdsConsent.getGdprApplies(); - * ``` - */ - getGdprApplies(): Promise; - - /** - * Returns the value stored under the `IABTCF_PurposeConsents` key - * in NSUserDefaults (iOS) / SharedPreferences (Android) as - * defined by the IAB Europe Transparency & Consent Framework. - * - * More information available here: - * https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#in-app-details - * - * #### Example - * - * ```js - * import { AdsConsent } from '@invertase/react-native-google-ads'; - * - * await AdsConsent.requestInfoUpdate(); - * const purposeConsents = await AdsConsent.getPurposeConsents(); - * const hasConsentForPurposeOne = purposeConsents.startsWith("1"); - * ``` - */ - getPurposeConsents(): Promise; + OBTAINED = 'OBTAINED', +} +/** + * AdsConsentPrivacyOptionsRequirementStatus enum. + */ +export enum AdsConsentPrivacyOptionsRequirementStatus { /** - * Returns the value stored under the `IABTCF_PurposeLegitimateInterests` key - * in NSUserDefaults (iOS) / SharedPreferences (Android) as - * defined by the IAB Europe Transparency & Consent Framework. - * - * More information available here: - * https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#in-app-details - * - * #### Example - * - * ```js - * import { AdsConsent } from '@invertase/react-native-google-ads'; - * - * await AdsConsent.requestInfoUpdate(); - * const purposeLegitimateInterests = await AdsConsent.getPurposeLegitimateInterests(); - * const hasLegitimateInterestForPurposeTwo = purposeLegitimateInterests.split("")[1] === "1"; - * ``` + * Unknown consent status, AdsConsent.requestInfoUpdate needs to be called to update it. */ - getPurposeLegitimateInterests(): Promise; + UNKNOWN = 'UNKNOWN', /** - * Provides information about a user's consent choices. - * - * #### Example - * - * ```js - * import { AdsConsent } from '@invertase/react-native-google-ads'; - * - * const { storeAndAccessInformationOnDevice } = await AdsConsent.getUserChoices(); - * ``` + * User consent required but not yet obtained. */ - getUserChoices(): Promise; + REQUIRED = 'REQUIRED', /** - * Resets the UMP SDK state. - * - * #### Example - * - * ```js - * import { AdsConsent } from '@invertase/react-native-google-ads'; - * - * AdsConsent.reset(); - * ``` + * User consent not required. */ - reset(): void; + NOT_REQUIRED = 'NOT_REQUIRED', } /** @@ -433,3 +309,215 @@ export interface AdsConsentUserChoices { */ usePreciseGeolocationData: boolean; } + +export interface AdsConsentInterface { + /** + * Requests user consent information. + * + * The response from this method provides information about consent form availability and consent status. + * + * #### Example + * + * ```js + * import { AdsConsent } from 'react-native-google-mobile-ads'; + * + * const consentInfo = await AdsConsent.requestInfoUpdate(); + * console.log('A consent form is available:', consentInfo.isConsentFormAvailable); + * console.log('User consent status:', consentInfo.status); + * ``` + * @param options An AdsConsentInfoOptions interface. + */ + requestInfoUpdate(options?: AdsConsentInfoOptions): Promise; + + /** + * Shows a Google-rendered user consent form. + * + * #### Example + * + * ```js + * import { AdsConsent, AdsConsentStatus } from 'react-native-google-mobile-ads'; + * + * async function requestConsent() { + * const consentInfo = await AdsConsent.requestInfoUpdate(); + * + * // Check if user requires consent + * if ( + * consentInfo.isConsentFormAvailable && + * (consentInfo.status === AdsConsentStatus.UNKNOWN || + * consentInfo.status === AdsConsentStatus.REQUIRED)) { + * // Show a Google-rendered form + * const formResult = await AdsConsent.showForm(); + * + * console.log('User consent obtained: ', formResult.status === AdsConsentStatus.OBTAINED); + * } + * } + * + * ``` + */ + showForm(): Promise; + + /** + * Presents a privacy options form if privacyOptionsRequirementStatus is required. + */ + showPrivacyOptionsForm(): Promise; + + /** + * Loads a consent form and immediately presents it if consentStatus is required. + * + * This method is intended for the use case of showing a form if needed when the app starts. + */ + loadAndShowConsentFormIfRequired(): Promise; + + /** + * Returns the UMP Consent Information from the last known session. + * + * #### Example + * + * ```js + * import { AdsConsent } from '@invertase/react-native-google-ads'; + * + * const consentInfo = await AdsConsent.getConsentInfo(); + * ``` + */ + getConsentInfo(): Promise; + + /** + * Helper method to call the UMP SDK methods to request consent information and load/present a + * consent form if necessary. + */ + gatherConsent(): Promise; + + /** + * Returns the value stored under the `IABTCF_TCString` key + * in NSUserDefaults (iOS) / SharedPreferences (Android) as + * defined by the IAB Europe Transparency & Consent Framework. + * + * More information available here: + * https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#in-app-details + * + * #### Example + * + * ```js + * import { AdsConsent } from '@invertase/react-native-google-ads'; + * + * const tcString = await AdsConsent.getTCString(); + * ``` + */ + getTCString(): Promise; + + /** + * Returns the TC Model of the saved IAB TCF 2.0 String. + * + * #### Example + * + * ```js + * import { AdsConsent } from '@invertase/react-native-google-ads'; + * + * const tcModel = await AdsConsent.getTCModel(); + * ``` + */ + getTCModel(): Promise; + + /** + * Returns the value stored under the `IABTCF_gdprApplies` key + * in NSUserDefaults (iOS) / SharedPreferences (Android) as + * defined by the IAB Europe Transparency & Consent Framework. + * + * More information available here: + * https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#in-app-details + * + * #### Example + * + * ```js + * import { AdsConsent } from '@invertase/react-native-google-ads'; + * + * await AdsConsent.requestInfoUpdate(); + * const gdprApplies = await AdsConsent.getGdprApplies(); + * ``` + */ + getGdprApplies(): Promise; + + /** + * Returns the value stored under the `IABTCF_PurposeConsents` key + * in NSUserDefaults (iOS) / SharedPreferences (Android) as + * defined by the IAB Europe Transparency & Consent Framework. + * + * More information available here: + * https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#in-app-details + * + * #### Example + * + * ```js + * import { AdsConsent } from '@invertase/react-native-google-ads'; + * + * await AdsConsent.requestInfoUpdate(); + * const purposeConsents = await AdsConsent.getPurposeConsents(); + * const hasConsentForPurposeOne = purposeConsents.startsWith("1"); + * ``` + */ + getPurposeConsents(): Promise; + + /** + * Returns the value stored under the `IABTCF_PurposeLegitimateInterests` key + * in NSUserDefaults (iOS) / SharedPreferences (Android) as + * defined by the IAB Europe Transparency & Consent Framework. + * + * More information available here: + * https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/TCFv2/IAB%20Tech%20Lab%20-%20CMP%20API%20v2.md#in-app-details + * + * #### Example + * + * ```js + * import { AdsConsent } from '@invertase/react-native-google-ads'; + * + * await AdsConsent.requestInfoUpdate(); + * const purposeLegitimateInterests = await AdsConsent.getPurposeLegitimateInterests(); + * const hasLegitimateInterestForPurposeTwo = purposeLegitimateInterests.split("")[1] === "1"; + * ``` + */ + getPurposeLegitimateInterests(): Promise; + + /** + * Provides information about a user's consent choices. + * + * #### Example + * + * ```js + * import { AdsConsent } from '@invertase/react-native-google-ads'; + * + * const { storeAndAccessInformationOnDevice } = await AdsConsent.getUserChoices(); + * ``` + */ + getUserChoices(): Promise; + + /** + * Resets the UMP SDK state. + * + * #### Example + * + * ```js + * import { AdsConsent } from '@invertase/react-native-google-ads'; + * + * AdsConsent.reset(); + * ``` + */ + reset(): void; +} + +export interface Spec extends TurboModule { + requestInfoUpdate(options?: AdsConsentInfoOptions): Promise; + showForm(): Promise; + showPrivacyOptionsForm(): Promise; + loadAndShowConsentFormIfRequired(): Promise; + getConsentInfo(): Promise; + gatherConsent(): Promise; + getTCString(): Promise; + getTCModel(): Promise; + getGdprApplies(): Promise; + getPurposeConsents(): Promise; + getPurposeLegitimateInterests(): Promise; + getUserChoices(): Promise; + reset(): void; +} + +export default TurboModuleRegistry.getEnforcing('RNGoogleMobileAdsConsentModule'); diff --git a/src/types/index.ts b/src/types/index.ts index bbbe55d5..deeec585 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -18,7 +18,6 @@ export * from './AdapterStatus'; export * from './AdEventListener'; export * from './AdEventsListener'; -export * from './AdsConsent.interface'; export * from './AdShowOptions'; export * from './AdStates'; export * from './BannerAdProps';