diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4fc48a90..c303dd1e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,7 @@
# Versions
+## 6.1.20
+- Unified deep linking
+
## 6.1.10
- iOS SDK 6.1.1
- Android SDK 6.1.0
diff --git a/Docs/API.md b/Docs/API.md
index a936bd8b..9a835e6e 100755
--- a/Docs/API.md
+++ b/Docs/API.md
@@ -51,7 +51,7 @@ The dev key is required for all apps and the appID is required only for iOS.
{
console.log(res);
diff --git a/Docs/Guides.md b/Docs/Guides.md
index 3747c819..a1e6b9f1 100755
--- a/Docs/Guides.md
+++ b/Docs/Guides.md
@@ -8,6 +8,7 @@
- [Deep Linking](#deeplinking)
- [Deferred Deep Linking (Get Conversion Data)](#deferred-deep-linking)
- [Direct Deep Linking](#direct-deep-linking)
+ - [Unified deep linking](#Unified-deep-linking)
- [iOS Deeplink Setup](#iosdeeplinks)
- [Android Deeplink Setup](#android-deeplinks)
- [Uninstall](#measure-app-uninstalls)
@@ -41,12 +42,12 @@ appsFlyer.initSdk(
-#### The 2 Deep Linking Types:
-Since users may or may not have the mobile app installed, there are 2 types of deep linking:
+#### The 3 Deep Linking Types:
+Since users may or may not have the mobile app installed, there are 3 types of deep linking:
1. Deferred Deep Linking - Serving personalized content to new or former users, directly after the installation.
2. Direct Deep Linking - Directly serving personalized content to existing users, which already have the mobile app installed.
-
+3. Unified deep linking - Unified deep linking sends new and existing users to a specific in-app activity as soon as the app is opened.
For more info please check out the [OneLink™ Deep Linking Guide](https://support.appsflyer.com/hc/en-us/articles/208874366-OneLink-Deep-Linking-Guide#Intro).
### 1. Deferred Deep Linking (Get Conversion Data)
@@ -103,6 +104,45 @@ appsFlyer.initSdk(/*...*/);
The `appsFlyer.onAppOpenAttribution` returns function to unregister this event listener. If you want to remove the listener for any reason, you can simply call `onAppOpenAttributionCanceller()`. This function will call `NativeAppEventEmitter.remove()`.
+
+
+### 3. Unified deep linking
+In order to use the unified deep link you need to send the `onDeepLinkListener: true` flag inside the object that sent to the sdk.
+**NOTE:** when sending this flag, the sdk will ignore `onAppOpenAttribution`!
+For more information about this api, please check [OneLink Guide Here](https://dev.appsflyer.com/docs/android-unified-deep-linking)
+
+
+```javascript
+var onDeepLinkCanceller = appsFlyer.onDeepLink(res => {
+ console.log('onDeepLinking: ' + JSON.stringify(res));
+ console.log('status: '+ res.status);
+ console.log('type: '+ res.type);
+})
+
+appsFlyer.initSdk(
+ {
+ devKey: 'K2***********99',
+ isDebug: false,
+ appId: '41*****44',
+ onInstallConversionDataListener: true,
+ onDeepLinkListener: true
+ },
+ (result) => {
+ console.log(result);
+ },
+ (error) => {
+ console.error(error);
+ }
+);
+```
+
+**Note:** The code implementation for `onDeepLink` must be made **prior to the initialization** code of the SDK.
+
+**Important**
+
+The `appsFlyer.onDeepLink` returns function to unregister this event listener. If you want to remove the listener for any reason, you can simply call `onDeepLinkCanceller()`. This function will call `NativeAppEventEmitter.remove()`.
+
+
### *Example:*
@@ -114,6 +154,10 @@ var onAppOpenAttributionCanceller = appsFlyer.onAppOpenAttribution((res) => {
console.log(res);
});
+var onDeepLinkCanceller = appsFlyer.onDeepLink(res => {
+ console.log('onDeepLinking: ' + JSON.stringify(res));
+})
+
var onInstallConversionDataCanceller = appsFlyer.onInstallConversionData(
(res) => {
if (JSON.parse(res.data.is_first_launch) == true) {
@@ -158,6 +202,11 @@ class App extends Component<{}> {
console.log('unregister onAppOpenAttributionCanceller');
onAppOpenAttributionCanceller = null;
}
+ if (onDeepLinkCanceller) {
+ onDeepLinkCanceller();
+ console.log('unregister onDeepLinkCanceller');
+ onDeepLinkCanceller = null;
+ }
}
}
```
diff --git a/README.md b/README.md
index 6994b38c..3b3c2ac6 100755
--- a/README.md
+++ b/README.md
@@ -117,7 +117,8 @@ appsFlyer.initSdk(
devKey: 'K2***********99',
isDebug: false,
appId: '41*****44',
- onInstallConversionDataListener: true,
+ onInstallConversionDataListener: true, //Optional
+ onDeepLinkListener: true, //Optional
},
(result) => {
console.log(result);
@@ -134,6 +135,7 @@ appsFlyer.initSdk(
| appId | Your iTunes [application ID](https://support.appsflyer.com/hc/en-us/articles/207377436-Adding-a-new-app#available-in-the-app-store-google-play-store-windows-phone-store) (iOS only) |
| isDebug | Debug mode - set to `true` for testing only |
|onInstallConversionDataListener| Set listener for SDK init response (Optional. default=true) |
+|onDeepLinkListener| Set listener for DDL response (Optional. default=false) |
## 📖 Guides
diff --git a/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerConstants.java b/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerConstants.java
index a8566aa9..973d6f23 100755
--- a/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerConstants.java
+++ b/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerConstants.java
@@ -20,6 +20,7 @@ public class RNAppsFlyerConstants {
final static String afEmails = "emails";
final static String afConversionData = "onInstallConversionDataListener";
+ final static String afDeepLink = "onDeepLinkListener";
final static String afSuccess = "success";
final static String afFailure = "failure";
@@ -27,6 +28,7 @@ public class RNAppsFlyerConstants {
final static String afOnAppOpenAttribution = "onAppOpenAttribution";
final static String afOnInstallConversionFailure = "onInstallConversionFailure";
final static String afOnInstallConversionDataLoaded = "onInstallConversionDataLoaded";
+ final static String afOnDeepLinking = "onDeepLinking";
final static String INVITE_FAIL = "Could not create invite link";
final static String INVITE_CHANNEL = "channel";
diff --git a/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerModule.java b/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerModule.java
index bcb0d89e..d45afc87 100755
--- a/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerModule.java
+++ b/android/src/main/java/com/appsflyer/reactnative/RNAppsFlyerModule.java
@@ -8,6 +8,11 @@
import android.net.Uri;
import android.util.Log;
+import androidx.annotation.NonNull;
+import com.appsflyer.reactnative.RNUtil;
+import com.appsflyer.deeplink.DeepLink;
+import com.appsflyer.deeplink.DeepLinkListener;
+import com.appsflyer.deeplink.DeepLinkResult;
import com.appsflyer.*;
import com.appsflyer.AFInAppEventType;
import com.appsflyer.AppsFlyerConversionListener;
@@ -38,6 +43,7 @@
import java.util.ArrayList;
import static com.appsflyer.reactnative.RNAppsFlyerConstants.*;
+import static com.appsflyer.reactnative.RNAppsFlyerConstants.afOnDeepLinking;
public class RNAppsFlyerModule extends ReactContextBaseJavaModule {
@@ -120,6 +126,7 @@ private String callSdkInternal(ReadableMap _options) {
String devKey;
boolean isDebug;
boolean isConversionData;
+ boolean isDeepLinking;
AppsFlyerLib instance = AppsFlyerLib.getInstance();
@@ -136,8 +143,13 @@ private String callSdkInternal(ReadableMap _options) {
if (isDebug == true) {
Log.d("AppsFlyer", "Starting SDK");
}
+ isDeepLinking = options.optBoolean(afDeepLink, false);
+
instance.init(devKey, (isConversionData == true) ? registerConversionListener() : null, application.getApplicationContext());
+ if (isDeepLinking) {
+ instance.subscribeForDeepLink(registerDeepLinkListener());
+ }
Intent intent = null;
Activity currentActivity = getCurrentActivity();
@@ -152,6 +164,32 @@ private String callSdkInternal(ReadableMap _options) {
return null;
}
+ private DeepLinkListener registerDeepLinkListener() {
+ return new DeepLinkListener() {
+ @Override
+ public void onDeepLinking(@NonNull DeepLinkResult deepLinkResult) {
+ DeepLinkResult.Error dlError = deepLinkResult.getError();
+ if (dlError != null) {
+ sendEvent(reactContext, afOnDeepLinking, dlError.toString());
+ }
+ JSONObject deepLinkObj = new JSONObject();
+ try {
+ deepLinkObj.put("status", afSuccess);
+ deepLinkObj.put("deepLinkStatus", deepLinkResult.getStatus());
+ deepLinkObj.put("type", afOnDeepLinking);
+ deepLinkObj.put("data", deepLinkResult.getDeepLink().toString());
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ try {
+ sendEvent(reactContext, afOnDeepLinking,deepLinkObj.toString());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ };
+ }
+
private AppsFlyerConversionListener registerConversionListener() {
return new AppsFlyerConversionListener() {
@@ -174,48 +212,49 @@ public void onConversionDataSuccess(Map conversionData) {
public void onConversionDataFail(String errorMessage) {
handleError(afOnInstallConversionFailure, errorMessage);
}
+ };
+ }
- private void handleSuccess(String eventType, Map conversionData, Map attributionData) {
- JSONObject obj = new JSONObject();
+ private void handleSuccess(String eventType, Map conversionData, Map attributionData) {
+ JSONObject obj = new JSONObject();
- try {
- JSONObject data = new JSONObject(conversionData == null ? attributionData : conversionData);
- obj.put("status", afSuccess);
- obj.put("type", eventType);
- obj.put("data", data);
- if (eventType.equals(afOnInstallConversionDataLoaded)) {
- sendEvent(reactContext, afOnInstallConversionDataLoaded, obj.toString());
- } else if (eventType.equals(afOnAppOpenAttribution)) {
- sendEvent(reactContext, afOnAppOpenAttribution, obj.toString());
- }
- } catch (JSONException e) {
- e.printStackTrace();
- }
+ try {
+ JSONObject data = new JSONObject(conversionData == null ? attributionData : conversionData);
+ obj.put("status", afSuccess);
+ obj.put("type", eventType);
+ obj.put("data", data);
+ if (eventType.equals(afOnInstallConversionDataLoaded)) {
+ sendEvent(reactContext, afOnInstallConversionDataLoaded, obj.toString());
+ } else if (eventType.equals(afOnAppOpenAttribution)) {
+ sendEvent(reactContext, afOnAppOpenAttribution, obj.toString());
}
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
- private void handleError(String eventType, String errorMessage) {
- JSONObject obj = new JSONObject();
+ private void handleError(String eventType, String errorMessage) {
+ JSONObject obj = new JSONObject();
- try {
- obj.put("status", afFailure);
- obj.put("type", eventType);
- obj.put("data", errorMessage);
- sendEvent(reactContext, afOnInstallConversionDataLoaded, obj.toString());
- } catch (JSONException e) {
- e.printStackTrace();
- }
- }
+ try {
+ obj.put("status", afFailure);
+ obj.put("type", eventType);
+ obj.put("data", errorMessage);
+ sendEvent(reactContext, afOnInstallConversionDataLoaded, obj.toString());
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
- private void sendEvent(ReactContext reactContext,
- String eventName,
- Object params) {
- reactContext
- .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
- .emit(eventName, params);
- }
- };
+ private void sendEvent(ReactContext reactContext,
+ String eventName,
+ Object params) {
+ reactContext
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
+ .emit(eventName, params);
}
+
private String logEventInternal(final String eventName, ReadableMap eventData) {
if (eventName.trim().equals("")) {
diff --git a/index.d.ts b/index.d.ts
index 7062b963..e4aedca0 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -10,7 +10,7 @@ declare module "react-native-appsflyer" {
type: "onAppOpenAttribution"
| "onInstallConversionDataLoaded"
| "onAttributionFailure"
- | "onInstallConversionFailure",
+ | "onInstallConversionFailure"
data: {
is_first_launch: "true" | "false";
media_source: string;
@@ -30,6 +30,7 @@ declare module "react-native-appsflyer" {
appId?: string; // iOS only
isDebug?: boolean;
onInstallConversionDataListener?: boolean;
+ onDeepLinkListener?: boolean;
timeToWaitForATTUserAuthorization?: number; // iOS only
}
@@ -62,6 +63,7 @@ declare module "react-native-appsflyer" {
onInstallConversionData(callback: (data: ConversionData) => any): () => void;
onInstallConversionFailure(callback: (data: ConversionData) => any): () => void;
onAppOpenAttribution(callback: (data: any) => any): () => void;
+ onDeepLink(callback: (data: any) => any): () => void;
initSdk(options: InitSDKOptions): Promise;
initSdk(options: InitSDKOptions, successC: SuccessCB, errorC: ErrorCB): void;
logEvent(eventName: string, eventValues: object): Promise;
diff --git a/index.js b/index.js
index 797e8f37..a97c2d91 100755
--- a/index.js
+++ b/index.js
@@ -381,6 +381,31 @@ appsFlyer.onAppOpenAttribution = callback => {
};
};
+appsFlyer.onDeepLink = callback => {
+
+ const listener = appsFlyerEventEmitter.addListener(
+ "onDeepLinking",
+ _data => {
+ if (callback && typeof callback === typeof Function) {
+ try {
+ let data = JSON.parse(_data);
+ callback(data);
+ } catch (_error) {
+ callback(new AFParseJSONException("Invalid data structure", _data));
+ }
+ }
+ }
+ );
+
+
+ eventsMap["onDeepLinking"] = listener;
+
+ // unregister listener (suppose should be called from componentWillUnmount() )
+ return function remove() {
+ listener.remove();
+ };
+};
+
/**
* Anonymize user Data.
* Use this API during the SDK Initialization to explicitly anonymize a user's installs, events and sessions.
diff --git a/ios/RNAppsFlyer.h b/ios/RNAppsFlyer.h
index b8fdcaf8..669f6fa6 100755
--- a/ios/RNAppsFlyer.h
+++ b/ios/RNAppsFlyer.h
@@ -40,6 +40,8 @@ static NSString *const IOS_14_ONLY = @"Feature only supported o
#define afOnAppOpenAttribution @"onAppOpenAttribution"
#define afOnInstallConversionFailure @"onInstallConversionFailure"
#define afOnInstallConversionDataLoaded @"onInstallConversionDataLoaded"
+ #define afDeepLink @"onDeepLinkListener"
+ #define afOnDeepLinking @"onDeepLinking"
// User Invites, Cross Promotion
#define afCpAppID @"crossPromotedAppId"
diff --git a/ios/RNAppsFlyer.m b/ios/RNAppsFlyer.m
index 9ec37815..cf69fe83 100755
--- a/ios/RNAppsFlyer.m
+++ b/ios/RNAppsFlyer.m
@@ -47,12 +47,14 @@ -(NSError *) callSdkInternal:(NSDictionary*)initSdkOptions {
NSString* appId = nil;
BOOL isDebug = NO;
BOOL isConversionData = YES;
+ BOOL isDeepLinking = NO;
NSNumber* interval = 0;
if (![initSdkOptions isKindOfClass:[NSNull class]]) {
id isDebugValue = nil;
id isConversionDataValue = nil;
+ id isDeepLinkingValue = nil;
devKey = (NSString*)[initSdkOptions objectForKey: afDevKey];
appId = (NSString*)[initSdkOptions objectForKey: afAppId];
interval = (NSNumber*)[initSdkOptions objectForKey: timeToWaitForATTUserAuthorization];
@@ -68,8 +70,12 @@ -(NSError *) callSdkInternal:(NSDictionary*)initSdkOptions {
if ([isConversionDataValue isKindOfClass:[NSNumber class]]) {
isConversionData = [(NSNumber*)isConversionDataValue boolValue];
}
- }
+ isDeepLinkingValue = [initSdkOptions objectForKey: afDeepLink];
+ if ([isDeepLinkingValue isKindOfClass:[NSNumber class]]) {
+ isDeepLinking = [(NSNumber*)isDeepLinkingValue boolValue];
+ }
+}
NSError* error = nil;
if (!devKey || [devKey isEqualToString:@""]) {
@@ -87,6 +93,9 @@ -(NSError *) callSdkInternal:(NSDictionary*)initSdkOptions {
if(isConversionData == YES){
[AppsFlyerLib shared].delegate = self;
}
+ if(isDeepLinking == YES){
+ [AppsFlyerLib shared].deepLinkDelegate = self;
+ }
#ifndef AFSDK_NO_IDFA
if (interval != 0 && interval != nil){
double timeoutInterval = [interval doubleValue];
@@ -290,6 +299,32 @@ -(NSError *) logEventInternal: (NSString *)eventName eventValues:(NSDictionary *
}
+- (void)didResolveDeepLink:(AppsFlyerDeepLinkResult* _Nonnull) result {
+ if(result.status != nil){
+ NSString *deepLinkStatus = nil;
+ switch(result.status) {
+ case AFSDKDeepLinkResultStatusFound:
+ deepLinkStatus = @"FOUND";
+ break;
+ case AFSDKDeepLinkResultStatusNotFound:
+ deepLinkStatus = @"NOT_FOUND";
+ break;
+ case AFSDKDeepLinkResultStatusFailure:
+ deepLinkStatus = @"Error";
+ break;
+ default:
+ [NSException raise:NSGenericException format:@"Unexpected FormatType."];
+ }
+ NSDictionary* message = @{
+ @"status": afSuccess,
+ @"deepLinkStatus": deepLinkStatus,
+ @"type": afOnDeepLinking,
+ @"data": result.deepLink.clickEvent
+ };
+ [self performSelectorOnMainThread:@selector(handleCallback:) withObject:message waitUntilDone:NO];
+ }
+}
+
-(void)onConversionDataSuccess:(NSDictionary*) installData {
NSDictionary* message = @{
@@ -370,7 +405,7 @@ -(void) handleCallback:(NSDictionary *) message {
}
- (NSArray *)supportedEvents {
- return @[afOnAttributionFailure,afOnAppOpenAttribution,afOnInstallConversionFailure, afOnInstallConversionDataLoaded];
+ return @[afOnAttributionFailure,afOnAppOpenAttribution,afOnInstallConversionFailure, afOnInstallConversionDataLoaded, afOnDeepLinking];
}
-(void) reportOnFailure:(NSString *)errorMessage type:(NSString*) type {
diff --git a/package.json b/package.json
index 46684a45..097a3961 100755
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "react-native-appsflyer",
- "version": "6.1.10",
+ "version": "6.1.20",
"description": "React Native Appsflyer plugin",
"main": "index.js",
"scripts": {