When adding utm campaign attributes to a dynamic link they are not registered in Firebase Analytics.
With debug mode enabled I can see the dynamic_link_first_open events rolling in, with campaign attribution:

With debug mode disabled the campaign attribution does not show up in the Firebase -> Analytics -> Conversions or in Google Analytics. I waited a few days to make sure, I also marked dynamic_link_first_open as a conversion event. The events do exists in the Analytics Database. Screenshot from BigQuery:

When I add the campaign details manually with logCampaignDetails the the campaign attribution is showing up in Firebase and Analytics. Am I missing something should I be logging the campaign attribution myself? I have now way to get the utm params from the dynamic link as getInitialLink and onLink only returns app/deeplink and not any details of the dynamic link (separate issue?).
I tested mostly on IOS but a short test on Android gave the same result.
Click To Expand
#### Dynamic link handling:
import React from 'react';
import {navigateToDeeplink} from 'lib/utils/deeplink';
import dynamicLinks from '@react-native-firebase/dynamic-links';
import analytics from '@react-native-firebase/analytics';
export default class RootNavigator extends React.Component {
componentDidMount() {
analytics(); // Init analytics before dynamic links
// Unquoting this make campaign tracking work
// But I have no way to get them from the dynamic link params
// A hacky solution could be adding them to the deeplink.
// analytics().logCampaignDetails({
// source: 'utmTestSource',
// campaign: 'utmTestCampaign',
// medium: 'utmTestMedium',
// });
this.handleDeepLinks();
this.firDynamicLinksUnsubscribe = dynamicLinks().onLink(
this.handleDynamicLink,
);
dynamicLinks()
.getInitialLink()
.then(this.handleDynamicLink);
}
handleDynamicLink = async link => {
console.log('#### handle firebase dynamic link:');
console.log(link);
console.log('resolve dynamic link');
if (link && link.url) {
console.log(link.url);
navigateToDeeplink(link.url);
}
};
componentWillUnmount() {
this.firDynamicLinksUnsubscribe();
}
render() {
return null
}
}
#### `package.json`:
{
"name": "--",
"version": "0.119",
"build": 72,
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start",
"test": "jest",
"test:watch": "jest --watch",
"postinstall": "./postinstall.sh"
},
"dependencies": {
"@react-native-community/async-storage": "^1.6.1",
"@react-native-community/google-signin": "^3.0.4",
"@react-native-community/netinfo": "^4.7.0",
"@react-native-firebase/analytics": "^7.6.7",
"@react-native-firebase/app": "^8.4.5",
"@react-native-firebase/crashlytics": "^8.4.9",
"@react-native-firebase/dynamic-links": "^7.5.10",
"@react-native-firebase/perf": "^7.4.8",
"axios": "^0.18.0",
"axios-mock-adapter": "^1.15.0",
"color-string": "^1.5.3",
"events": "^3.0.0",
"i18n-js": "^3.0.11",
"immutable": "^3.8.2",
"js-sha1": "^0.6.0",
"jsx-control-statements": "^3.2.8",
"lodash": "^4.17.11",
"lottie-ios": "3.0.3",
"lottie-react-native": "^3.1.0",
"parse-duration": "^0.1.1",
"prop-types": "^15.6.2",
"react": "16.11.0",
"react-native": "0.62.2",
"react-native-actionsheet": "^2.4.2",
"react-native-app-auth": "^5.1.3",
"react-native-background-color": "^0.0.8",
"react-native-background-timer": "^2.1.1",
"react-native-barcode-builder": "^2.0.0",
"react-native-blur": "^3.2.2",
"react-native-camera": "^3.3.0",
"react-native-circular-progress": "^1.0.1",
"react-native-date-picker": "^2.7.3",
"react-native-device-info": "^5.2.1",
"react-native-extra-dimensions-android": "^1.2.5",
"react-native-fast-image": "^8.3.2",
"react-native-gesture-handler": "^1.5.0",
"react-native-image-picker": "^0.27.1",
"react-native-keyboard-aware-scroll-view": "^0.9.1",
"react-native-keychain": "^4.0.5",
"react-native-languages": "^3.0.0",
"react-native-qrcode-svg": "^5.1.1",
"react-native-reanimated": "^1.3.1",
"react-native-render-html": "^3.10.0",
"react-native-restart": "^0.0.7",
"react-native-screens": "^2.0.0-alpha.6",
"react-native-splash-screen": "^3.2.0",
"react-native-startup-time": "^1.3.0",
"react-native-status-bar-height": "^2.1.0",
"react-native-svg": "9.3.7",
"react-native-system-setting": "^1.7.3",
"react-native-vector-icons": "^6.6.0",
"react-native-version-number": "0.3.4",
"react-native-webview": "^8.0.3",
"react-navigation": "^4.0.10",
"react-navigation-drawer": "^2.3.3",
"react-navigation-stack": "^1.10.3",
"react-redux": "^5.0.7",
"redux": "^4.0.0",
"redux-actions": "^2.6.1",
"redux-form": "^7.4.2",
"striptags": "^3.1.1",
"url-parse": "^1.4.7"
},
"devDependencies": {
"@babel/cli": "^7.2.3",
"@babel/core": "^7.6.2",
"@babel/runtime": "^7.6.2",
"@react-native-community/eslint-config": "^0.0.7",
"babel-jest": "24.9.0",
"babel-plugin-jsx-control-statements": "^3.2.8",
"babel-plugin-module-resolver": "^3.1.1",
"eslint": "^6.5.1",
"eslint-config-standard": "^12.0.0",
"eslint-config-standard-react": "^7.0.2",
"eslint-import-resolver-babel-module": "5.0.0-beta.1",
"eslint-plugin-detox": "^1.0.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-jest": "^21.22.0",
"eslint-plugin-jsx-a11y": "^6.1.1",
"eslint-plugin-jsx-control-statements": "^2.2.1",
"eslint-plugin-jsx-max-len": "^1.0.0",
"eslint-plugin-node": "^8.0.1",
"eslint-plugin-promise": "^4.0.1",
"eslint-plugin-react": "^7.11.1",
"eslint-plugin-react-native": "^3.2.1",
"eslint-plugin-standard": "^4.0.0",
"flipper-plugin-react-native-performance": "^0.5.0",
"fs-extra": "^8.1.0",
"immutablediff": "^0.4.4",
"jest": "24.9.0",
"jetifier": "^1.6.4",
"metro-react-native-babel-preset": "^0.58.0",
"react-devtools": "^3.3.4",
"react-native-flipper": "^0.52.1",
"react-test-renderer": "16.11.0",
"reactotron-react-native": "^5.0.0",
"replace-in-file": "^4.1.3"
}
}
#### `firebase.json` for react-native-firebase v6:
{
"react-native": {
"crashlytics_debug_enabled": true
}
}
### iOS
#### `ios/Podfile`: - [ ] I'm not using Pods - [x] I'm using Pods and my Podfile looks like:
# Uncomment the next line to define a global platform for your project
platform :ios, '12.0'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
#source 'https://github.com/CocoaPods/Specs.git'
source 'https://cdn.cocoapods.org/'
def flipper_pods()
flipperkit_version = '0.52.1'
pod 'FlipperKit', '~>' + flipperkit_version, :configuration => 'Debug'
pod 'FlipperKit/FlipperKitLayoutPlugin', '~>' + flipperkit_version, :configuration => 'Debug'
pod 'FlipperKit/SKIOSNetworkPlugin', '~>' + flipperkit_version, :configuration => 'Debug'
pod 'FlipperKit/FlipperKitUserDefaultsPlugin', '~>' + flipperkit_version, :configuration => 'Debug'
pod 'FlipperKit/FlipperKitReactPlugin', '~>' + flipperkit_version, :configuration => 'Debug'
end
# Post Install processing for Flipper
def flipper_post_install(installer)
file_name = Dir.glob("*.xcodeproj")[0]
app_project = Xcodeproj::Project.open(file_name)
app_project.native_targets.each do |target|
target.build_configurations.each do |config|
cflags = config.build_settings['OTHER_CFLAGS'] || '$(inherited) '
unless cflags.include? '-DFB_SONARKIT_ENABLED=1'
puts 'Adding -DFB_SONARKIT_ENABLED=1 in OTHER_CFLAGS...'
cflags << '-DFB_SONARKIT_ENABLED=1'
end
config.build_settings['OTHER_CFLAGS'] = cflags
end
app_project.save
end
installer.pods_project.save
end
target 'Project' do
pod 'FBLazyVector', :path => "../node_modules/react-native/Libraries/FBLazyVector"
pod 'FBReactNativeSpec', :path => "../node_modules/react-native/Libraries/FBReactNativeSpec"
pod 'RCTRequired', :path => "../node_modules/react-native/Libraries/RCTRequired"
pod 'RCTTypeSafety', :path => "../node_modules/react-native/Libraries/TypeSafety"
pod 'React', :path => '../node_modules/react-native/'
pod 'React-Core', :path => '../node_modules/react-native/'
pod 'React-CoreModules', :path => '../node_modules/react-native/React/CoreModules'
pod 'React-Core/DevSupport', :path => '../node_modules/react-native/'
pod 'React-RCTActionSheet', :path => '../node_modules/react-native/Libraries/ActionSheetIOS'
pod 'React-RCTAnimation', :path => '../node_modules/react-native/Libraries/NativeAnimation'
pod 'React-RCTBlob', :path => '../node_modules/react-native/Libraries/Blob'
pod 'React-RCTImage', :path => '../node_modules/react-native/Libraries/Image'
pod 'React-RCTLinking', :path => '../node_modules/react-native/Libraries/LinkingIOS'
pod 'React-RCTNetwork', :path => '../node_modules/react-native/Libraries/Network'
pod 'React-RCTSettings', :path => '../node_modules/react-native/Libraries/Settings'
pod 'React-RCTText', :path => '../node_modules/react-native/Libraries/Text'
pod 'React-RCTVibration', :path => '../node_modules/react-native/Libraries/Vibration'
pod 'React-Core/RCTWebSocket', :path => '../node_modules/react-native/'
pod 'React-ART', :path => '../node_modules/react-native/Libraries/ART'
pod 'React-cxxreact', :path => '../node_modules/react-native/ReactCommon/cxxreact'
pod 'React-jsi', :path => '../node_modules/react-native/ReactCommon/jsi'
pod 'React-jsiexecutor', :path => '../node_modules/react-native/ReactCommon/jsiexecutor'
pod 'React-jsinspector', :path => '../node_modules/react-native/ReactCommon/jsinspector'
pod 'ReactCommon/callinvoker', :path => "../node_modules/react-native/ReactCommon"
pod 'ReactCommon/turbomodule/core', :path => "../node_modules/react-native/ReactCommon"
pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga', :modular_headers => true
pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
pod 'GoogleSignIn', '~> 5.0.2'
use_native_modules!
pod 'notificare-push-lib-react-native', :path => '../node_modules/notificare-push-lib-react-native'
# Enables Flipper.
#
# Note that if you have use_frameworks! enabled, Flipper will not work and
# you should disable these next few lines.
flipper_pods()
pod 'flipper-plugin-react-native-performance', :path => "../node_modules/flipper-plugin-react-native-performance/ios", :configuration => 'Debug'
post_install do |installer|
flipper_post_install(installer)
end
end
target 'notification' do
pod 'notificare-push-lib', '2.3.2'
end
#### `AppDelegate.m`:
#import "AppDelegate.h"
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <Firebase.h>
#import "RNSplashScreen.h"
#import "NotificareReactNativeIOS.h"
#import <RNGoogleSignin/RNGoogleSignin.h>
#if DEBUG
#ifdef FB_SONARKIT_ENABLED
#import <FlipperKit/FlipperClient.h>
#import <FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h>
#import <FlipperKitLayoutPlugin/SKDescriptorMapper.h>
#import <FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h>
#import <FlipperKitReactPlugin/FlipperKitReactPlugin.h>
#import <FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h>
#import <SKIOSNetworkPlugin/SKIOSNetworkAdapter.h>
#import <flipper-plugin-react-native-performance/FlipperReactPerformancePlugin.h>
#endif
#endif
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Force Firebase DebugView if FirebaseDebugEnabled set in Info.plist
// see https://stackoverflow.com/questions/43754848/how-to-debug-firebase-on-ios-adhoc-build
NSString *plistPath = [[NSBundle mainBundle] pathForResource:@"Info" ofType:@"plist"];
NSDictionary *plistConfig = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
BOOL isFirebaseDebugEnabled = [[plistConfig valueForKey:@"FirebaseDebugEnabled"] boolValue];
if (isFirebaseDebugEnabled) {
NSLog(@"Firebase debug enabled.");
NSMutableArray *newArguments = [NSMutableArray arrayWithArray:[[NSProcessInfo processInfo] arguments]];
[newArguments addObject:@"-FIRAnalyticsDebugEnabled"];
[newArguments addObject:@"-FIRDebugEnabled"];
[[NSProcessInfo processInfo] setValue:[newArguments copy] forKey:@"arguments"];
}
if ([FIRApp defaultApp] == nil) {
[FIRApp configure];
}
[self initializeFlipper:application];
[NotificareReactNativeIOS launch:launchOptions];
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"Project"
initialProperties:nil];
rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
#if DEBUG
#ifdef FB_SONARKIT_ENABLED
[[FlipperReactPerformancePlugin sharedInstance] setBridge:bridge];
#endif
#endif
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
[RNSplashScreen show];
return YES;
}
- (void) initializeFlipper:(UIApplication *)application {
#if DEBUG
#ifdef FB_SONARKIT_ENABLED
FlipperClient *client = [FlipperClient sharedClient];
SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults];
[client addPlugin: [[FlipperKitLayoutPlugin alloc] initWithRootNode: application withDescriptorMapper: layoutDescriptorMapper]];
[client addPlugin: [[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]];
[client addPlugin: [FlipperReactPerformancePlugin sharedInstance]];
[client addPlugin: [FlipperKitReactPlugin new]];
[client addPlugin: [[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]];
[client start];
#endif
#endif
}
- (void)application:(UIApplication *)application
didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
NSLog(@"didFailToRegisterForRemoteNotificationsWithError %@", error);
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}
- (void)application:(UIApplication *)application
didRegisterForRemoteNotificationsWithDeviceToken:(nonnull NSData *)deviceToken {
[NotificareReactNativeIOS didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo
fetchCompletionHandler:(nonnull void (^)(UIBackgroundFetchResult))completionHandler {
[
NotificareReactNativeIOS
didReceiveRemoteNotification:userInfo
completionHandler:^(id _Nullable response, NSError * _Nullable error) {
if (!error) {
completionHandler(UIBackgroundFetchResultNewData);
} else {
completionHandler(UIBackgroundFetchResultNoData);
}
}
];
}
- (void)application:(UIApplication *)application
handleActionWithIdentifier:(nullable NSString *)identifier
forRemoteNotification:(nonnull NSDictionary *)userInfo
withResponseInfo:(nonnull NSDictionary *)responseInfo
completionHandler:(nonnull void (^)())completionHandler
{
[
[NotificarePushLib shared]
handleActionWithIdentifier:identifier
forRemoteNotification:userInfo
withResponseInfo:responseInfo
completionHandler:^(id _Nullable response, NSError * _Nullable error)
{
completionHandler();
}
];
}
// Deep Link was executed when your app was inactive
- (void)notificarePushLib:(NotificarePushLib *)library
didReceiveLaunchURL:(NSURL *)launchURL {
[NotificareReactNativeIOS handleOpenURL:launchURL withOptions:nil];
}
@end
Click To Expand
#### Have you converted to AndroidX? - [x] my application is an AndroidX application? - [x] I am using `android/gradle.settings` `jetifier=true` for Android compatibility? - [x] I am using the NPM package `jetifier` for react-native compatibility? #### `android/build.gradle`:
apply plugin: "com.android.application"
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.firebase.crashlytics'
apply plugin: 'com.google.firebase.firebase-perf'
import com.android.build.OutputFile
/**
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
* and bundleReleaseJsAndAssets).
* These basically call `react-native bundle` with the correct arguments during the Android build
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
* bundle directly from the development server. Below you can see all the possible configurations
* and their defaults. If you decide to add a configuration block, make sure to add it before the
* `apply from: "../../node_modules/react-native/react.gradle"` line.
*
* project.ext.react = [
* // the name of the generated asset file containing your JS bundle
* bundleAssetName: "index.android.bundle",
*
* // the entry file for bundle generation. If none specified and
* // "index.android.js" exists, it will be used. Otherwise "index.js" is
* // default. Can be overridden with ENTRY_FILE environment variable.
* entryFile: "index.android.js",
*
* // https://facebook.github.io/react-native/docs/performance#enable-the-ram-format
* bundleCommand: "ram-bundle",
*
* // whether to bundle JS and assets in debug mode
* bundleInDebug: false,
*
* // whether to bundle JS and assets in release mode
* bundleInRelease: true,
*
* // whether to bundle JS and assets in another build variant (if configured).
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
* // The configuration property can be in the following formats
* // 'bundleIn${productFlavor}${buildType}'
* // 'bundleIn${buildType}'
* // bundleInFreeDebug: true,
* // bundleInPaidRelease: true,
* // bundleInBeta: true,
*
* // whether to disable dev mode in custom build variants (by default only disabled in release)
* // for example: to disable dev mode in the staging build type (if configured)
* devDisabledInStaging: true,
* // The configuration property can be in the following formats
* // 'devDisabledIn${productFlavor}${buildType}'
* // 'devDisabledIn${buildType}'
*
* // the root of your project, i.e. where "package.json" lives
* root: "../../",
*
* // where to put the JS bundle asset in debug mode
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
*
* // where to put the JS bundle asset in release mode
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in debug mode
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in release mode
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
*
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
* // for example, you might want to remove it from here.
* inputExcludes: ["android/**", "ios/**"],
*
* // override which node gets called and with what additional arguments
* nodeExecutableAndArgs: ["node"],
*
* // supply additional arguments to the packager
* extraPackagerArgs: []
* ]
*/
project.ext.react = [
enableHermes: true, // clean and rebuild if changing
]
apply from: "../../node_modules/react-native/react.gradle"
/**
* Set this to true to create two separate APKs instead of one:
* - An APK that only works on ARM devices
* - An APK that only works on x86 devices
* The advantage is the size of the APK is reduced by about 4MB.
* Upload all the APKs to the Play Store and people will download
* the correct one based on the CPU architecture of their device.
*/
def enableSeparateBuildPerCPUArchitecture = false
/**
* Run Proguard to shrink the Java bytecode in release builds.
*/
def enableProguardInReleaseBuilds = false
/**
* The preferred build flavor of JavaScriptCore.
*
* For example, to use the international variant, you can use:
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
*
* The international variant includes ICU i18n library and necessary data
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
* give correct results when using with locales other than en-US. Note that
* this variant is about 6MiB larger per architecture than default.
*/
def jscFlavor = 'org.webkit:android-jsc:+'
/**
* Whether to enable the Hermes VM.
*
* This should be set on project.ext.react and mirrored here. If it is not set
* on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
* and the benefits of using Hermes will therefore be sharply reduced.
*/
def enableHermes = project.ext.react.get("enableHermes", false);
// Create a variable called keystorePropertiesFile, and initialize it to your
// keystore.properties file, in the rootProject folder.
def buildVarsFile = rootProject.file("build.properties")
// Initialize a new Properties() object called keystoreProperties.
def buildVars = new Properties()
// Load your keystore.properties file into the keystoreProperties object.
buildVars.load(new FileInputStream(buildVarsFile))
android {
compileSdkVersion rootProject.ext.compileSdkVersion
lintOptions {
checkReleaseBuilds false
// Or, if you prefer, you can continue to check for errors in release builds,
// but continue the build even when errors are found:
abortOnError false
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
defaultConfig {
applicationId buildVars['applicationId']
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
missingDimensionStrategy 'react-native-camera', 'general'
renderscriptSupportModeEnabled true
multiDexEnabled true
versionCode buildVars['buildNumber'].toInteger()
versionName buildVars['version']
manifestPlaceholders = [
appAuthRedirectScheme: 'auth.' + buildVars['applicationId']
]
}
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
}
}
signingConfigs {
debug {
storeFile file('debug.keystore')
storePassword 'android'
keyAlias 'androiddebugkey'
keyPassword 'android'
}
// Note: these values are replaced in prepare script
// Script assumes that these vars occur just once in this file
// If you make changes, makin this assumption false, make sure to change prepare script too
release {
storeFile file('release.keystore')
storePassword buildVars['releaseStorePassword']
keyAlias buildVars['releaseKeyAlias']
keyPassword buildVars['releaseKeyPassword']
}
}
buildTypes {
debug {
signingConfig signingConfigs.debug
}
release {
// Caution! In production, you need to generate your own keystore file.
// see https://facebook.github.io/react-native/docs/signed-apk-android.
signingConfig signingConfigs.release
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
packagingOptions {
pickFirst "lib/armeabi-v7a/libc++_shared.so"
pickFirst "lib/arm64-v8a/libc++_shared.so"
pickFirst "lib/x86/libc++_shared.so"
pickFirst "lib/x86_64/libc++_shared.so"
}
// applicationVariants are e.g. debug, release
applicationVariants.all { variant ->
variant.outputs.each { output ->
// For each separate APK per architecture, set a unique version code as described here:
// https://developer.android.com/studio/build/configure-apk-splits.html
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
def abi = output.getFilter(OutputFile.ABI)
if (abi != null) { // null for the universal-debug, universal-release variants
output.versionCodeOverride =
versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
}
}
}
}
dependencies {
implementation project(':notificare-push-lib-react-native')
implementation fileTree(dir: "libs", include: ["*.jar"])
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:+" // From node_modules
implementation "androidx.multidex:multidex:2.0.0"
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
// For WebP support, including animated WebP
compile 'com.facebook.fresco:animated-webp:2.0.0'
compile 'com.facebook.fresco:webpsupport:2.0.0'
compile 'com.facebook.fresco:animated-gif:2.0.0'
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
exclude group:'com.facebook.fbjni'
}
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
}
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
exclude group:'com.facebook.flipper'
}
if (enableHermes) {
def hermesPath = "../../node_modules/hermes-engine/android/";
debugImplementation files(hermesPath + "hermes-debug.aar")
releaseImplementation files(hermesPath + "hermes-release.aar")
} else {
implementation jscFlavor
}
}
// Run this once to be able to run the application with BUCK
// puts all compile dependencies into folder libs for BUCK to use
task copyDownloadableDepsToLibs(type: Copy) {
from configurations.compile
into 'libs'
}
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
#### `android/app/build.gradle`:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext {
buildToolsVersion = "29.0.2"
minSdkVersion = 21
compileSdkVersion = 29
targetSdkVersion = 29
supportLibVersion = "28.0.0"
googlePlayServicesVersion = "12.0.1"
googlePlayServicesAuthVersion = "16.0.1"
}
repositories {
google()
maven {
url 'https://maven.google.com/'
name 'Google'
}
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.2'
classpath 'com.google.gms:google-services:4.3.3'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.2.0'
classpath 'com.google.firebase:perf-plugin:1.3.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
mavenLocal()
maven {
url 'https://www.jitpack.io'
}
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url("$rootDir/../node_modules/react-native/android")
}
maven {
// Android JSC is installed from npm
url("$rootDir/../node_modules/jsc-android/dist")
}
maven {
url "https://github.com/Notificare/notificare-mvn-repo/raw/master/releases"
}
google()
jcenter()
}
}
// got error with react-native-background-color
// Adding this solved issue
// https://stackoverflow.com/questions/50574492/react-native-error-resource-androidstyle-textappearance-material-widget-butto
subprojects {
afterEvaluate {project ->
// force libs to use recent buildtools
if (project.hasProperty("android")) {
android {
compileSdkVersion = 29 // change to match your desired version
buildToolsVersion = "29.0.2" // ....
}
}
}
}
#### `android/settings.gradle`:
rootProject.name = 'Project'
include ':notificare-push-lib-react-native'
project(':notificare-push-lib-react-native').projectDir = new File(rootProject.projectDir, '../node_modules/notificare-push-lib-react-native/android')
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
include ':app'
#### `MainApplication.java`:
package nl.project.whitelabel;
import android.app.Application;
import android.content.Context;
import androidx.multidex.MultiDexApplication;
import android.webkit.WebView;
// import re.notifica.reactnative.NotificarePackage;
import re.notifica.reactnative.NotificareReceiver;
import re.notifica.Notificare;
import com.airbnb.android.react.lottie.LottiePackage;
import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.soloader.SoLoader;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
public class MainApplication extends MultiDexApplication implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
packages.add(new LottiePackage());
return packages;
}
@Override
protected String getJSMainModuleName() {
return "index";
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
Notificare.shared().setDebugLogging(BuildConfig.DEBUG);
Notificare.shared().launch(this);
Notificare.shared().createDefaultChannel();
Notificare.shared().setIntentReceiver(NotificareReceiver.class);
SoLoader.init(this, /* native exopackage */ false);
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
// Enable debugging of webview using in chrome://inspect
WebView.setWebContentsDebuggingEnabled(BuildConfig.DEBUG);
}
/**
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
*
* @param context
* @param reactInstanceManager
*/
private static void initializeFlipper(
Context context, ReactInstanceManager reactInstanceManager) {
if (BuildConfig.DEBUG) {
try {
/*
We use reflection here to pick up the class that initializes Flipper,
since Flipper library is not available in release mode
*/
Class<?> aClass = Class.forName("nl.project.whitelabel.ReactNativeFlipper");
aClass
.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
.invoke(null, context, reactInstanceManager);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
#### `AndroidManifest.xml`:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="nl.project.whitelabel"
xmlns:tools="http://schemas.android.com/tools"
android:installLocation="auto"
>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
<uses-permission android:name="android.permission.CAMERA" />
<application
android:name=".MainApplication"
android:label="@string/app_name"
android:largeHeap="true"
android:icon="@mipmap/ic_launcher"
android:allowBackup="false"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
android:launchMode="singleTask"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
</intent-filter>
<intent-filter>
<action android:name="re.notifica.intent.action.NotificationOpened" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter android:label="filter_react_native">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="${applicationId}" />
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
<activity
android:theme="@style/Theme.Transparent"
android:name="re.notifica.ui.NotificationActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:hardwareAccelerated="true"/>
<activity
android:name="re.notifica.ui.PassbookActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:hardwareAccelerated="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="push.notifica.re"
android:pathPrefix="@string/notificare_app_id"
android:scheme="https" />
</intent-filter>
</activity>
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="${applicationId}" />
</intent-filter>
</receiver>
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver" android:exported="false" />
<meta-data
android:name="com.facebook.sdk.ApplicationId"
android:value="@string/facebook_app_id" />
</application>
</manifest>
Click To Expand
**`react-native info` output:**
System:
OS: macOS 10.15.6
CPU: (8) x64 Intel(R) Core(TM) i5-8279U CPU @ 2.40GHz
Memory: 348.67 MB / 16.00 GB
Shell: 5.7.1 - /bin/zsh
Binaries:
Node: 10.16.3 - ~/.nvm/versions/node/v10.16.3/bin/node
Yarn: 1.17.3 - /usr/local/bin/yarn
npm: 6.9.0 - ~/.nvm/versions/node/v10.16.3/bin/npm
Watchman: 4.9.0 - /usr/local/bin/watchman
Managers:
CocoaPods: 1.9.3 - /Users/bobsmits/.rbenv/shims/pod
SDKs:
iOS SDK:
Platforms: iOS 14.2, DriverKit 20.0, macOS 11.0, tvOS 14.2, watchOS 7.1
Android SDK:
API Levels: 28, 29
Build Tools: 28.0.3, 29.0.2
System Images: android-19 | Google APIs Intel x86 Atom, android-22 | Google APIs Intel x86 Atom_64, android-29 | Google Play Intel x86 Atom
Android NDK: Not Found
IDEs:
Android Studio: 3.5 AI-191.8026.42.35.5977832
Xcode: 12.2/12B45b - /usr/bin/xcodebuild
Languages:
Java: 1.8.0_222 - /usr/bin/javac
Python: 2.7.16 - /usr/bin/python
npmPackages:
@react-native-community/cli: Not Found
react: 16.11.0 => 16.11.0
react-native: 0.62.2 => 0.62.2
react-native-macos: Not Found
npmGlobalPackages:
*react-native*: Not Found
- **Platform that you're experiencing the issue on**:
- [x] iOS
- [x] Android
- [x] **iOS** but have not tested behavior on Android
- [] **Android** but have not tested behavior on iOS
- [x] Both **I tested mostly on IOS** but a short test on Android gave the same result.
- **`react-native-firebase` version you're using that has this issue:**
- `8.4.5`
- **`Firebase` module(s) you're using that has the issue:**
- `dynamic-links, analytics `
- **Are you using `TypeScript`?**
- `N` & `VERSION`
React Native Firebase and Invertase on Twitter for updates on the library.Hi there! I haven't used these myself and I haven't tested it, so I won't be more advanced in knowledge unfortunately, but I did search around a bit:
It does not appear that you are supposed to do anything else?
When you debug the link are certain you see the parameters you expect? https://firebase.google.com/docs/dynamic-links/debug (you might try turning on diagnostics for links in your AppDelegate after FIRApp configure even)
There is currently no underlying native API to get the original URL handled the underlying native SDK. They only return the resolved link.
As a possible workaround you might try using React Native Linking https://reactnative.dev/docs/linking#get-the-deep-link to get the raw link and then the semi-new resolveLink https://rnfirebase.io/reference/dynamic-links#resolveLink in order to then resolve it?
That only seems to works if the app is already installed. I cant seem to get the url from application:openURL:options to the javascript side, Linking.getInitialURL is always null. I guess thats because there is no initial url when the dynamic link still has to be fetched via fingerprinting.
Then again I'm not very familiar with objective-c, if someone has a working example I would be grateful. For now I made a custom implementation by adding the campaign attribution to the deeplink url. But it would be great if we could alteast get the origional dynamic link from the native side. Of even beter get the official dynamic link campaign attribution to work.
The quickstarts are usually the canonical examples: https://github.com/firebase/quickstart-ios/tree/master/dynamiclinks
That would mean capturing the dynamic link and calling logCampaignDetails on the native side. Also a possibility but I think I will stick to my temporary solution. Also fyi there seem to be a bug in Android causing automatic events not to be logged. Like dynamic_link_first_open.
https://github.com/firebase/firebase-android-sdk/issues/2182
https://github.com/firebase/firebase-android-sdk/issues/2179
I had to downgrade to 8.4.5 to get those events working again.
Interesting. What version of firebase-android-sdk is coming in with 8.4.5? Gradle should print it out during build. 2179 looks like the canonical issue and 2182 will likely be closed as duplicate - one thing to note when logging upstream issues - we encourage that and it is very helpful of course, but upstream they will typically close issues that reference react-native-firebase immediately without much comment. It is understandable - they can't easily reproduce those issues, the best course is to use their quickstarts as the basis for a quick repro, then the bugs are usually investigated and fixed with great speed: https://github.com/firebase/quickstart-android/
More topical for us here: the thing is that I don't think we get the raw link anywhere, we always get the resolved link, and by that time it's missing the campaign details already. I haven't gone looking upstream in their code yet to see what's going on, but I just don't think we have the information in the react-native-firebase layer to do anything here other than a collection of workarounds (start with react-natve 'Linking' to get raw URL, then use react-native-firebase to resolve it (so it's useful to you) while at same time getting campaign details from the raw URL and doing an analytics hit. That's objectively horrible :-), the upstream libraries should be doing it correctly
@mikehardy Is this what you are looking for?
:react-native-firebase_app package.json found at ...node_modules/@react-native-firebase/app/package.json
:react-native-firebase_app:firebase.bom using default value: 25.12.0
:react-native-firebase_app:play.play-services-auth using default value: 18.1.0
:react-native-firebase_app package.json found at ...node_modules/@react-native-firebase/app/package.json
:react-native-firebase_app:version set from package.json: 8.4.6 (8,4,6 - 8004006)
:react-native-firebase_app:android.compileSdk using custom value: 29
:react-native-firebase_app:android.targetSdk using custom value: 29
:react-native-firebase_app:android.minSdk using custom value: 21
:react-native-firebase_app:reactNativeAndroidDir ...node_modules/react-native/android
So if I'm following correctly BoM 25.12.0 works for dynamic link attribution, but after that (either 25.13.0 or then 26.0.0 and 26.1.0) do not work? I don't believe the upstream issue you linked has that information, it might be valuable for the firebase-android-sdk team as they attempt to triage
I did put that in the issue: "I saw in the firebase android changelog that in Analytics version 18.0.0 reserved names where added. Maybe the fault is introduced there? On bom 25.12.0 it still works on 26.0.0 it breaks."
I just tested 26.1.0 same result. Updated the issue with that result. I also filed a bug at Firebase for the origional topic of this issue.
I think you might be on to something, I noticed that as well - it was part of our forward port here https://github.com/invertase/react-native-firebase/pull/4471/commits/a675cd7f7cf808e6a6d10cc174eeff3007ceac58#diff-648d65cd49b8771784c33ac287798bc42e56a399525c8cf8687624dc1efd3482
I got a response from the Firebase Support Team:
_Upon consulting with the team, it seems that this is a bug with the attribution tracking between Google Analytics for Firebase and Firebase Dynamic Links. Our engineers are actively investigating and working on this issue, as they consider this issue of high priority. However, it would take an indefinite timeline to be fixed. We'll be sure to address things though once we've identified the root cause of the issue. For further updates, I would suggest to keep an eye on our release notes or check our Console from time to time._
So fingers crossed.
Thanks for keeping the loop closed. I am semi-embarrassed to admit I have those release notes on speed dial in my browser, so I'm sure I'll see it ;-). Cheers
Hello 馃憢, to help manage issues we automatically close stale issues.
This issue has been automatically marked as stale because it has not had activity for quite some time. Has this issue been fixed, or does it still require the community's attention?
This issue will be closed in 15 days if no further activity occurs.
Thank you for your contributions.
Curious if this is still a problem?
Me to 馃槈 I will check it next week.