From 5542675d877dd2dad662627d84c044c3c4606d3d Mon Sep 17 00:00:00 2001 From: Chris Frewin Date: Wed, 19 Oct 2022 09:09:34 +0200 Subject: [PATCH 01/11] Fix for RN >= 0.69.X --- react-native.config.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/react-native.config.js b/react-native.config.js index dfbe81b4..9fe432f3 100644 --- a/react-native.config.js +++ b/react-native.config.js @@ -3,11 +3,10 @@ const path = require('path'); module.exports = { dependency: { platforms: { - ios: { podspecPath: path.join(__dirname, 'react-native-appsflyer.podspec') }, android: { packageImportPath: 'import com.appsflyer.reactnative.RNAppsFlyerPackage;', packageInstance: 'new RNAppsFlyerPackage()', }, }, }, -}; \ No newline at end of file +}; From 9e0aaa23dd3c9412307ac82aeee59f0820fb8a97 Mon Sep 17 00:00:00 2001 From: Seth Rutner Date: Mon, 23 Oct 2023 22:35:57 -0700 Subject: [PATCH 02/11] Fix typo im RN_UserInvite.md --- Docs/RN_UserInvite.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Docs/RN_UserInvite.md b/Docs/RN_UserInvite.md index 50c725b8..dbb7750e 100644 --- a/Docs/RN_UserInvite.md +++ b/Docs/RN_UserInvite.md @@ -36,7 +36,7 @@ The link that is generated for the user invite will use this OneLink ID as the b ```javascript -// set the tamplate ID before you generate a link. Without it UserInvite won't work. +// set the template ID before you generate a link. Without it UserInvite won't work. appsFlyer.setAppInviteOneLinkID('scVs', null); // set the user invite params From aca472b3b3743cab0de42812d07ea719d6bd8d63 Mon Sep 17 00:00:00 2001 From: Amit kremer Date: Wed, 14 Feb 2024 16:06:39 +0200 Subject: [PATCH 03/11] version 6.13.0, DMA feature --- android/build.gradle | 2 +- .../reactnative/RNAppsFlyerConstants.java | 2 +- .../reactnative/RNAppsFlyerModule.java | 22 ++++++++- index.d.ts | 3 ++ index.js | 20 ++++++++ ios/AppsFlyerConsent.h | 26 ++++++++++ ios/AppsFlyerLib.h | 49 +++++++++++++++---- ios/AppsFlyerLinkGenerator.h | 2 +- ios/RNAppsFlyer.h | 2 +- ios/RNAppsFlyer.m | 19 +++++++ react-native-appsflyer.podspec | 4 +- 11 files changed, 134 insertions(+), 17 deletions(-) create mode 100644 ios/AppsFlyerConsent.h diff --git a/android/build.gradle b/android/build.gradle index 2372c120..fd85b614 100755 --- a/android/build.gradle +++ b/android/build.gradle @@ -54,5 +54,5 @@ repositories { dependencies { implementation "com.facebook.react:react-native:${safeExtGet('reactNativeVersion', '+')}" implementation "com.android.installreferrer:installreferrer:${safeExtGet('installReferrerVersion', '2.1')}" - api "com.appsflyer:af-android-sdk:${safeExtGet('appsflyerVersion', '6.12.2')}" + api "com.appsflyer:af-android-sdk:${safeExtGet('appsflyerVersion', '6.13.0')}" } diff --git a/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerConstants.java b/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerConstants.java index eff5d9f1..2802506b 100755 --- a/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerConstants.java +++ b/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerConstants.java @@ -6,7 +6,7 @@ public class RNAppsFlyerConstants { - final static String PLUGIN_VERSION = "6.12.2"; + final static String PLUGIN_VERSION = "6.13.0"; final static String NO_DEVKEY_FOUND = "No 'devKey' found or its empty"; final static String UNKNOWN_ERROR = "AF Unknown Error"; final static String SUCCESS = "Success"; diff --git a/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerModule.java b/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerModule.java index c607a1a5..f60fa1bb 100755 --- a/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerModule.java +++ b/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerModule.java @@ -815,7 +815,27 @@ public void performOnDeepLinking() { Log.d("AppsFlyer", "performOnDeepLinking: activity is null!"); } } - + + @ReactMethod + public void enableTCFDataCollection(Boolean enabled) { + AppsFlyerLib.getInstance().enableTCFDataCollection(enabled); + } + + @ReactMethod + public void setConsentData(ReadableMap consentData) { + JSONObject JSONConsentData = RNUtil.readableMapToJson(consentData); + boolean hasConsentForDataUsage = JSONConsentData.optBoolean("hasConsentForDataUsage"); + boolean hasConsentForAdsPersonalization = JSONConsentData.optBoolean("hasConsentForAdsPersonalization"); + AppsFlyerConsent consentObject = AppsFlyerConsent.forGDPRUser(hasConsentForDataUsage, hasConsentForAdsPersonalization); + AppsFlyerLib.getInstance().setConsentData(consentObject); + } + + @ReactMethod + public void setNonGDPRUser() { + AppsFlyerConsent consentData = AppsFlyerConsent.forNonGDPRUser(); + AppsFlyerLib.getInstance().setConsentData(consentData); + } + @ReactMethod public void addListener(String eventName) { // Keep: Required for RN built in Event Emitter Calls. diff --git a/index.d.ts b/index.d.ts index bddce131..9a61d51c 100644 --- a/index.d.ts +++ b/index.d.ts @@ -151,6 +151,9 @@ declare module "react-native-appsflyer" { setPartnerData(partnerId: string, partnerData: object): void appendParametersToDeepLinkingURL(contains: string, parameters: object): void startSdk(): void + enableTCFDataCollection(enabled: boolean): void + setConsentData(consentData: object): void + setNonGDPRUser(): void /** * For iOS Only diff --git a/index.js b/index.js index 93e2e0d2..b1c78d4e 100755 --- a/index.js +++ b/index.js @@ -625,6 +625,26 @@ appsFlyer.performOnDeepLinking = () => { return RNAppsFlyer.performOnDeepLinking(); }; +/** + * instruct the SDK to collect the TCF data from the device. + * @param enabled: if the sdk should collect the TCF data. true/false + */ +appsFlyer.enableTCFDataCollection= (enabled) => { + return RNAppsFlyer.enableTCFDataCollection(enabled); +} + +/** + * If your app does not use a CMP compatible with TCF v2.2, use the SDK API detailed below to provide the consent data directly to the SDK. + * @param consentData: object with 2 properties: hasConsentForDataUsage and hasConsentForAdsPersonalization. + */ +appsFlyer.setConsentData = (consentData) => { + return RNAppsFlyer.setConsentData(consentData); +} + +appsFlyer.setNonGDPRUser = () => { + return RNAppsFlyer.setNonGDPRUser(); +} + function AFParseJSONException(_message, _data) { this.message = _message; this.data = _data; diff --git a/ios/AppsFlyerConsent.h b/ios/AppsFlyerConsent.h new file mode 100644 index 00000000..564912a9 --- /dev/null +++ b/ios/AppsFlyerConsent.h @@ -0,0 +1,26 @@ +// +// AppsFlyerConsent.h +// AppsFlyerLib +// +// Created by Veronica Belyakov on 14/01/2024. +// +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface AppsFlyerConsent : NSObject + +@property (nonatomic, readonly, assign) BOOL isUserSubjectToGDPR; +@property (nonatomic, readonly, assign) BOOL hasConsentForDataUsage; +@property (nonatomic, readonly, assign) BOOL hasConsentForAdsPersonalization; + +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)new NS_UNAVAILABLE; + +- (instancetype)initForGDPRUserWithHasConsentForDataUsage:(BOOL)hasConsentForDataUsage + hasConsentForAdsPersonalization:(BOOL)hasConsentForAdsPersonalization NS_DESIGNATED_INITIALIZER; +- (instancetype)initNonGDPRUser NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/ios/AppsFlyerLib.h b/ios/AppsFlyerLib.h index 0b900a92..34ee5f01 100644 --- a/ios/AppsFlyerLib.h +++ b/ios/AppsFlyerLib.h @@ -2,7 +2,7 @@ // AppsFlyerLib.h // AppsFlyerLib // -// AppsFlyer iOS SDK 6.10.1 (109) +// AppsFlyer iOS SDK 6.13.0 (148) // Copyright (c) 2012-2023 AppsFlyer Ltd. All rights reserved. // @@ -12,6 +12,7 @@ #import #import #import +#import NS_ASSUME_NONNULL_BEGIN @@ -140,7 +141,7 @@ typedef enum { EmailCryptTypeSHA256 = 3 } EmailCryptType; -typedef NS_CLOSED_ENUM (NSInteger ,AFSDKPlugin) { +typedef NS_CLOSED_ENUM(NSInteger, AFSDKPlugin) { AFSDKPluginIOSNative, AFSDKPluginUnity, AFSDKPluginFlutter, @@ -159,7 +160,6 @@ typedef NS_CLOSED_ENUM (NSInteger ,AFSDKPlugin) { AFSDKPluginAdobeSwiftAEP } NS_SWIFT_NAME(Plugin); -@class AFSDKPluginInfo; NS_SWIFT_NAME(DeepLinkDelegate) @protocol AppsFlyerDeepLinkDelegate @@ -351,7 +351,7 @@ NS_SWIFT_NAME(waitForATTUserAuthorization(timeoutInterval:)); [[AppsFlyerLib shared] setResolveDeepLinkURLs:@[@"domain.com", @"subdomain.domain.com"]]; */ -@property(nonatomic, nullable) NSArray *resolveDeepLinkURLs; +@property(nonatomic, nullable, copy) NSArray *resolveDeepLinkURLs; /** For advertisers who use vanity OneLinks. @@ -362,12 +362,12 @@ NS_SWIFT_NAME(waitForATTUserAuthorization(timeoutInterval:)); [[AppsFlyerLib shared] oneLinkCustomDomains:@[@"domain.com", @"subdomain.domain.com"]]; */ -@property(nonatomic, nullable) NSArray *oneLinkCustomDomains; +@property(nonatomic, nullable, copy) NSArray *oneLinkCustomDomains; /* * Set phone number for each `start` event. `phoneNumber` will be sent as SHA256 string */ -@property(nonatomic, nullable) NSString *phoneNumber; +@property(nonatomic, nullable, copy) NSString *phoneNumber; - (NSString *)phoneNumber UNAVAILABLE_ATTRIBUTE; @@ -390,13 +390,16 @@ NS_SWIFT_NAME(waitForATTUserAuthorization(timeoutInterval:)); AppsFlyerLib.shared().currentDeviceLanguage("EN") */ -@property(nonatomic, nullable) NSString *currentDeviceLanguage; +@property(nonatomic, nullable, copy) NSString *currentDeviceLanguage; /** Internal API. Please don't use. */ -- (void)setPluginInfoWith:(AFSDKPlugin)plugin pluginVersion:(NSString *)version additionalParams:(NSDictionary * _Nullable)additionalParams +- (void)setPluginInfoWith:(AFSDKPlugin)plugin + pluginVersion:(NSString *)version + additionalParams:(NSDictionary * _Nullable)additionalParams NS_SWIFT_NAME(setPluginInfo(plugin:version:additionalParams:)); + /** Enable the collection of Facebook Deferred AppLinks Requires Facebook SDK and Facebook app on target/client device. @@ -578,6 +581,11 @@ NS_SWIFT_NAME(logEvent(name:values:completionHandler:)); */ - (void)remoteDebuggingCallWithData:(NSString *)data; +/** + This is for internal use. + */ +- (void)remoteDebuggingCallV2WithData:(NSString *)dataAsString; + /** Used to force the trigger `onAppOpenAttribution` delegate. Notice, re-engagement, session and launch won't be counted. @@ -634,13 +642,13 @@ NS_SWIFT_NAME(logEvent(name:values:completionHandler:)); /** API to set manually Facebook deferred app link */ -@property(nonatomic, nullable) NSURL *facebookDeferredAppLink; +@property(nonatomic, nullable, copy) NSURL *facebookDeferredAppLink; /** Block an events from being shared with ad networks and other 3rd party integrations Must only include letters/digits or underscore, maximum length: 45 */ -@property(nonatomic, nullable) NSArray *sharingFilter DEPRECATED_MSG_ATTRIBUTE("starting SDK version 6.4.0, please use `setSharingFilterForPartners:`"); +@property(nonatomic, nullable, copy) NSArray *sharingFilter DEPRECATED_MSG_ATTRIBUTE("starting SDK version 6.4.0, please use `setSharingFilterForPartners:`"); @property(nonatomic) NSUInteger deepLinkTimeout; @@ -661,6 +669,27 @@ NS_SWIFT_NAME(logEvent(name:values:completionHandler:)); */ - (void)setSharingFilterForPartners:(NSArray * _Nullable)sharingFilter; + +/** + Sets or updates the user consent data related to GDPR and DMA regulations for advertising and data usage + purposes within the application. This method must be invoked with the user's current consent status each + time the app starts or whenever there is a change in the user's consent preferences. + + Note that this method does not persist the consent data across app sessions; it only applies for the + duration of the current app session. If you wish to stop providing the consent data, you should + cease calling this method. + + @param consent an instance of AppsFlyerConsent that encapsulates the user's consent information. + */ +- (void)setConsentData:(AppsFlyerConsent *)consent; + +/** + Enable the SDK to collect and send TCF data + + @param shouldCollectConsentData indicates if the TCF data collection is enabled. + */ +- (void)enableTCFDataCollection:(BOOL)shouldCollectConsentData; + /** Validate if URL contains certain string and append quiery parameters to deeplink URL. In case if URL does not contain user-defined string, diff --git a/ios/AppsFlyerLinkGenerator.h b/ios/AppsFlyerLinkGenerator.h index b917074e..d3ec8f4e 100644 --- a/ios/AppsFlyerLinkGenerator.h +++ b/ios/AppsFlyerLinkGenerator.h @@ -22,7 +22,7 @@ NS_ASSUME_NONNULL_BEGIN /// from `-[AppsFlyerShareInviteHelper generateInviteUrlWithLinkGenerator:completionHandler]` + (instancetype)new NS_UNAVAILABLE; -@property(nonatomic, nullable) NSString *brandDomain; +@property(nonatomic, nullable, copy) NSString *brandDomain; /// The channel through which the invite was sent (e.g. Facebook/Gmail/etc.). Usage: Recommended - (void)setChannel :(nonnull NSString *)channel; diff --git a/ios/RNAppsFlyer.h b/ios/RNAppsFlyer.h index 506499ce..03487ab8 100755 --- a/ios/RNAppsFlyer.h +++ b/ios/RNAppsFlyer.h @@ -22,7 +22,7 @@ @end -static NSString *const kAppsFlyerPluginVersion = @"6.12.2"; +static NSString *const kAppsFlyerPluginVersion = @"6.13.0"; static NSString *const NO_DEVKEY_FOUND = @"No 'devKey' found or its empty"; static NSString *const NO_APPID_FOUND = @"No 'appId' found or its empty"; static NSString *const NO_EVENT_NAME_FOUND = @"No 'eventName' found or its empty"; diff --git a/ios/RNAppsFlyer.m b/ios/RNAppsFlyer.m index 284c31e9..0c8997fe 100755 --- a/ios/RNAppsFlyer.m +++ b/ios/RNAppsFlyer.m @@ -564,4 +564,23 @@ - (BOOL)isExpoApp { RCT_EXPORT_METHOD(appendParametersToDeepLinkingURL:(NSString *)contains partnerData:(NSDictionary *)parameters) { [[AppsFlyerLib shared] appendParametersToDeepLinkingURLWithString:contains parameters:parameters]; } + +RCT_EXPORT_METHOD(enableTCFDataCollection:(BOOL *)enabled) { + [[AppsFlyerLib shared] enableTCFDataCollection:enabled]; +} + +RCT_EXPORT_METHOD(setConsentData:(NSDictionary *)consentData) { + if (![consentData isKindOfClass:[NSNull null]]) { + BOOL hasConsentForDataUsage = consentData[@"hasConsentForDataUsage"]; + BOOL hasConsentForAdsPersonalization = consentData[@"hasConsentForAdsPersonalization"]; + AppsFlyerConsent *consentData = [[AppsFlyerConsent alloc] initForGDPRUserWithHasConsentForDataUsage:hasConsentForDataUsage hasConsentForAdsPersonalization:hasConsentForAdsPersonalization]; + [[AppsFlyerLib shared] setConsentData:consentData]; + } +} + +RCT_EXPORT_METHOD(setNonGDPRUser) { + AppsFlyerConsent *consentData = [[AppsFlyerConsent alloc] initNonGDPRUser]; + [[AppsFlyerLib shared] setConsentData:consentData]; +} + @end diff --git a/react-native-appsflyer.podspec b/react-native-appsflyer.podspec index 7674b34e..5daca372 100644 --- a/react-native-appsflyer.podspec +++ b/react-native-appsflyer.podspec @@ -18,13 +18,13 @@ Pod::Spec.new do |s| # AppsFlyerFramework if defined?($RNAppsFlyerStrictMode) && ($RNAppsFlyerStrictMode == true) Pod::UI.puts "#{s.name}: Using AppsFlyerFramework/Strict mode" - s.dependency 'AppsFlyerFramework/Strict', '6.12.2' + s.dependency 'AppsFlyerFramework/Strict', '6.13.0' s.xcconfig = {'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) AFSDK_NO_IDFA=1' } else if !defined?($RNAppsFlyerStrictMode) Pod::UI.puts "#{s.name}: Using default AppsFlyerFramework. You may require App Tracking Transparency. Not allowed for Kids apps." Pod::UI.puts "#{s.name}: You may set variable `$RNAppsFlyerStrictMode=true` in Podfile to use strict mode for kids apps." end - s.dependency 'AppsFlyerFramework', '6.12.2' + s.dependency 'AppsFlyerFramework', '6.13.0' end end From 70671fce79427c4e251dfc6e5e46fe98e552f403 Mon Sep 17 00:00:00 2001 From: Amit kremer Date: Thu, 15 Feb 2024 16:59:59 +0200 Subject: [PATCH 04/11] DMA feature readme --- Docs/RN_API.md | 58 +++++++++++++++++++++ Docs/RN_CMP.md | 136 +++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 1 + 3 files changed, 195 insertions(+) create mode 100644 Docs/RN_CMP.md diff --git a/Docs/RN_API.md b/Docs/RN_API.md index 6277bc57..b1bf5eb7 100644 --- a/Docs/RN_API.md +++ b/Docs/RN_API.md @@ -35,6 +35,9 @@ The list of available methods for this plugin is described below. - [addPushNotificationDeepLinkPath](#addpushnotificationdeeplinkpath) - [appendParametersToDeepLinkingURL](#appendparameterstodeeplinkingurl) - [disableAdvertisingIdentifier](#disableAdvertisingIdentifier) + - [enableTCFDataCollection](#enableTCFDataCollection) + - [setConsentData](#setConsentData) + - [setNonGDPRUser](#setNonGDPRUser) - [Android Only APIs](#android-only-apis) - [setCollectAndroidID](#setcollectandroidid) - [setCollectIMEI](#setcollectimei) @@ -750,6 +753,61 @@ Disables collection of various Advertising IDs by the SDK.
appsFlyer.disableAdvertisingIdentifier(true); ``` +--- +### enableTCFDataCollection +`enableTCFDataCollection(enabled): void` + +instruct the SDK to collect the TCF data from the device. + + +| parameter | type | description | +| ---------- |----------|------------------ | +| enabled | boolean | enable/disable TCF data collection | + +*Example:* + +```javascript +appsFlyer.enableTCFDataCollection(true); +``` + +--- +### setConsentData +`setConsentData(consentData): void` + +When GDPR applies to the user and your app does not use a CMP compatible with TCF v2.2, use this API to provide the consent data directly to the SDK.
+The consent object has 2 properties: + +1. `hasConsentForDataUsage`: Indicates whether the user has consented to use their data for advertising purposes. +2. `hasConsentForAdsPersonalization`: ndicates whether the user has consented to use their data for personalized advertising. + +| parameter | type | description | +| ---------- |----------|------------------ | +| consentData | object | Consent object of the user | + +*Example:* + +```javascript +let consentData = { + hasConsentForDataUsage: true, + hasConsentForAdsPersonalization: false + } + +appsFlyer.setConsentData(consentData); +``` + +--- + +### setNonGDPRUser +`setNonGDPRUser(): void` + +Use this API if GDPR doesn’t apply to the user.
+ +*Example:* + +```javascript +appsFlyer.setNonGDPRUser(); +``` + ## Android Only APIs ### setCollectAndroidID diff --git a/Docs/RN_CMP.md b/Docs/RN_CMP.md new file mode 100644 index 00000000..3a23c855 --- /dev/null +++ b/Docs/RN_CMP.md @@ -0,0 +1,136 @@ +--- +title: Send consent for DMA compliance +category: 5f9705393c689a065c409b23 +parentDoc: 645213236f53a00d4daa9230 +order: 12 +hidden: false +--- + +## Send consent for DMA compliance +The SDK offers two alternative methods for gathering consent data: + +Through a Consent Management Platform (CMP): If the app uses a CMP that complies with the Transparency and Consent Framework (TCF) v2.2 protocol, the SDK can automatically retrieve the consent details. + +OR + +Through a dedicated SDK API: Developers can pass Google's required consent data directly to the SDK using a specific API designed for this purpose. + +### Use CMP to collect consent data +A CMP compatible with TCF v2.2 collects DMA consent data and stores it in NSUserDefaults (iOS) and SharedPreferences (Android). To enable the SDK to access this data and include it with every event, follow these steps: + +1. Call `appsFlyer.enableTCFDataCollection(true)` +2. Initialize the SDK in [manual start mode](/Docs/RN_API.md#initsdk) +3. Use the CMP to decide if you need the consent dialog in the current session to acquire the consent data. If you need the consent dialog move to step 4; otherwise move to step 5 +4. Get confirmation from the CMP that the user has made their consent decision and the data is available in NSUserDefaults/SharedPreferences +5. Call `appsFlyer.startSdk()` +```javascript + useEffect(() => { + const option = { + isDebug: true, + devKey: 'UxXxXxXxXd', + onInstallConversionDataListener: true, + onDeepLinkListener: true, + timeToWaitForATTUserAuthorization: 10, + manualStart: true, // <-- Manual start + }; + // TCF data collection + appsFlyer.enableTCFDataCollection(true); + + //init appsflyer + appsFlyer.initSdk( + option, + res => { + console.log(res); + }, + err => { + console.log(err); + }, + ); + + ... + + // CMP Pseudocode + if (cmpManager.hasConsent()) { + appsFlyer.startSdk(); + } else { + cmpManager.presentConsentDialog(res => { + appsFlyer.startSdk(); + }); + } + },[]) +``` +## Manually collect consent data +If your app does not use a CMP compatible with TCF v2.2, use the SDK API detailed below to provide the consent data directly to the SDK. + +### When GDPR applies to the user +If GDPR applies to the user, perform the following: + +1. Given that GDPR is applicable to the user, determine whether the consent data is already stored for this session. + 1. If there is no consent data stored, show the consent dialog to capture the user consent decision. + 2. If there is consent data stored continue to the next step. +2. To transfer the consent data to the SDK create a JSON with the following parameters:
+ `hasConsentForDataUsage` - Indicates whether the user has consented to use their data for advertising purposes.
+ `hasConsentForAdsPersonalization` - Indicates whether the user has consented to use their data for personalized advertising. +3. Call `appsFlyer.setConsentData({})` with the JSON. +4. Call `appsFlyer.initSdk()`. +```javascript +useEffect(() => { + const option = { + isDebug: true, + devKey: 'UxXxXxXxXd', + onInstallConversionDataListener: true, + onDeepLinkListener: true, + timeToWaitForATTUserAuthorization: 10, + }; + + // user consent data + let consentData = { + hasConsentForDataUsage: true/false, + hasConsentForAdsPersonalization: true/false + } + + appsFlyer.setConsentData(consentData); + + //start appsflyer + appsFlyer.initSdk( + option, + res => { + console.log(res); + }, + err => { + console.log(err); + }, + ); + },[]) +``` +### When GDPR does not apply to the user + +If GDPR doesn’t apply to the user perform the following: + +1. Call `appsFlyer.setNonGDPRUser()`. +2. Call `appsFlyer.initSdk()`. +```javascript +useEffect(() => { + const option = { + isDebug: true, + devKey: 'UxXxXxXxXd', + onInstallConversionDataListener: true, + onDeepLinkListener: true, + timeToWaitForATTUserAuthorization: 10, + }; + + // GDPR does not apply to the user + appsFlyer.setNonGDPRUser() + + //start appsflyer + appsFlyer.initSdk( + option, + res => { + console.log(res); + }, + err => { + console.log(err); + }, + ); + },[]) +``` \ No newline at end of file diff --git a/README.md b/README.md index 56ad9f49..9a8d303f 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ If you have used 1 of the removed APIs, please check the integration guide for t - [Test integration](/Docs/RN_Testing.md) - [In-app events](/Docs/RN_InAppEvents.md) - [Uninstall measurement](/Docs/RN_UninstallMeasurement.md) +- [Send consent for DMA compliance](/Docs/RN_CMP.md) ## 🔗 Deep Linking - [Integration](/Docs/RN_DeepLinkIntegrate.md) - [***Expo*** Integration](/Docs/RN_ExpoDeepLinkIntegration.md) From 5691c3bcfc0d72d66a54dee84a33a73eddb72b49 Mon Sep 17 00:00:00 2001 From: Amit kremer Date: Sun, 18 Feb 2024 17:09:50 +0200 Subject: [PATCH 05/11] review 1 --- Docs/RN_API.md | 35 +++++++------------ .../reactnative/RNAppsFlyerModule.java | 14 ++++---- index.d.ts | 10 ++++-- index.js | 30 ++++++++++++---- ios/RNAppsFlyer.m | 23 ++++++------ 5 files changed, 64 insertions(+), 48 deletions(-) diff --git a/Docs/RN_API.md b/Docs/RN_API.md index b1bf5eb7..11905b42 100644 --- a/Docs/RN_API.md +++ b/Docs/RN_API.md @@ -37,7 +37,6 @@ The list of available methods for this plugin is described below. - [disableAdvertisingIdentifier](#disableAdvertisingIdentifier) - [enableTCFDataCollection](#enableTCFDataCollection) - [setConsentData](#setConsentData) - - [setNonGDPRUser](#setNonGDPRUser) - [Android Only APIs](#android-only-apis) - [setCollectAndroidID](#setcollectandroidid) - [setCollectIMEI](#setcollectimei) @@ -772,40 +771,30 @@ appsFlyer.enableTCFDataCollection(true); --- ### setConsentData -`setConsentData(consentData): void` +`setConsentData(consentObject): void` When GDPR applies to the user and your app does not use a CMP compatible with TCF v2.2, use this API to provide the consent data directly to the SDK.
-The consent object has 2 properties: +The AppsFlyerConsent object has 2 methods: + +1. `AppsFlyerConsent.forNonGDPRUser`: Indicates that GDPR doesn’t apply to the user and generates nonGDPR consent object. This method doesn’t accept any parameters. +2. `AppsFlyerConsent.forGDPRUser`: create an AppsFlyerConsent object with 2 parameters: -1. `hasConsentForDataUsage`: Indicates whether the user has consented to use their data for advertising purposes. -2. `hasConsentForAdsPersonalization`: ndicates whether the user has consented to use their data for personalized advertising. | parameter | type | description | | ---------- |----------|------------------ | -| consentData | object | Consent object of the user | +| hasConsentForDataUsage | boolean | Indicates whether the user has consented to use their data for advertising purposes | +| hasConsentForAdsPersonalization | boolean | Indicates whether the user has consented to use their data for personalized advertising | *Example:* ```javascript -let consentData = { - hasConsentForDataUsage: true, - hasConsentForAdsPersonalization: false - } - -appsFlyer.setConsentData(consentData); -``` - ---- - -### setNonGDPRUser -`setNonGDPRUser(): void` +import appsFlyer, {AppsFlyerConsent} from 'react-native-appsflyer'; -Use this API if GDPR doesn’t apply to the user.
+let nonGDPRUser = AppsFlyerConsent.forNonGDPRUser(); +// OR +let GDPRUser = AppsFlyerConsent.forGDPRUser(true, false); -*Example:* - -```javascript -appsFlyer.setNonGDPRUser(); +appsFlyer.setConsentData(nonGDPRUser /**or**/ GDPRUser); ``` ## Android Only APIs diff --git a/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerModule.java b/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerModule.java index f60fa1bb..90989a67 100755 --- a/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerModule.java +++ b/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerModule.java @@ -824,18 +824,18 @@ public void enableTCFDataCollection(Boolean enabled) { @ReactMethod public void setConsentData(ReadableMap consentData) { JSONObject JSONConsentData = RNUtil.readableMapToJson(consentData); + boolean isUserSubjectToGDPR = JSONConsentData.optBoolean("isUserSubjectToGDPR"); boolean hasConsentForDataUsage = JSONConsentData.optBoolean("hasConsentForDataUsage"); boolean hasConsentForAdsPersonalization = JSONConsentData.optBoolean("hasConsentForAdsPersonalization"); - AppsFlyerConsent consentObject = AppsFlyerConsent.forGDPRUser(hasConsentForDataUsage, hasConsentForAdsPersonalization); + AppsFlyerConsent consentObject; + if (isUserSubjectToGDPR) { + consentObject = AppsFlyerConsent.forGDPRUser(hasConsentForDataUsage, hasConsentForAdsPersonalization); + } else { + consentObject = AppsFlyerConsent.forNonGDPRUser(); + } AppsFlyerLib.getInstance().setConsentData(consentObject); } - @ReactMethod - public void setNonGDPRUser() { - AppsFlyerConsent consentData = AppsFlyerConsent.forNonGDPRUser(); - AppsFlyerLib.getInstance().setConsentData(consentData); - } - @ReactMethod public void addListener(String eventName) { // Keep: Required for RN built in Event Emitter Calls. diff --git a/index.d.ts b/index.d.ts index 9a61d51c..e1ae4930 100644 --- a/index.d.ts +++ b/index.d.ts @@ -114,6 +114,13 @@ declare module "react-native-appsflyer" { brandDomain?: string; } + export const AppsFlyerConsent: { + forGDPRUser: (hasConsentForDataUsage: boolean, hasConsentForAdsPersonalization: boolean) => void; + forNonGDPRUser: () => void; + } + + export type AppsFlyerConsentType = typeof AppsFlyerConsent; + const appsFlyer: { onInstallConversionData(callback: (data: ConversionData) => any): () => void; onInstallConversionFailure(callback: (data: ConversionData) => any): () => void; @@ -152,8 +159,7 @@ declare module "react-native-appsflyer" { appendParametersToDeepLinkingURL(contains: string, parameters: object): void startSdk(): void enableTCFDataCollection(enabled: boolean): void - setConsentData(consentData: object): void - setNonGDPRUser(): void + setConsentData(consentData: AppsFlyerConsentType): void /** * For iOS Only diff --git a/index.js b/index.js index b1c78d4e..07a297c8 100755 --- a/index.js +++ b/index.js @@ -635,20 +635,38 @@ appsFlyer.enableTCFDataCollection= (enabled) => { /** * If your app does not use a CMP compatible with TCF v2.2, use the SDK API detailed below to provide the consent data directly to the SDK. - * @param consentData: object with 2 properties: hasConsentForDataUsage and hasConsentForAdsPersonalization. + * @param consentData: AppsFlyerConsent object. */ appsFlyer.setConsentData = (consentData) => { return RNAppsFlyer.setConsentData(consentData); } -appsFlyer.setNonGDPRUser = () => { - return RNAppsFlyer.setNonGDPRUser(); -} - function AFParseJSONException(_message, _data) { this.message = _message; this.data = _data; this.name = 'AFParseJSONException'; } -export default appsFlyer; +// Consent object +export const AppsFlyerConsent = (function () { + // Private constructor + function AppsFlyerConsent(isUserSubjectToGDPR, hasConsentForDataUsage, hasConsentForAdsPersonalization) { + this.isUserSubjectToGDPR = isUserSubjectToGDPR; + this.hasConsentForDataUsage = hasConsentForDataUsage; + this.hasConsentForAdsPersonalization = hasConsentForAdsPersonalization; + } + + return { + // Factory method for GDPR user + forGDPRUser: function(hasConsentForDataUsage, hasConsentForAdsPersonalization) { + return new AppsFlyerConsent(true, hasConsentForDataUsage, hasConsentForAdsPersonalization); + }, + + // Factory method for non GDPR user + forNonGDPRUser: function() { + return new AppsFlyerConsent(false, null, null); + } + }; +})(); + +export default appsFlyer; \ No newline at end of file diff --git a/ios/RNAppsFlyer.m b/ios/RNAppsFlyer.m index 0c8997fe..43e48f78 100755 --- a/ios/RNAppsFlyer.m +++ b/ios/RNAppsFlyer.m @@ -569,18 +569,21 @@ - (BOOL)isExpoApp { [[AppsFlyerLib shared] enableTCFDataCollection:enabled]; } -RCT_EXPORT_METHOD(setConsentData:(NSDictionary *)consentData) { - if (![consentData isKindOfClass:[NSNull null]]) { - BOOL hasConsentForDataUsage = consentData[@"hasConsentForDataUsage"]; - BOOL hasConsentForAdsPersonalization = consentData[@"hasConsentForAdsPersonalization"]; - AppsFlyerConsent *consentData = [[AppsFlyerConsent alloc] initForGDPRUserWithHasConsentForDataUsage:hasConsentForDataUsage hasConsentForAdsPersonalization:hasConsentForAdsPersonalization]; +RCT_EXPORT_METHOD(setConsentData:(NSDictionary *)consentDictionary) { + if (![consentDictionary isKindOfClass:[NSNull null]]) { + BOOL isUserSubjectToGDPR = [consentDictionary[@"isUserSubjectToGDPR"] boolValue]; + + AppsFlyerConsent *consentData; + if (isUserSubjectToGDPR){ + BOOL hasConsentForDataUsage = [consentDictionary[@"hasConsentForDataUsage"] boolValue]; + BOOL hasConsentForAdsPersonalization = [consentDictionary[@"hasConsentForAdsPersonalization"] boolValue]; + consentData = [[AppsFlyerConsent alloc] initForGDPRUserWithHasConsentForDataUsage:hasConsentForDataUsage hasConsentForAdsPersonalization:hasConsentForAdsPersonalization]; + } else { + consentData = [[AppsFlyerConsent alloc] initNonGDPRUser]; + } + [[AppsFlyerLib shared] setConsentData:consentData]; } } -RCT_EXPORT_METHOD(setNonGDPRUser) { - AppsFlyerConsent *consentData = [[AppsFlyerConsent alloc] initNonGDPRUser]; - [[AppsFlyerLib shared] setConsentData:consentData]; -} - @end From cc5ebfca0d0f88cbaf02450db7655355f2a8dd6c Mon Sep 17 00:00:00 2001 From: Amit kremer Date: Sun, 18 Feb 2024 17:21:44 +0200 Subject: [PATCH 06/11] update cmp docs --- Docs/RN_CMP.md | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/Docs/RN_CMP.md b/Docs/RN_CMP.md index 3a23c855..9930bcac 100644 --- a/Docs/RN_CMP.md +++ b/Docs/RN_CMP.md @@ -68,12 +68,14 @@ If GDPR applies to the user, perform the following: 1. Given that GDPR is applicable to the user, determine whether the consent data is already stored for this session. 1. If there is no consent data stored, show the consent dialog to capture the user consent decision. 2. If there is consent data stored continue to the next step. -2. To transfer the consent data to the SDK create a JSON with the following parameters:
- `hasConsentForDataUsage` - Indicates whether the user has consented to use their data for advertising purposes.
- `hasConsentForAdsPersonalization` - Indicates whether the user has consented to use their data for personalized advertising. -3. Call `appsFlyer.setConsentData({})` with the JSON. +2. To transfer the consent data to the SDK create an AppsFlyerConsent object using `forGDPRUser` method that accepts the following parameters:
+ `hasConsentForDataUsage: boolean` - Indicates whether the user has consented to use their data for advertising purposes.
+ `hasConsentForAdsPersonalization: boolean` - Indicates whether the user has consented to use their data for personalized advertising. +3. Call `appsFlyer.setConsentData(consentData)` with the AppsFlyerConsent object. 4. Call `appsFlyer.initSdk()`. ```javascript +import appsFlyer, {AppsFlyerConsent} from 'react-native-appsflyer'; + useEffect(() => { const option = { isDebug: true, @@ -84,10 +86,7 @@ useEffect(() => { }; // user consent data - let consentData = { - hasConsentForDataUsage: true/false, - hasConsentForAdsPersonalization: true/false - } + let consentData = AppsFlyerConsent.forGDPRUser(true, false); appsFlyer.setConsentData(consentData); @@ -106,10 +105,12 @@ useEffect(() => { ### When GDPR does not apply to the user If GDPR doesn’t apply to the user perform the following: - -1. Call `appsFlyer.setNonGDPRUser()`. -2. Call `appsFlyer.initSdk()`. +1. Create an AppsFlyerConsent object using `forNonGDPRUser` method that doesn't accepts any parameters +2. Call `appsFlyer.setConsentData(consentData)` with the AppsFlyerConsent object. +3. Call `appsFlyer.initSdk()`. ```javascript +import appsFlyer, {AppsFlyerConsent} from 'react-native-appsflyer'; + useEffect(() => { const option = { isDebug: true, @@ -120,7 +121,9 @@ useEffect(() => { }; // GDPR does not apply to the user - appsFlyer.setNonGDPRUser() + let consentData = AppsFlyerConsent.forNonGDPRUser(); + + appsFlyer.setConsentData(consentData); //start appsflyer appsFlyer.initSdk( From f2d138da3b18da4c5ac8a85087ce4d9fa3db76c9 Mon Sep 17 00:00:00 2001 From: Amit kremer Date: Mon, 19 Feb 2024 10:22:55 +0200 Subject: [PATCH 07/11] fix sample app dependency errors --- demos/appsflyer-react-native-app/android/build.gradle | 4 ++-- demos/appsflyer-react-native-app/package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/demos/appsflyer-react-native-app/android/build.gradle b/demos/appsflyer-react-native-app/android/build.gradle index 7591242f..e5f20a04 100644 --- a/demos/appsflyer-react-native-app/android/build.gradle +++ b/demos/appsflyer-react-native-app/android/build.gradle @@ -4,8 +4,8 @@ buildscript { ext { buildToolsVersion = "30.0.2" minSdkVersion = 21 - compileSdkVersion = 31 - targetSdkVersion = 31 + compileSdkVersion = 32 + targetSdkVersion = 32 ndkVersion = "20.1.5948944" } repositories { diff --git a/demos/appsflyer-react-native-app/package.json b/demos/appsflyer-react-native-app/package.json index c79dfa0b..ce3eac5d 100644 --- a/demos/appsflyer-react-native-app/package.json +++ b/demos/appsflyer-react-native-app/package.json @@ -18,8 +18,8 @@ "react-native-elements": "^3.4.2", "react-native-gesture-handler": "^1.10.3", "react-native-safe-area-context": "^3.3.2", - "react-native-screens": "^3.7.0", - "react-native-vector-icons": "^8.1.0" + "react-native-screens": "3.7.2", + "react-native-vector-icons": "8.1.0" }, "devDependencies": { "@babel/core": "^7.12.9", From b813fc77fde94ec78282b841257adf4cc708a46b Mon Sep 17 00:00:00 2001 From: Amit kremer Date: Mon, 19 Feb 2024 11:20:40 +0200 Subject: [PATCH 08/11] update SDK versions --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9a8d303f..423f3e79 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ ### This plugin is built for -- Android AppsFlyer SDK **v6.12.2** -- iOS AppsFlyer SDK **v6.12.2** +- Android AppsFlyer SDK **v6.13.0** +- iOS AppsFlyer SDK **v6.13.0** ## ❗❗ Breaking changes when updating to v6.x.x❗❗ From 3ab0e2f690430b04a7c06d10217a620f4f87c75c Mon Sep 17 00:00:00 2001 From: "Amit.kremer" Date: Mon, 19 Feb 2024 09:24:56 +0000 Subject: [PATCH 09/11] 6.13.0-rc2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7813f911..c2c6e499 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-appsflyer", - "version": "6.12.2", + "version": "6.13.0-rc2", "description": "React Native Appsflyer plugin", "main": "index.js", "types": "index.d.ts", From 9de998954c0083f54377ed1dc2fca5abdd299e6f Mon Sep 17 00:00:00 2001 From: "Amit.kremer" Date: Mon, 19 Feb 2024 10:44:13 +0000 Subject: [PATCH 10/11] 6.13.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c2c6e499..674815a4 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-appsflyer", - "version": "6.13.0-rc2", + "version": "6.13.0", "description": "React Native Appsflyer plugin", "main": "index.js", "types": "index.d.ts", From 2635e1d6d5f8171a9e9f12c466181374cd9c27d3 Mon Sep 17 00:00:00 2001 From: "Amit.kremer" Date: Mon, 19 Feb 2024 10:44:15 +0000 Subject: [PATCH 11/11] Update CHANGELOG.md --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 18e089c2..6ec06f8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 6.13.0 + Release date: *2024-02-19* + +- Add DMA support +- Fix incompatible config for RN >= 0.69.x +- Update Plugin to v6.13.0 + ## 6.12.2 Release date: *2023-08-29*