Reputation: 3430
I'm trying to get deferred deep links working on Android. I added a test device in the dashboard & reset the device data and made sure the app is uninstalled. When I click a branch.io link, it takes me to the play store as expected. I then launch the app from Android Studio onto the phone, then the logs say that Branch SDK sent a request to https://api2.branch.io/v1/install
, but the problem is the response doesn't contain the original link, query params, or key value pairs I set in the dashboard. This is the JSONObject I'm receiving in onInitFinished
:
{"+match_guaranteed":true,"link_click_id":"976953359423305632","+clicked_branch_link":true,"+click_timestamp":1634196720,"+is_first_session":true,"utm_source":"test-referrer"}
Where is all the other information? This doesn't include the original link, the key-value pairs, tags, etc.
For a comparison, this is what I receive in the iOS app:
["$ios_passive_deepview": "branch_passive_default", "source": "test-referrer", "+is_first_session": 1, "~channel": test-referrer, "$matching_ttl_s": 7200, "~id": 976763609660742721, "~creation_source": 1, "$one_time_use": 0, "~marketing": 1, "~referring_link": "https://myapp.test-app.link/test-referrer", "~feature": "test", "+click_timestamp": 1634249299, "+match_guaranteed": 0, "$og_description": "My app description", "$og_title": "MyApp", "+clicked_branch_link": 1, "$marketing_title": "Test Referral Link", "~tags": ["test-referrer"], "~campaign": "test"]
If I rotate the phone to recreate the Activity or reopen the app a single time, it then sends a request to https://api2.branch.io/v1/open
and returns all the info I expected initially. How do I get the information after installing the app?
I'm currently testing with myapp.test-app.link, and I call Branch.enableTestMode()
before Branch.getAutoInstance(this)
in my custom Application class's onCreate()
. I also tried with a live link and got the same result.
These are the libraries I'm using in build.grade:
implementation 'io.branch.sdk.android:library:5.0.13'
implementation 'com.google.firebase:firebase-appindexing:20.0.0'
implementation 'com.google.android.gms:play-services-ads-identifier:17.1.0'
implementation 'androidx.browser:browser:1.3.0'
I've also set up app links and the uri scheme in the dashboard as well as in the app. Using getFirstReferringParams()
and getLatestReferringParams()
on the first session after installing doesn't help either.
UPDATE:
Repeating the exact same testing process I described above, now the JSONObject that gets passed into onInitFinished
has even less information and is claiming that I'm not clicking a branch link:
{"+clicked_branch_link":false,"+is_first_session":true}
And getFirstReferringParams()
returns an empty json object.
I'm about to start looking for an alternative at this rate.
Upvotes: 0
Views: 3610
Reputation: 1513
I fixed this issue by adding intent.putExtra("branch_force_new_session", true);
right after this.activity.setIntent(intent);
when initialising the intent. Something like this:
...
this.activity.setIntent(intent);
intent.putExtra("branch_force_new_session", true);
Branch
.sessionBuilder(this.activity)
.withCallback(branchReferralInitListener)
.reInit();
...
Look here for the branch_force_new_session
reference.
The other reason for the missing data is the non-existent link alias, but it looks like it is not the case as soon as you tried with the same link in iOS.
Here is a script example to apply the patch for the Flutter project (do it after every pub get
or pub update
). Modify the paths to use with an Android project built with any other framework:
#!/bin/sh
# NOTE: You can have PUB_CACHE in a different location
PUB_CACHE="$HOME/.pub-cache/hosted/pub.dev"
getVersion() {
echo "$packages" | grep "^$1 " | awk '{print $2}'
}
patchPackage() {
flag=$1
package=$2
file=$3
from=$4
to=$5
version=$(getVersion $package)
sed -i'.bak' "$flag" "s/$from/$to/g" "$PUB_CACHE/$package-$version/$file"
echo "✔️ $package $version"
}
# * flutter_branch_sdk
patchPackage -e flutter_branch_sdk 'android/src/main/java/br/com/rsmarques/flutter_branch_sdk/FlutterBranchSdkPlugin.java' 'this.activity.setIntent(intent);' 'this.activity.setIntent(intent);intent.putExtra("branch_force_new_session", true);'
Here is the link getter example in Flutter, I guess Java/Kotlin implementation would be similar:
StreamSubscription? _branchListener;
final Map<String, DateTime> _processedLinks = {};
/// Use [hot] set to `False` to start listening to deeplinks on a cold app launch
/// and to `True` to continue listening after returning to the app from the background.
Future<void> listenToAppLinks({bool hot = false}) async {
if (!hot) {
try {
final InstallationAppReferrer installer = await InstallReferrer.referrer;
if ([
InstallationAppReferrer.iosDebug,
InstallationAppReferrer.iosTestFlight,
].contains(installer)) {
await reportAnalyticsProperties({'tester': true});
}
} catch (_) {}
}
if (hot) {
await _branchListener?.cancel();
_branchListener = null;
}
// You can also want to use FlutterBranchSdk.checkPasteboardOnInstall() here
_branchListener = FlutterBranchSdk.initSession().listen(
(data) => _processBranchEvent(
data,
calledFrom: 'listener',
firstLaunch: store.state.data.firstTimeLaunch,
),
cancelOnError: false,
onError: (dynamic e) async {},
);
if (!hot && store.state.data.firstTimeLaunch) {
_processBranchEvent(
await FlutterBranchSdk.getFirstReferringParams(),
calledFrom: 'getFirstReferringParams',
firstLaunch: true,
);
}
Future.delayed(
const Duration(milliseconds: 300),
() async => _processBranchEvent(
await FlutterBranchSdk.getLatestReferringParams(),
calledFrom: 'getLatestReferringParams',
firstLaunch: store.state.data.firstTimeLaunch,
),
);
}
void _processBranchEvent(
Map<dynamic, dynamic> data, {
String calledFrom = 'listener',
bool firstLaunch = false,
}) {
String? link =
data['~referring_link'] as String? ?? data['+non_branch_link'] as String?;
if (link == null) return;
final keyOnlyParams = data.keys.where(
(key) =>
data[key] == null ||
data[key] is String && (data[key] as String).isEmpty,
);
if (keyOnlyParams.isNotEmpty) {
final parts = link.split('?');
link =
'''${parts[0]}?${keyOnlyParams.join('&')}${parts.length > 1 ? '&${parts.slice(1).join('')}' : ''}''';
}
processUniLink(
Uri.parse(link),
campaign: data['~campaign'] as String?,
firstLaunch: firstLaunch,
medium: data['~feature'] as String?,
source: data['~channel'] as String?,
tags: List<String>.from(castOr<Iterable>(data['~tags'], [])),
);
}
Future<void> processUniLink(
Uri uri, {
String? bankCode,
String? campaign,
bool firstLaunch = false,
bool launch = false,
String? medium,
String? source,
List<String> tags = const [],
}) async {
final now = DateTime.now();
/// Query params - https://firebase.google.com/docs/dynamic-links/create-manually
final String? linkStr =
!uri.host.contains('-alternate') ? uri.queryParameters['link'] : null;
final Uri link = linkStr != null ? Uri.parse(linkStr) : uri;
/// Ignore unsupported links
if (!link.toString().startsWith(RegExp('http|$appSchema'))) return;
/// Ignore same links called twice within 15 seconds
final DateTime? previousCallAt = _processedLinks[link.toString()];
if (previousCallAt?.isAfter(now - 15.seconds) ?? false) return;
_processedLinks[link.toString()] = now;
...
}
EDIT (2024-01-18): Added a patch script and Flutter code examples.
Upvotes: 1