diff --git a/CHANGELOG.md b/CHANGELOG.md
index d474e5b..9ca2490 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 6.13.0
+ Release date: *2024-02-26*
+
+- Capacitor >> MonoRepo structure project >> failed to build on Android since v6.10.3
+
## 6.12.1
Release date: *2023-07-31*
diff --git a/README.md b/README.md
index a28ccc7..d929cc7 100644
--- a/README.md
+++ b/README.md
@@ -14,16 +14,16 @@
### This plugin is built for
-- Android AppsFlyer SDK **6.12.1**
-- iOS AppsFlyer SDK **6.12.1**
+- Android AppsFlyer SDK **6.13.0**
+- iOS AppsFlyer SDK **6.13.0**
-## ❗❗ Breaking changes when updating to v6.12.1❗❗
+## ❗❗ Breaking changes when updating to v6.12.1 ❗❗
Starting from v6.12.1, this plugin works only with Capacitor 5.
If you are still interested in using Capacitor 4, please follow the instructions [here](/docs/Installation.md#cap4) to install the latest version that supports Capacitor 4.
-## ❗❗ Breaking changes when updating to v6.9.2❗❗
+## ❗❗ Breaking changes when updating to v6.9.2 ❗❗
Starting from v6.9.2, this plugin works only with Capacitor 4.
If you are still interested in using Capacitor 3, please follow the instructions [here](/docs/Installation.md#cap3) to install the latest version that supports Capacitor 3.
@@ -37,9 +37,10 @@ If you are still interested in using Capacitor 3, please follow the instructions
## 📖 Guides
- [Adding the SDK to your project](/docs/Installation.md)
-- [Initializing the SDK](/docs/BasicIntegration.md)
+- [Initializing The SDK](/docs/BasicIntegration.md)
- [In-app Events](/docs/InAppEvents.md)
- [Deep Linking](/docs/DeepLink.md)
- [Advanced API](/docs/AdvancedAPI.md)
-- [Testing the integration](/docs/Testing.md)
+- [Testing The integration](/docs/Testing.md)
- [API](/docs/API.md)
+- [Set Consent For DMA Compliance](/docs/DMA.md)
diff --git a/android/build.gradle b/android/build.gradle
index e5481c5..ac71624 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -1,11 +1,74 @@
-// On top of your file import a JSON parser
import groovy.json.JsonSlurper
+import java.nio.file.FileVisitResult
+import java.nio.file.Files
+import java.nio.file.Path
+import java.nio.file.SimpleFileVisitor
+import java.nio.file.attribute.BasicFileAttributes
+
+String getPackageJsonPath() {
+ return findProperty("APPSFLYER_PACKAGE_JSON") ?: "$rootDir/../node_modules/appsflyer-capacitor-plugin/package.json"
+}
+
+def findNodeModulesDir(File currentDir) {
+ def dir = currentDir
+ while (dir != null) {
+ def nodeModulesDir = new File(dir, 'node_modules')
+ if (nodeModulesDir.exists()) {
+ return nodeModulesDir
+ }
+ dir = dir.parentFile
+ }
+ return null
+}
+
+def findPackageJsonInDep(String packageName) {
+ def nodeModulesDir = findNodeModulesDir(project.rootDir)
+ if (nodeModulesDir == null) {
+ println "node_modules directory not found in any parent directories."
+ return null
+ }
+
+ def json = null
+
+ def walker = new SimpleFileVisitor() {
+ @Override
+ FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
+ if (file.toAbsolutePath().endsWith("appsflyer-capacitor-plugin/package.json")) {
+ try {
+ def content = new JsonSlurper().parseText(file.toFile().text)
+ if (content.name == packageName) {
+ println "Found package.json: ${file.toAbsolutePath()}"
+ json = content
+ return FileVisitResult.TERMINATE
+ }
+ } catch (Exception e) {
+ println "Error parsing JSON in file: ${file.toAbsolutePath().toString()}\n${e.message}\n\t"
+ }
+ }
+ return FileVisitResult.CONTINUE
+ }
+ }
+ while (json == null && nodeModulesDir != null) {
+ Files.walkFileTree(nodeModulesDir.toPath(), walker)
+ // parentFile will give us exact same directory so we have to go 2 level upper
+ // and find another node_modules
+ nodeModulesDir = findNodeModulesDir(nodeModulesDir.parentFile.parentFile)
+ }
+ return json
+}
+
def getPackageJson() {
- // Read and parse package.json file from project root
- def dir = "$rootDir/../node_modules/appsflyer-capacitor-plugin/package.json"
- def inputFile = new File(dir)
- def packageJson = new JsonSlurper().parseText(inputFile.text)
+ def packageJson
+ def inputFile = new File(getPackageJsonPath())
+ if (inputFile.exists()) {
+ println "found package.json from ENV variable"
+ packageJson = new JsonSlurper().parseText(inputFile.text)
+ } else {
+ println "could not found package.json from ENV variable"
+ println "searching for package.json recursively"
+ packageJson = findPackageJsonInDep("appsflyer-capacitor-plugin")
+ }
return packageJson
}
// Create an easy to use function
@@ -19,6 +82,7 @@ def getSDKVersionFromNpm() {
// Return the version, you can get any value this way
return getPackageJson()["androidSdkVersion"]
}
+
def getPluginBuildVersionFromNpm() {
// Return the version, you can get any value this way
return getPackageJson()["buildNumber"]
diff --git a/android/src/main/java/capacitor/plugin/appsflyer/sdk/AppsFlyerConstants.kt b/android/src/main/java/capacitor/plugin/appsflyer/sdk/AppsFlyerConstants.kt
index bf54099..a0dbc6f 100644
--- a/android/src/main/java/capacitor/plugin/appsflyer/sdk/AppsFlyerConstants.kt
+++ b/android/src/main/java/capacitor/plugin/appsflyer/sdk/AppsFlyerConstants.kt
@@ -59,3 +59,9 @@ const val AF_DATA = "data"
const val AF_PARTNER_ID = "partnerId"
const val AF_DEEP_LINK_TIME_OUT = "deepLinkTimeout"
const val AF_EVENT_PARAMETERS = "eventParameters"
+const val AF_ENABLE_TCF_DATA_COLLECTION = "shouldEnableTCFDataCollection"
+const val AF_MANUAL_START = "manualStart"
+const val AF_IS_SUBJECTED_TO_GDPR = "isUserSubjectToGDPR"
+const val AF_CONSENT_FOR_DATA_USAGE = "hasConsentForDataUsage"
+const val AF_CONSENT_FOR_ADS_PERSONALIZATION = "hasConsentForAdsPersonalization"
+
diff --git a/android/src/main/java/capacitor/plugin/appsflyer/sdk/AppsFlyerPlugin.kt b/android/src/main/java/capacitor/plugin/appsflyer/sdk/AppsFlyerPlugin.kt
index 1a317b2..e6f7ec2 100644
--- a/android/src/main/java/capacitor/plugin/appsflyer/sdk/AppsFlyerPlugin.kt
+++ b/android/src/main/java/capacitor/plugin/appsflyer/sdk/AppsFlyerPlugin.kt
@@ -49,6 +49,7 @@ class AppsFlyerPlugin : Plugin() {
val devKey = call.getString(AF_DEV_KEY)
val debug = call.getBoolean(AF_DEBUG, false)
val minTime = call.getInt(AF_MIN_TIME)
+ val manualStart = call.getBoolean(AF_MANUAL_START, false)
conversion = call.getBoolean(AF_CONVERSION_LISTENER, true)
oaoa = call.getBoolean(AF_OAOA, true)
udl = call.getBoolean(AF_UDL, false)
@@ -59,7 +60,7 @@ class AppsFlyerPlugin : Plugin() {
PluginInfo(
com.appsflyer.internal.platform_extension.Plugin.CAPACITOR,
BuildConfig.VERSION_NAME
- //, mapOf("build_number" to BuildConfig.VERSION_CODE.toString())
+ //, mapOf("build_number" to BuildConfig.VERSION_CODE.toString())
)
)
if (debug == true) {
@@ -86,18 +87,15 @@ class AppsFlyerPlugin : Plugin() {
subscribeForDeepLink(getDeepLinkListener())
}
}
- start(activity ?: context.applicationContext, null, object : AppsFlyerRequestListener {
- override fun onSuccess() {
- val ret = JSObject()
- ret.put("res", "ok")
- call.resolve(ret)
- }
- override fun onError(p0: Int, p1: String) {
- call.reject(p1, p0.toString())
+ if (manualStart == false) {
+ startSDK(call)
+ } else {
+ val result = JSObject().apply {
+ put("res", "SDK initiated successfully. SDK has NOT been started yet")
}
-
- })
+ call.resolve(result)
+ }
}
}
@@ -284,6 +282,23 @@ class AppsFlyerPlugin : Plugin() {
}
}
+ @PluginMethod
+ fun startSDK(call: PluginCall) {
+ AppsFlyerLib.getInstance()
+ .start(activity ?: context.applicationContext, null, object : AppsFlyerRequestListener {
+ override fun onSuccess() {
+ val result = JSObject().apply {
+ put("res", "success")
+ }
+ call.resolve(result)
+ }
+
+ override fun onError(errCode: Int, msg: String) {
+ call.reject("Error Code: $errCode, Message: $msg")
+ }
+ })
+ }
+
@PluginMethod
fun disableSKAdNetwork(call: PluginCall) {
call.unavailable()
@@ -517,6 +532,35 @@ class AppsFlyerPlugin : Plugin() {
}
+ @PluginMethod(returnType = PluginMethod.RETURN_NONE)
+ fun enableTCFDataCollection(call: PluginCall) {
+ val shouldEnable = call.getBoolean(AF_ENABLE_TCF_DATA_COLLECTION)
+ if (shouldEnable != null) {
+ AppsFlyerLib.getInstance().enableTCFDataCollection(shouldEnable)
+ } else {
+ call.reject("Missing boolean value $AF_ENABLE_TCF_DATA_COLLECTION")
+ }
+ }
+
+ @PluginMethod(returnType = PluginMethod.RETURN_NONE)
+ fun setConsentData(call: PluginCall) {
+ val consentData = call.getObject("data") ?: return call.reject("Missing consent data")
+
+ val isUserSubjectToGDPR = consentData.optBoolean(AF_IS_SUBJECTED_TO_GDPR)
+ val hasConsentForDataUsage = consentData.optBoolean(AF_CONSENT_FOR_DATA_USAGE)
+ val hasConsentForAdsPersonalization = consentData.optBoolean(AF_CONSENT_FOR_ADS_PERSONALIZATION)
+
+ val consentObject = if (isUserSubjectToGDPR) {
+ AppsFlyerConsent.forGDPRUser(hasConsentForDataUsage, hasConsentForAdsPersonalization)
+ } else {
+ AppsFlyerConsent.forNonGDPRUser()
+ }
+
+ AppsFlyerLib.getInstance().setConsentData(consentObject)
+
+ call.resolve()
+ }
+
private fun getDeepLinkListener(): DeepLinkListener {
return DeepLinkListener {
if (udl == true) {
diff --git a/docs/API.md b/docs/API.md
index 4887d63..1efdfbc 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -56,6 +56,10 @@ The list of available methods for this plugin is described below.
* [`setSharingFilterForPartners`](#setsharingfilterforpartners)
* [`setSharingFilter`](#setsharingfilter) - Deprecated
* [`setSharingFilterForAllPartners`](#setsharingfilterforallpartners) - Deprecated
+* [`startSDK`](#startSDK) - Since 6.13.0
+* [`enableTCFDataCollection`](#enableTCFDataCollection) - Since 6.13.0
+* [`setConsentData`](#setConsentData) - Since 6.13.0
+
@@ -180,7 +184,27 @@ See also Init SDK guide [here](/Guides.md#init-sdk).
--------------------
-
+
+
+### startSDK
+```typescript
+startSDK(): Promise;
+```
+
+Use this method to start AppsFlyer SDK only on manual start mode.
+
+**Returns:** Promise<AFRes>
+
+**Usage Example:**
+```typescript
+ AppsFlyer.startSDK()
+ .then(res => console.log("AppsFlyer SDK Start Response: ", res.res))
+ .catch(err => console.error("AppsFlyer SDK Start Error: ", err));
+```
+
+
+--------------------
+
### logEvent
@@ -908,6 +932,55 @@ Use to log a user-invite in-app event (af_invite).
.catch(e => console.log(e));
```
+--------------------
+
+### enableTCFDataCollection
+```typescript
+enableTCFDataCollection(shouldEnableTCFDataCollection: AFEnableTCFDataCollection): Promise
+```
+
+Use to opt-in/out the automatic collection of consent data, for users who use a CMP.
+Flag value will be persisted between app sessions.
+
+| Param | Type |
+| ------------- | ------------------------------------------------------- |
+| **`shouldEnableTCFDataCollection`** | AFEnableTCFDataCollection
|
+
+**Returns:** Promise
+
+**Usage Example:**
+```typescript
+ AppsFlyer.enableTCFDataCollection({shouldEnableTCFDataCollection : })
+```
+
+--------------------
+
+### setConsentData
+```typescript
+setConsentData(data : AFConsentData): Promise
+```
+
+Use to set user consent data manualy.
+if your app doesn't use a CMP compatible with TCF v2.2, use the following method to manualy provide the consent data directly to the SDK.
+
+| Param | Type |
+| ------------- | ------------------------------------------------------- |
+| **`data`** | AFConsentData
|
+
+**Returns:** Promise
+
+**Usage Example:**
+If *GDPR doesn’t* to the user, perform the following:
+```typescript
+ AppsFlyer.setConsentData({data: AppsFlyerConsent.forNonGDPRUser()})
+```
+If *GDPR applies* apply to the user perform the following:
+```typescript
+ AppsFlyer.setConsentData({data : AppsFlyerConsent.forGDPRUser(, )});
+```
+*Please take a look how to properly setConsentData Manualy in [Set Consent For DMA Compliance](/docs/DMA.md#)*
+
+
## Interfaces
@@ -1193,6 +1266,26 @@ Use to log a user-invite in-app event (af_invite).
| **`eventParameters`** | StringMap
|
| **`channel`** | string
|
+#### AFEnableTCFDataCollection
+
+| Prop | Type |
+| ----------------- | ----------------------------------------------- |
+| **`shouldEnableTCFDataCollection`** | boolean
|
+
+#### AFConsentData
+
+| Prop | Type |
+| ----------------- | ----------------------------------------------- |
+| **`data`** | IAppsFlyerConsent
|
+
+#### IAppsFlyerConsent
+
+| Prop | Type |
+| ----------------- | ----------------------------------------------- |
+| **`isUserSubjectToGDPR`** | boolean
|
+| **`hasConsentForDataUsage`** | boolean
|
+| **`hasConsentForAdsPersonalization`** | boolean
|
+
## Enums
diff --git a/docs/DMA.md b/docs/DMA.md
new file mode 100644
index 0000000..6550629
--- /dev/null
+++ b/docs/DMA.md
@@ -0,0 +1,105 @@
+# Set Consent For DMA Compliance
+
+Following the DMA regulations that were set by the European Commission, Google (and potentially other SRNs in the future) require to send them the user’s consent data in order to interact with them during the attribution process. In our latest plugin update (6.13.0), we've introduced two new public APIs, enhancing our support for user consent and data collection preferences in line with evolving digital market regulations.
+There are two alternative ways 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({shouldEnableTCFDataCollection : true});
+2. Initialize the SDK in manual start mode by simply adding manualStart: true
in the AppsFlyer.initSDK()
method.
+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()
+```typescript
+ AppsFlyer.initSDK({
+ appID: '1234567890',
+ devKey: 'your_dev_key',
+ isDebug: true,
+ registerOnDeepLink: true,
+ minTimeBetweenSessions: 6,
+ registerConversionListener: true,
+ registerOnAppOpenAttribution: false,
+ useReceiptValidationSandbox: true,
+ useUninstallSandbox: true,
+ manualStart: true // <--- Manual Start
+ });
+
+ .......
+
+ // CMP pseudocode procedure
+ if (cmpManager.hasConsent()) {
+ AppsFlyer.startSDK();
+ } else {
+ cmpManager.presentConsentDialogToUser()
+ .then(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, distinguishing between cases when GDPR applies or not.
+
+### 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 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()`.
+```typescript
+ // If the user is subject to GDPR - collect the consent data
+ // or retrieve it from the storage
+ ......
+ // Set the consent data to the SDK:
+ let gdprConsent = AppsFlyerConsent.forGDPRUser(true, false);
+
+ AppsFlyer.setConsentData({data : gdprConsent});
+
+ AppsFlyer.initSDK({
+ appID: '1234567890',
+ devKey: 'your_dev_key',
+ isDebug: true,
+ registerOnDeepLink: true,
+ minTimeBetweenSessions: 6,
+ registerConversionListener: true,
+ registerOnAppOpenAttribution: false,
+ useReceiptValidationSandbox: true,
+ useUninstallSandbox: true,
+ });
+ .......
+```
+
+### When GDPR does not apply to the user
+
+If GDPR doesn’t apply to the user perform the following:
+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()`.
+```typescript
+ // If the user is not subject to GDPR:
+ let nonGdprUserConsentData = AppsFlyerConsent.forNonGDPRUser();
+
+ AppsFlyer.setConsentData({data : nonGdprUserConsentData});
+
+ AppsFlyer.initSDK({
+ appID: '1234567890',
+ devKey: 'your_dev_key',
+ isDebug: true,
+ registerOnDeepLink: true,
+ minTimeBetweenSessions: 6,
+ registerConversionListener: true,
+ registerOnAppOpenAttribution: false,
+ useReceiptValidationSandbox: true,
+ useUninstallSandbox: true,
+ });
+ .......
+```
+
+
diff --git a/examples/CapacitorReact/package.json b/examples/CapacitorReact/package.json
index d163fab..70c7bcb 100644
--- a/examples/CapacitorReact/package.json
+++ b/examples/CapacitorReact/package.json
@@ -49,10 +49,17 @@
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
+ "sync": "npx cap sync",
"installFromNpm": "npm install appsflyer-capacitor-plugin && npx cap sync",
"installQA": "npm install appsflyer-capacitor-plugin@QA && npx cap sync",
"removePlugin": "npm uninstall appsflyer-capacitor-plugin && npx cap sync",
- "installLocal": "npm install ../../ && npx cap sync"
+ "installLocal": "npm install ../../ && npx cap sync",
+ "addIos": "npx cap add ios",
+ "addAndroid": "npx cap add android",
+ "openIos": "npx cap open ios",
+ "openAndroid": "npx cap open android",
+ "runIos": "npx cap run ios",
+ "runAndroid": "npx cap run android"
},
"eslintConfig": {
"extends": [
diff --git a/ios/Plugin/AppsFlyerConstants.swift b/ios/Plugin/AppsFlyerConstants.swift
index 0f31387..f9b9c6b 100644
--- a/ios/Plugin/AppsFlyerConstants.swift
+++ b/ios/Plugin/AppsFlyerConstants.swift
@@ -29,7 +29,7 @@ class AppsFlyerConstants {
static let AF_ONELINK_ID = "onelinkID"
static let AF_ONELINK_DOMAIN = "domains"
static let AF_DEEPLINK_URLS = "urls"
- static let AF_PATH = "path"
+ static let AF_PATH = "path"
static let AF_UID = "uid"
static let AF_ANONYMIZE_USER = "anonymizeUser"
static let AF_STOP = "stop"
@@ -68,5 +68,9 @@ class AppsFlyerConstants {
static let AF_EVENT_PARAMETERS = "eventParameters"
static let AF_PARTNER_ID = "partnerId"
static let AF_DATA = "data"
-
+ static let AF_ENABLE_TCF_DATA_COLLECTION = "shouldEnableTCFDataCollection"
+ static let AF_MANUAL_START = "manualStart"
+ static let AF_IS_SUBJECTED_TO_DGPR = "isUserSubjectToGDPR"
+ static let AF_CONSENT_FOR_DATA_USAGE = "hasConsentForDataUsage"
+ static let AF_CONSENT_FOR_ADS_PERSONALIZATION = "hasConsentForAdsPersonalization"
}
diff --git a/ios/Plugin/AppsFlyerPlugin.m b/ios/Plugin/AppsFlyerPlugin.m
index 38bc975..e25d470 100644
--- a/ios/Plugin/AppsFlyerPlugin.m
+++ b/ios/Plugin/AppsFlyerPlugin.m
@@ -38,6 +38,10 @@
CAP_PLUGIN_METHOD(setPartnerData, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(logInvite, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(setSharingFilterForPartners, CAPPluginReturnPromise);
+ CAP_PLUGIN_METHOD(enableTCFDataCollection, CAPPluginReturnNone);
+ CAP_PLUGIN_METHOD(setConsentData, CAPPluginReturnNone);
+ CAP_PLUGIN_METHOD(startSDK, CAPPluginReturnPromise);
+
diff --git a/ios/Plugin/AppsFlyerPlugin.swift b/ios/Plugin/AppsFlyerPlugin.swift
index 8207035..f6627c2 100644
--- a/ios/Plugin/AppsFlyerPlugin.swift
+++ b/ios/Plugin/AppsFlyerPlugin.swift
@@ -5,7 +5,7 @@ import AppsFlyerLib
@objc(AppsFlyerPlugin)
public class AppsFlyerPlugin: CAPPlugin {
- private let APPSFLYER_PLUGIN_VERSION = "6.12.1"
+ private let APPSFLYER_PLUGIN_VERSION = "6.13.0"
private var conversion = true
private var oaoa = true
private var udl = false
@@ -34,7 +34,8 @@ public class AppsFlyerPlugin: CAPPlugin {
let debug = call.getBool(AppsFlyerConstants.AF_DEBUG, false)
let sandbox = call.getBool(AppsFlyerConstants.AF_SANDBOX, false)
- let receiptSandbox = call.getBool(AppsFlyerConstants.AF_RECEIPT_SANDBOX , false)
+ let receiptSandbox = call.getBool(AppsFlyerConstants.AF_RECEIPT_SANDBOX, false)
+ let manualStart = call.getBool(AppsFlyerConstants.AF_MANUAL_START, false)
conversion = call.getBool(AppsFlyerConstants.AF_CONVERSION_LISTENER, true)
oaoa = call.getBool(AppsFlyerConstants.AF_OAOA, true)
@@ -69,18 +70,25 @@ public class AppsFlyerPlugin: CAPPlugin {
appsflyer.waitForATTUserAuthorization(timeoutInterval: Double(attInterval!))
}
#endif
+
+ if !manualStart {
+ startSDK(call)
+ } else {
+ call.resolve(["res": "SDK initiated successfully. SDK has NOT started yet"])
+ }
+ }
+
+ @objc func startSDK(_ call: CAPPluginCall) {
NotificationCenter.default.addObserver(self, selector: #selector(sendLaunch), name: UIApplication.didBecomeActiveNotification, object: nil)
- appsflyer.start(completionHandler: { (dictionnary, error) in
- if (error != nil){
- call.reject(error!.localizedDescription)
- return
+ AppsFlyerLib.shared().start { dictionary, error in
+ if let error = error {
+ call.reject(error.localizedDescription)
} else {
- call.resolve(["res":"ok"])
- return
+ call.resolve(["res": "success"])
}
- })
+ }
}
@objc func logEvent(_ call: CAPPluginCall){
@@ -240,6 +248,36 @@ public class AppsFlyerPlugin: CAPPlugin {
@objc func setDisableNetworkData(_ call: CAPPluginCall){
call.unavailable("Android only method - has no effact on iOS apps")
}
+
+ @objc func enableTCFDataCollection(_ call: CAPPluginCall){
+ guard let shouldEnableTCFDataCollection = call.getBool(AppsFlyerConstants.AF_ENABLE_TCF_DATA_COLLECTION) else {
+ call.reject("Missing boolean value shouldEnableTCFDataCollection")
+ return
+ }
+ AppsFlyerLib.shared().enableTCFDataCollection(shouldEnableTCFDataCollection)
+ }
+
+ @objc func setConsentData(_ call: CAPPluginCall) {
+ guard let consentData = call.getObject("data") else {
+ call.reject("Consent data is missing")
+ return
+ }
+
+ let isUserSubjectToGDPR = consentData[AppsFlyerConstants.AF_IS_SUBJECTED_TO_DGPR] as? Bool ?? false
+ let hasConsentForDataUsage = consentData[AppsFlyerConstants.AF_CONSENT_FOR_DATA_USAGE] as? Bool ?? false
+ let hasConsentForAdsPersonalization = consentData[AppsFlyerConstants.AF_CONSENT_FOR_ADS_PERSONALIZATION] as? Bool ?? false
+
+ let consentObject: AppsFlyerConsent
+ if isUserSubjectToGDPR {
+ consentObject = AppsFlyerConsent(forGDPRUserWithHasConsentForDataUsage: hasConsentForDataUsage, hasConsentForAdsPersonalization: hasConsentForAdsPersonalization)
+ } else {
+ consentObject = AppsFlyerConsent(nonGDPRUser: ())
+ }
+
+ AppsFlyerLib.shared().setConsentData(consentObject)
+
+ call.resolve()
+ }
@objc func anonymizeUser(_ call: CAPPluginCall){
guard let anonymize = call.getBool(AppsFlyerConstants.AF_ANONYMIZE_USER) else{
diff --git a/ios/Podfile b/ios/Podfile
index 78905ba..06f963a 100644
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -10,7 +10,7 @@ end
target 'Plugin' do
capacitor_pods
- pod 'AppsFlyerFramework', ' 6.10.1'
+ pod 'AppsFlyerFramework', ' 6.13.0'
end
target 'PluginTests' do
diff --git a/package.json b/package.json
index 4084cf5..a910d90 100644
--- a/package.json
+++ b/package.json
@@ -1,9 +1,9 @@
{
"name": "appsflyer-capacitor-plugin",
- "version": "6.12.1",
- "iosSdkVersion": "6.12.1",
- "androidSdkVersion": "6.12.1",
- "buildNumber": "10",
+ "version": "6.13.0",
+ "iosSdkVersion": "6.13.0",
+ "androidSdkVersion": "6.13.0",
+ "buildNumber": "11",
"description": "AppsFlyer SDK plugin for Capacitor",
"main": "dist/plugin.cjs.js",
"module": "dist/esm/index.js",
diff --git a/src/appsflyer_interfaces.ts b/src/appsflyer_interfaces.ts
index 895fb85..49284b8 100644
--- a/src/appsflyer_interfaces.ts
+++ b/src/appsflyer_interfaces.ts
@@ -10,7 +10,7 @@ export interface AFInit{
useReceiptValidationSandbox?: boolean;
minTimeBetweenSessions?: number
deepLinkTimeout?: number
-
+ manualStart?: boolean;
}
export interface AFEvent{
@@ -112,3 +112,39 @@ export interface AFAppendToDeepLink{contains: string;
parameters: StringMap;
}
+export interface AFEnableTCFDataCollection { shouldEnableTCFDataCollection: boolean }
+
+export interface IAppsFlyerConsent {
+ isUserSubjectToGDPR: boolean,
+ hasConsentForDataUsage?: boolean,
+ hasConsentForAdsPersonalization?: boolean
+}
+
+class AppsFlyerConsentClass implements IAppsFlyerConsent {
+ public isUserSubjectToGDPR: boolean;
+ public hasConsentForDataUsage?: boolean;
+ public hasConsentForAdsPersonalization?: boolean;
+
+ private constructor(isUserSubjectToGDPR: boolean, hasConsentForDataUsage?: boolean, hasConsentForAdsPersonalization?: boolean) {
+ this.isUserSubjectToGDPR = isUserSubjectToGDPR;
+ this.hasConsentForDataUsage = hasConsentForDataUsage;
+ this.hasConsentForAdsPersonalization = hasConsentForAdsPersonalization;
+ }
+
+ static forGDPRUser(hasConsentForDataUsage: boolean, hasConsentForAdsPersonalization: boolean): IAppsFlyerConsent {
+ return new AppsFlyerConsentClass(true, hasConsentForDataUsage, hasConsentForAdsPersonalization);
+ }
+
+ static forNonGDPRUser(): IAppsFlyerConsent {
+ return new AppsFlyerConsentClass(false);
+ }
+}
+
+export const AppsFlyerConsent = {
+ forGDPRUser: AppsFlyerConsentClass.forGDPRUser,
+ forNonGDPRUser: AppsFlyerConsentClass.forNonGDPRUser
+};
+
+export interface AFConsentData {
+ data: IAppsFlyerConsent
+}
diff --git a/src/definitions.ts b/src/definitions.ts
index af622be..c94c2e5 100644
--- a/src/definitions.ts
+++ b/src/definitions.ts
@@ -36,7 +36,10 @@ import type {
AFLatLng,
AFPhone,
AFPartnerData,
- AFLogInvite
+ AFLogInvite,
+ AFEnableTCFDataCollection,
+ AFConsentData
+
} from "./appsflyer_interfaces";
export interface AppsFlyerPlugin {
@@ -63,6 +66,11 @@ export interface AppsFlyerPlugin {
*/
initSDK(options: AFInit): Promise;
+ /**
+ * Use this method to start AppsFlyer SDK, only on manual start mode.
+ */
+ startSDK(): Promise;
+
/**
* Log an in-app event.
*
@@ -246,5 +254,19 @@ export interface AppsFlyerPlugin {
* @param disable Defaults to false
*/
setDisableNetworkData(disable : AFDisable): Promise;
+
+ /**
+ * Use to opt-in/out the automatic collection of consent data, for users who use a CMP.
+ * Flag value will be persisted between app sessions.
+ */
+ enableTCFDataCollection(shouldEnableTCFDataCollection: AFEnableTCFDataCollection): Promise
+
+ /**
+ * Use to set user consent data manualy.
+ * if your app doesn't use a CMP compatible with TCF v2.2, use the following method to manualy provide the consent data directly to the SDK.
+ * @param data: AppsFlyerConsent object.
+ */
+ setConsentData(data : AFConsentData): Promise
+
}