react-native-cli: 2.0.1
react-native: 0.57.1
react-native-branch: "^2.3.3"
Tested on OnePlus 5T
This is my JS code:
// Branch sdk
branch.initSessionTtl = 10000; // 10 seconds
branch.subscribe(({ error, params }) => {
if (error) {
console.log('Error from Branch: ' + error);
return;
}
// params will never be null if error is null
console.log('branch params:', params);
});
branch.getLatestReferringParams().then((lastParams) => {
// params from last open
console.log('branch lastParams: ', lastParams);
});
branch.getFirstReferringParams().then((installParams) => {
// params from original install
console.log('branch installParams: ', installParams);
handleFirstInstall(installParams);
});
async function handleFirstInstall(installParams) {
const value = await AsyncStorage.getItem(NEW_INSTALL_WELCOME_MESSAGE_SHOWN_KEY);
const times = value === null ? 0 : parseInt(value);
console.log('welcome message shown times: ', times);
if (times <= 1) {
if ('referrerOffer' in installParams && installParams.referrerOffer) {
console.log('Thank you for signing up. You will get the benefits of ' + installParams.referrerOffer);
Toast.show('Thank you for signing up. You will get the benefits of ' + installParams.referrerOffer, Toast.LONG);
await AsyncStorage.setItem(REFERRAL_OFFER_KEY, installParams.referrerOffer);
await AsyncStorage.setItem(REFERRING_CUSTOMER_ID_KEY, installParams.referrerCustomerId);
}
await AsyncStorage.setItem(NEW_INSTALL_WELCOME_MESSAGE_SHOWN_KEY, '' + (times + 1));
} else {
console.log('Welcome message was shown already. dont bother');
}
}
android/app/build.gradle:
dependencies {
compile (project(':react-native-branch')) {
exclude group: 'com.google.android.gms'
exclude group: "com.google.firebase"
}
compile (project(':react-native-push-notification')) {
exclude group: 'com.google.android.gms'
exclude group: "com.google.firebase"
transitive = false
}
compile (project(':react-native-device-info')) {
exclude group: 'com.google.android.gms'
transitive = false
}
compile (project(':react-native-firebase')) {
exclude group: 'com.google.android.gms'
exclude group: "com.google.firebase"
transitive = false
}
compile (project(':react-native-share')) {
exclude group: 'com.google.android.gms'
}
compile (project(':react-native-code-push')) {
exclude group: 'com.google.android.gms'
}
compile (project(':appcenter-crashes')) {
exclude group: 'com.google.android.gms'
}
compile (project(':appcenter-analytics')) {
exclude group: 'com.google.android.gms'
}
compile (project(':appcenter')) {
exclude group: 'com.google.android.gms'
}
compile (project(':react-native-vector-icons')) {
exclude group: 'com.google.android.gms'
}
compile (project(':react-native-maps')) {
exclude group: 'com.google.android.gms'
}
compile ("com.google.android.gms:play-services-base:$googlePlayServicesVersion") {
force = true;
}
compile ("com.google.android.gms:play-services-basement:$googlePlayServicesVersion") {
force = true;
}
compile ("com.google.android.gms:play-services-maps:$googlePlayServicesVersion") {
force = true;
}
compile ("com.google.firebase:firebase-iid:17.0.0") {
force = true;
}
// Firebase dependencies
implementation "com.google.firebase:firebase-core:16.0.3"
implementation "com.google.firebase:firebase-messaging:17.3.0"
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
implementation "com.facebook.react:react-native:+" // From node_modules
}
MainApplication.java:
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
protected String getJSBundleFile() {
return CodePush.getJSBundleFile();
}
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new RNBranchPackage(),
new ReactNativePushNotificationPackage(),
new RNDeviceInfo(),
new RNFirebasePackage(),
new RNFirebaseMessagingPackage(),
new RNFirebaseNotificationsPackage(),
new RNSharePackage(),
new CodePush(getResources().getString(R.string.reactNativeCodePush_androidDeploymentKey), getApplicationContext(), BuildConfig.DEBUG),
new MapsPackage(),
new AppCenterReactNativeCrashesPackage(MainApplication.this, getResources().getString(R.string.appCenterCrashes_whenToSendCrashes)),
new AppCenterReactNativeAnalyticsPackage(MainApplication.this, getResources().getString(R.string.appCenterAnalytics_whenToEnableAnalytics)),
new AppCenterReactNativePackage(MainApplication.this),
new VectorIconsPackage()
);
}
@Override
protected String getJSMainModuleName() {
return "index";
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
// Initialize the Branch object
Branch.getAutoInstance(this);
SoLoader.init(this, /* native exopackage */ false);
}
// @Override
public String getFileProviderAuthority() {
return "com.homedruid.customer.app.rn.notificationtester.provider";
}
}
MainActivity.java:
public class MainActivity extends ReactActivity {
/**
* Returns the name of the main component registered from JavaScript.
* This is used to schedule rendering of the component.
*/
@Override
protected String getMainComponentName() {
return "homedruid.customer.app.rn.notificationtester";
}
@Override
protected void onStart() {
super.onStart();
RNBranchModule.initSession(getIntent().getData(), this);
}
@Override
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
}
}
AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.homedruid.customer.app.rn.notificationtester">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<application
android:name=".MainApplication"
android:label="@string/app_name"
android:icon="@mipmap/business_logo"
android:allowBackup="false"
android:theme="@style/AppTheme">
<service android:name="io.invertase.firebase.messaging.RNFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
<service android:name="io.invertase.firebase.messaging.RNFirebaseInstanceIdService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
<service android:name="io.invertase.firebase.messaging.RNFirebaseBackgroundMessagingService"/>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/filepaths"/>
</provider>
<meta-data android:name="com.google.android.geo.API_KEY" android:value=""/>
<activity
android:name=".MainActivity"
android:launchMode="singleTask"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<!-- Branch URI scheme -->
<intent-filter>
<data android:scheme="homedruidcust" android:host="open" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
<!-- Branch App Links -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="homedruid1.app.link" />
</intent-filter>
</activity>
<!-- Branch init -->
<meta-data android:name="io.branch.sdk.BranchKey" android:value="" />
<meta-data android:name="io.branch.sdk.BranchKey.test" android:value="" />
<!-- Branch testing (TestMode "true" to simulate fresh installs on dev environment) -->
<meta-data android:name="io.branch.sdk.TestMode" android:value="false" />
<!-- Branch install referrer tracking -->
<receiver android:name="io.branch.referral.InstallListener" android:exported="true">
<intent-filter>
<action android:name="com.android.vending.INSTALL_REFERRER" />
</intent-filter>
</receiver>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity"/>
</application>
</manifest>
Install app via https://homedruid1.app.link/sEYJMpEQdR link
Expected behavior:
branch installParams should be non-empty and should show the referral offer.
Actual:
On install and first open, this is what happens:
10-24 11:05:18.297 32345-32397/? I/ReactNativeJS: 'branch lastParams: ', {}
10-24 11:05:18.298 32345-32397/? I/ReactNativeJS: 'branch installParams: ', {}
10-24 11:05:18.369 32345-32397/? I/ReactNativeJS: 'welcome message shown times: ', 0
10-24 11:05:18.507 32345-32397/? I/ReactNativeJS: 'branch params:', { '~channel': 'Email',
'~referring_link': 'https://homedruid1.app.link/sEYJMpEQdR',
'+is_first_session': false,
'~campaign': 'Eta Garden apartment, Magadi road',
'~id': '582899367938644486',
'+clicked_branch_link': true,
'$marketing_title': 'ETA Garden apartments, Magadi Road',
referrerCustomerId: 'ETA_GARDEN_APARTMENTS',
'~creation_source': 1,
'$one_time_use': false,
'+match_guaranteed': true,
'~feature': 'marketing',
'+click_timestamp': 1540359264,
referrerOffer: 'HOMEDRUID<>',
'~marketing': true }
If i kill the app and open 2nd time, this is what i get:
10-24 11:06:58.696 32736-320/? I/ReactNativeJS: Initializing react-native-branch v. 2.3.3
10-24 11:06:58.947 32736-320/? I/ReactNativeJS: 'branch params:', { '+is_first_session': false,
'+clicked_branch_link': false,
cached_initial_event: true }
10-24 11:06:58.948 32736-320/? I/ReactNativeJS: 'branch lastParams: ', { '+is_first_session': false, '+clicked_branch_link': false }
'branch installParams: ', { '~channel': 'Email',
'~referring_link': 'https://homedruid1.app.link/sEYJMpEQdR',
'+is_first_session': false,
'~campaign': 'Eta Garden apartment, Magadi road',
'~id': '582899367938644486',
'+clicked_branch_link': true,
'$marketing_title': 'ETA Garden apartments, Magadi Road',
referrerCustomerId: 'ETA_GARDEN_APARTMENTS',
'~creation_source': 1,
'$one_time_use': false,
'+match_guaranteed': true,
'~feature': 'marketing',
'+click_timestamp': 1540359264,
referrerOffer: 'HOMEDRUID<>',
'~marketing': true }
10-24 11:06:58.952 32736-320/? I/ReactNativeJS: 'Initial url is: ', null
10-24 11:06:58.954 32736-320/? I/ReactNativeJS: 'welcome message shown times: ', 1
Thank you for signing up. You will get the benefits of HOMEDRUID<>
Same thing happens on Android emulator as well : Nexus 5 - API 28 (target: Android null Google play) - x86.
It seems if you wait for the first branch.subscribe() to trigger, you can get the install params from there. Sadly, there's another bug in getFirstReferringParams(), it gets updated if you open the app via a branch link, even though its not the app install event.
Ended up working around both the issues, but it made my code really trick. Please get this fixed.
Also, at times the initial subscribe call takes 3 seconds. From the documentation, it says that it should be cached.
async init(nextState) {
const NUM_TIMES_APP_OPEN = 'num-times-app-open';
const value = await AsyncStorage.getItem(NUM_TIMES_APP_OPEN);
const numTimesAppOpen = value === null ? 0 : parseInt(value);
console.log('numTimesAppOpen: ', numTimesAppOpen);
await AsyncStorage.setItem(NUM_TIMES_APP_OPEN, '' + (numTimesAppOpen + 1));
const FIRST_SUBSCRIBE_CALL = 'first-subscribe-call';
await AsyncStorage.setItem(FIRST_SUBSCRIBE_CALL, 'yes');
const nowMs = new Date().getTime();
const f = async ({ error, params }) => {
const firstSubscribeCall = await AsyncStorage.getItem(FIRST_SUBSCRIBE_CALL);
await AsyncStorage.setItem(FIRST_SUBSCRIBE_CALL, 'no');
console.log('firstSubscribeCall:', firstSubscribeCall);
const storedReferrerOffer = await AsyncStorage.getItem(REFERRAL_OFFER_KEY);
console.log('storedReferrerOffer:', storedReferrerOffer);
if (firstSubscribeCall === 'yes') {
console.log('Branch initial latency: ', new Date().getTime() - nowMs);
}
if (error) {
console.log('Error from Branch: ' + error);
if (numTimesAppOpen === 0 && firstSubscribeCall === 'yes') {
Toast.show('Bad internet connection: unable to get referral params', Toast.LONG);
}
if (firstSubscribeCall === 'yes') {
this.setState({state: nextState, referrerOffer: storedReferrerOffer});
}
return;
}
// params will never be null if error is null
console.log('branch params:', params);
let referrerOffer = null;
if (numTimesAppOpen === 0 && firstSubscribeCall === 'yes' && 'referrerOffer' in params && params.referrerOffer) {
referrerOffer = params.referrerOffer;
console.log('Thank you for signing up. You will get the benefits of ' + referrerOffer);
await AsyncStorage.setItem(REFERRAL_OFFER_KEY, referrerOffer);
await AsyncStorage.setItem(REFERRING_CUSTOMER_ID_KEY, params.referrerCustomerId);
}
if (numTimesAppOpen > 0 || firstSubscribeCall === 'no') {
referrerOffer = storedReferrerOffer;
}
if (firstSubscribeCall === 'yes') {
// Should only be called the first time Splash screen is rendered. In case you turn the screen off and
// open again, branch.subscribe is triggered again.
this.setState({state: nextState, referrerOffer: referrerOffer});
}
};
// Initialize branch sdk
branch.initSessionTtl = 10000; // 10 seconds
branch.subscribe(f);
}
+1
For me I am getting +non_branch_link in the params when link is clicked after install from where I parse my apps deeplink. But this param doesn't exist when app is installed through link and launched. Fallback link works in desktop, but this bug is blocking mobile client implementation totally.
This is a common issue that can occur if you have mismatched keys. Can you double check that the key used to create your test or live link is the same key in the app?
@gagangoku Are you still experiencing this issue?
@csalmi-branch after i updated to the 3.0 version all my troubles with this went away and it just works now.
EDIT:
The issue is actually still there for me as well. Once the app is installed i can make new call to get the params and then they are available, but not with the initial install.
@csalmi-branch : keys are matching. Last I checked (when i raised the bug), am able to create links and rest of the sdk works as well, just the calls to getFirstReferringParams and getLatestReferringParams are bad.
Is there an update that might have fixed this ?
@gagangoku we've release a v3.0.0-beta that fixed a number of old issues. Are you able to update to that version and see if you still have this problem? Keep in mind that there is a small configuration change you will need to make when upgrading to that version:
https://github.com/BranchMetrics/react-native-branch-deep-linking#updating-from-an-earlier-version
@stenmuchow and @gagangoku what is your use-case that you are trying to use getFirstReferringParams for? Can you use the callback instead of calling that function?
@stenmuchow and @gagangoku are you still experiencing this issue?
@csalmi-branch i most certainly am...After experimenting a whole bunch with using the RNModuleBranch vs Branch inside my java files on the android side of things i can say that the getLatestRefering params and the callback are not receiving correct values.
Ive been thru nearly all the examples in the code base to grab missing pieces from the documentation - which seems to be quite fragmented now after the introduction of v3.0.0 beta.
Anyways I have determined i can always receive the install params thru getInstallParams call 100% of the time...
The main problem i face is being able to tell what the applicatoin received the last time it was opened thru a branch link. I never have access to this using the RNModuleBranch call on the java side of things. I can make it work with using native branch code but then the subscribe callback no longer works.
Ive decided the only was i am reliably able to fetch the last params is to save them in the file systems on Android thru redux persist, feels kinda hacky tho as i would expect Branch to keep track of these things.
I can provide files to show how im setting things up, but it seems to be an issue in the RNModuleBranch side of things.
Nevetheless thanks for the package, its really a great tool and if we could fix this then it would be one less thing for me to be confused about ;)
@csalmi-branch 鈽濓笍
@jdee Can you look into this issue next?
Can you guys fix this issue? @jdee @csalmi-branch
I'm not sure if this is actually an issue except maybe with docs. You cannot get anything meaningful from getLatestReferringParams or getFirstReferringParams until the subscribe callback is called. I'm not sure what the use case even is for getLatestReferringParams, since that is just the params passed into the subscribe callback. If you modify your code this way it should do what you want:
branch.subscribe(({params, error}) => {
if (error) return; // log, etc.
const firstParams = branch.getFirstReferringParams();
const latestParams = branch.getLatestReferringParams();
// now latestParams is a synonym for params
});
If that doesn't work, there's definitely a bug.
@jdee Interesting, Im using sagas and event channels to handle this, but im kicking off my subscribe further down in the function.
However from the docs I thought that getLatestReferringParams() would do both latest and first?
function* initApp() {
try {
console.log('initialize user started');
const latestParams = yield call(branch.getLatestReferringParams);
console.log('INIT USER LATEST PARAMS', latestParams);
const { token } = latestParams;
if (token) {
yield put(userActions.checkNewShipmentStatus(latestParams));
}
const contentChannel = yield call(channel);
while (true) {
const contentAction = yield take(contentChannel);
yield put(contentAction);
}
} catch (e) {
console.log(e);
}
}
My event channel code.
function channel() {
return eventChannel(emit => {
console.log('subscribing to branch');
const unsubscribe = branch.subscribe(({ error, params }) => {
const { token } = params;
if (!error && token) {
emit(userActions.checkNewShipmentStatus(params));
}
});
return () => unsubscribe();
}, buffers.expanding());
}
For anyone interested in a Saga like approach.
Ill rearrange the function calls and see if it makes a difference. ###
It does seem like some race condition, from my use case in some function along the way I need to get the first referring params so i'm using:
let branchLinkData = await branch.getFirstReferringParams();
// when adding these lines things works -
if (isObject(branchLinkData) && !Object.keys(branchLinkData).length) {
await delayInMilliseconds(800);
branchLinkData = await branch.getFirstReferringParams();
}
Now I can't use subscribe here as it can be that it was called before, so I have to use some delay.
I expect getFristReferringParams to return the data properly. it's an async function so the wait logic should be internal to this function IMO
Most helpful comment
I'm not sure if this is actually an issue except maybe with docs. You cannot get anything meaningful from getLatestReferringParams or getFirstReferringParams until the subscribe callback is called. I'm not sure what the use case even is for getLatestReferringParams, since that is just the
paramspassed into the subscribe callback. If you modify your code this way it should do what you want:If that doesn't work, there's definitely a bug.