Skip to content

Commit 1039575

Browse files
authored
Merge pull request #665 from AppsFlyerSDK/releases/6.x.x/6.17.x/6.17.8-rc1
6.17.8
2 parents f9f3857 + 89e5f37 commit 1039575

25 files changed

+828
-494
lines changed

.eslintrc.js

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/**
2+
* ESLint configuration for react-native-appsflyer plugin
3+
* Optimized for TypeScript + React Native library development
4+
*/
5+
6+
module.exports = {
7+
root: true,
8+
parser: '@typescript-eslint/parser',
9+
parserOptions: {
10+
ecmaVersion: 2020,
11+
sourceType: 'module',
12+
ecmaFeatures: { jsx: true },
13+
},
14+
15+
env: {
16+
node: true,
17+
es6: true,
18+
},
19+
20+
plugins: [
21+
'@typescript-eslint',
22+
],
23+
24+
extends: [
25+
'eslint:recommended',
26+
'plugin:@typescript-eslint/recommended',
27+
],
28+
29+
rules: {
30+
// JS/TS hygiene
31+
'no-console': 'off',
32+
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
33+
'@typescript-eslint/no-explicit-any': 'off',
34+
'@typescript-eslint/no-var-requires': 'off', // Allow require() in CommonJS files
35+
36+
// Import/export rules - disabled because TypeScript handles this
37+
// and our index.d.ts properly declares all exports for ESLint compatibility
38+
'import/default': 'off',
39+
'import/named': 'off',
40+
'import/no-unresolved': 'off',
41+
},
42+
overrides: [
43+
{
44+
files: ['expo/**/*.js'],
45+
rules: {
46+
'@typescript-eslint/no-var-requires': 'off', // Expo config plugins use require()
47+
},
48+
},
49+
{
50+
files: ['**/*.ts', '**/*.tsx'],
51+
rules: {
52+
// TypeScript-specific rules
53+
'no-undef': 'off', // TypeScript handles this
54+
},
55+
},
56+
],
57+
58+
ignorePatterns: [
59+
'node_modules/**',
60+
'demos/**',
61+
'android/**',
62+
'ios/**',
63+
'build/**',
64+
'dist/**',
65+
'__tests__/**',
66+
'*.config.js',
67+
'babel.config.js',
68+
'metro.config.js',
69+
'jest.config.js',
70+
'react-native.config.js',
71+
],
72+
};

CHANGELOG.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
1+
## 6.17.8
2+
Release date: *2025-12-17*
3+
4+
- React Native >> Update Android SDK to 6.17.5
5+
- React Native >> Update iOS SDK to 6.17.8
6+
- React Native >> Update iOS Purchase Connector to 6.17.8
7+
- React Native >> Fixed callback double-invocation crash in React Native New Architecture on Android
8+
- React Native >> Fixed TypeScript return type for `validateAndLogInAppPurchaseV2`
9+
- React Native >> Fixed TypeScript type issues (`onFailure``OnFailure`, import paths)
10+
- React Native >> Fixed Expo Android build failure related to backup rules
11+
- React Native >> Added `preferAppsFlyerBackupRules` flag for Expo Android (default: false)
12+
- React Native >> Enhanced iOS Swift header import with CocoaPods fallback
13+
- React Native >> Added ESLint configuration and lint scripts
14+
- React Native >> Update Plugin to v6.17.8
15+
116
## 6.17.7
2-
Release date: *2025-10-22*
17+
Release date: 2025-10-22
318

419
- React Native >> Update Android SDK to 6.17.7
520
- React Native >> Update iOS SDK to 6.17.7

Docs/RN_API.md

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,9 @@ appsFlyer.setSharingFilterForPartners(['googleadwords_int', 'all']);
596596

597597
### validateAndLogInAppPurchase
598598
`validateAndLogInAppPurchase(purchaseInfo, successC, errorC): Response<string>`
599+
600+
> ⚠️ **Deprecated**: This API is deprecated. Use `validateAndLogInAppPurchaseV2` instead.
601+
599602
Receipt validation is a secure mechanism whereby the payment platform (e.g. Apple or Google) validates that an in-app purchase indeed occurred as reported.
600603
Learn more - https://support.appsflyer.com/hc/en-us/articles/207032106-Receipt-validation-for-in-app-purchases
601604
❗Important❗ for iOS - set SandBox to ```true```
@@ -628,20 +631,22 @@ appsFlyer.validateAndLogInAppPurchase(info, res => console.log(res), err => cons
628631
```
629632

630633
---
631-
<!--
632-
## New In-App Purchase Validation API
633-
The new `validateAndLogInAppPurchase` API uses `AFPurchaseDetails` and `AFPurchaseType` enum for structured purchase validation.
634634

635-
### AFPurchaseType Enum
635+
### validateAndLogInAppPurchaseV2
636+
`validateAndLogInAppPurchaseV2(purchaseDetails, additionalParameters, callback): void`
637+
638+
The `validateAndLogInAppPurchaseV2` API uses `AFPurchaseDetails` and `AFPurchaseType` enum for structured purchase validation.
639+
640+
#### AFPurchaseType Enum
636641

637642
```javascript
638643
import { AFPurchaseType } from 'react-native-appsflyer';
639644

640645
AFPurchaseType.SUBSCRIPTION // "subscription"
641-
AFPurchaseType.ONE_TIME_PURCHASE // "one-time-purchase"
646+
AFPurchaseType.ONE_TIME_PURCHASE // "one_time_purchase"
642647
```
643648

644-
### AFPurchaseDetails Interface
649+
#### AFPurchaseDetails Interface
645650

646651
```typescript
647652
interface AFPurchaseDetails {
@@ -651,15 +656,15 @@ interface AFPurchaseDetails {
651656
}
652657
```
653658

654-
### Usage Example
659+
#### Usage Example
655660

656661
```javascript
657662
import appsFlyer, { AFPurchaseType } from 'react-native-appsflyer';
658663

659664
const purchaseDetails = {
660-
purchaseType: AFPurchaseType.SUBSCRIPTION,
661-
transactionId: "google_play_purchase_token_123",
662-
productId: "com.example.app.premium_monthly"
665+
purchaseType: AFPurchaseType.ONE_TIME_PURCHASE,
666+
transactionId: "2000000569065806",
667+
productId: "deviceIdconsumableid"
663668
};
664669

665670
const additionalParams = {
@@ -670,10 +675,19 @@ const additionalParams = {
670675
appsFlyer.validateAndLogInAppPurchaseV2(
671676
purchaseDetails,
672677
additionalParams,
673-
(result) => console.log(result)
678+
(result) => {
679+
if (result.error) {
680+
console.error('Validation failed:', result.error);
681+
} else {
682+
console.log('Validation success:', result);
683+
}
684+
}
674685
);
675686
```
676-
-->
687+
**Important Notes:**
688+
- The callback receives both `result` and `error` parameters
689+
- Always check for `error` first before processing `result`
690+
- Handle the case where `AFSDKPurchaseDetails` creation might fail
677691
---
678692

679693
### updateServerUninstallToken
@@ -824,12 +838,39 @@ appsFlyer.enableTCFDataCollection(true);
824838
### setConsentData
825839
`setConsentData(consentObject): void`
826840

827-
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.<br>
828-
The AppsFlyerConsent object has 2 methods:
841+
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.
829842

830-
1. `AppsFlyerConsent.forNonGDPRUser`: Indicates that GDPR doesn’t apply to the user and generates nonGDPR consent object. This method doesn’t accept any parameters.
831-
2. `AppsFlyerConsent.forGDPRUser`: create an AppsFlyerConsent object with 2 parameters:
843+
**Recommended approach (since v6.16.2):**
844+
Use the `AppsFlyerConsent` constructor:
845+
846+
```javascript
847+
import appsFlyer, {AppsFlyerConsent} from 'react-native-appsflyer';
832848

849+
// Full consent for GDPR user
850+
const consent1 = new AppsFlyerConsent(true, true, true, true);
851+
852+
// No consent for GDPR user
853+
const consent2 = new AppsFlyerConsent(true, false, false, false);
854+
855+
// Non-GDPR user
856+
const consent3 = new AppsFlyerConsent(false);
857+
858+
appsFlyer.setConsentData(consent1);
859+
```
860+
861+
**Constructor parameters:**
862+
| parameter | type | description |
863+
| ---------- |----------|------------------ |
864+
| isUserSubjectToGDPR | boolean | Whether GDPR applies to the user (required) |
865+
| hasConsentForDataUsage | boolean | Consent for data usage (optional) |
866+
| hasConsentForAdsPersonalization | boolean | Consent for ads personalization (optional) |
867+
| hasConsentForAdStorage | boolean | Consent for ad storage (optional) |
868+
869+
**Deprecated approach (still supported):**
870+
The AppsFlyerConsent object has 2 deprecated methods:
871+
872+
1. `AppsFlyerConsent.forNonGDPRUser`: Indicates that GDPR doesn't apply to the user and generates nonGDPR consent object. This method doesn't accept any parameters.
873+
2. `AppsFlyerConsent.forGDPRUser`: create an AppsFlyerConsent object with 2 parameters:
833874

834875
| parameter | type | description |
835876
| ---------- |----------|------------------ |

Docs/RN_EspIntegration.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,7 @@ adb shell am start -W -a android.intent.action.VIEW -d "https://your-onelink-dom
477477
<manifest xmlns:tools="http://schemas.android.com/tools">
478478
<application android:allowBackup="false" tools:replace="android:allowBackup">
479479
```
480+
See [AppsFlyer Android SDK documentation](https://dev.appsflyer.com/hc/docs/install-android-sdk#backup-rules) for more details.
480481

481482
**4. Package attribute deprecated:**
482483
- Remove `package="com.yourapp"` from AndroidManifest.xml

Docs/RN_ExpoInstallation.md

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ expo install react-native-appsflyer
2525
"react-native-appsflyer",
2626
{
2727
"shouldUseStrictMode": false, // optional – kids-apps strict mode
28-
"shouldUsePurchaseConnector": true // NEW – enables Purchase Connector
28+
"shouldUsePurchaseConnector": true, // optional – enables Purchase Connector
29+
"preferAppsFlyerBackupRules": false // optional – use AppsFlyer SDK backup rules (default: false)
2930
}
3031
]
3132
],
@@ -42,8 +43,40 @@ expo install react-native-appsflyer
4243
],
4344
...
4445
```
45-
### Fix for build failure with RN 0.76 and Expo 52
46-
To ensure seamless integration of the AppsFlyer plugin in your Expo-managed project, it’s essential to handle modifications to the AndroidManifest.xml correctly. Since direct edits to the AndroidManifest.xml aren’t feasible in the managed workflow, you’ll need to create a custom configuration to include the necessary changes.
46+
### Backup Rules Configuration (Android)
47+
48+
The AppsFlyer SDK includes built-in backup rules in its Android manifest to ensure accurate install/reinstall detection. By default, the plugin respects your app's backup rules and does not modify them.
49+
50+
**Default Behavior** (`preferAppsFlyerBackupRules: false` or omitted):
51+
- Your app's `android:dataExtractionRules` and `android:fullBackupContent` attributes are left untouched
52+
- You maintain full control over your app's backup policy
53+
- No manifest merge conflicts occur
54+
55+
**Opt-in Behavior** (`preferAppsFlyerBackupRules: true`):
56+
- If your app defines backup rules, they will be removed to let AppsFlyer SDK's built-in rules take precedence
57+
- This ensures AppsFlyer SDK's backup rules are used, which may improve install/reinstall detection accuracy
58+
- Use this flag if you want AppsFlyer SDK to manage backup rules for you
59+
60+
**When to use `preferAppsFlyerBackupRules: true`:**
61+
- You want AppsFlyer SDK to handle backup rules automatically
62+
- You're experiencing issues with install/reinstall detection that may be related to backup rules
63+
- You don't have specific backup requirements for your app
64+
65+
**Example configuration:**
66+
```json
67+
{
68+
"expo": {
69+
"plugins": [
70+
[
71+
"react-native-appsflyer",
72+
{
73+
"preferAppsFlyerBackupRules": true
74+
}
75+
]
76+
]
77+
}
78+
}
79+
```
4780

4881
### Handling dataExtractionRules Conflict
4982

@@ -123,3 +156,11 @@ Setting `"shouldUsePurchaseConnector": true` will:
123156

124157
* **iOS** – add the `PurchaseConnector` CocoaPod automatically
125158
* **Android** – add `appsflyer.enable_purchase_connector=true` to `gradle.properties`
159+
160+
### Plugin Options Summary
161+
162+
| Option | Type | Default | Description |
163+
|-------|------|---------|-------------|
164+
| `shouldUseStrictMode` | boolean | `false` | Enable strict mode for kids apps |
165+
| `shouldUsePurchaseConnector` | boolean | `false` | Enable Purchase Connector support |
166+
| `preferAppsFlyerBackupRules` | boolean | `false` | Remove app's backup rules to use AppsFlyer SDK's built-in rules (Android only) |

Docs/RN_InAppEvents.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ appsFlyer.logEvent(
5454

5555
---
5656
## In-app purchase validation
57+
58+
> ⚠️ **Deprecated**: The `validateAndLogInAppPurchase` API is deprecated. Use `validateAndLogInAppPurchaseV2` instead. See the [API reference](/Docs/RN_API.md#validateandloginapppurchasev2) for details.
59+
5760
Receipt validation is a secure mechanism whereby the payment platform (e.g. Apple or Google) validates that an in-app purchase indeed occurred as reported.
5861
Learn more [here](https://support.appsflyer.com/hc/en-us/articles/207032106-Receipt-validation-for-in-app-purchases).
5962

PurchaseConnector/models/canceled_state_context.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export class CanceledStateContext {
5252
class DeveloperInitiatedCancellation {
5353
constructor() {}
5454

55-
static fromJson(json: any): DeveloperInitiatedCancellation {
55+
static fromJson(_json: any): DeveloperInitiatedCancellation {
5656
// Here you would implement the conversion from JSON to DeveloperInitiatedCancellation instance
5757
return new DeveloperInitiatedCancellation();
5858
}
@@ -66,7 +66,7 @@ class DeveloperInitiatedCancellation {
6666
class ReplacementCancellation {
6767
constructor() {}
6868

69-
static fromJson(json: any): ReplacementCancellation {
69+
static fromJson(_json: any): ReplacementCancellation {
7070
// Here you would implement the conversion from JSON to ReplacementCancellation instance
7171
return new ReplacementCancellation();
7272
}
@@ -79,7 +79,7 @@ class ReplacementCancellation {
7979
class SystemInitiatedCancellation {
8080
constructor() {}
8181

82-
static fromJson(json: any): SystemInitiatedCancellation {
82+
static fromJson(_json: any): SystemInitiatedCancellation {
8383
// Here you would implement the conversion from JSON to SystemInitiatedCancellation instance
8484
return new SystemInitiatedCancellation();
8585
}

PurchaseConnector/models/test_purchase.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ interface TestPurchaseJson {}
33
export class TestPurchase {
44
constructor() {}
55

6-
static fromJson(json: TestPurchaseJson): TestPurchase {
6+
static fromJson(_json: TestPurchaseJson): TestPurchase {
77
return new TestPurchase();
88
}
99

PurchaseConnector/utils/connector_callbacks.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { IosError, JVMThrowable } from "../models";
1+
import { IosError } from "../models/ios_errors";
2+
import { JVMThrowable } from "../models/jvm_throwable";
23

34
// Type definition for a general-purpose listener.
45
export type PurchaseConnectorListener = (data: any) => void;

0 commit comments

Comments
 (0)