When trying to create a short dynamic link on iOS I get an exception from ExceptionsManager
. Creating a regular dynamic link works as expected. Android works as expected.
const dm = new firebase.links.DynamicLink(
'https://google.no',
'example.page.link'
);
const dynamicLinks = firebase.links();
dynamicLinks
.createShortDynamicLink(dm)
.then(link => console.log('Short dynamic link: ', link))
.catch(err => console.error(err));
dynamicLinks
.createDynamicLink(dm)
.then(link => console.log('Dynamic link: ', link))
.catch(err => console.error(err));
Which produce the following output in my console:
ExceptionsManager.js:84 Short dynamic link error: Error: Failed to create Short Dynamic Link
at createErrorFromErrorData (NativeModules.js:146)
at NativeModules.js:95
at MessageQueue.__invokeCallback (MessageQueue.js:397)
at MessageQueue.js:127
at MessageQueue.__guard (MessageQueue.js:297)
at MessageQueue.invokeCallbackAndReturnFlushedQueue (MessageQueue.js:126)
at t (RNDebuggerWorker.js:1)
reactConsoleErrorHandler @ ExceptionsManager.js:84
console.error @ YellowBox.js:59
(anonymous) @ VM344:21
tryCallOne @ core.js:37
(anonymous) @ core.js:123
(anonymous) @ JSTimers.js:295
_callTimer @ JSTimers.js:152
_callImmediatesPass @ JSTimers.js:200
callImmediates @ JSTimers.js:464
__callImmediates @ MessageQueue.js:320
(anonymous) @ MessageQueue.js:135
__guard @ MessageQueue.js:297
flushedQueue @ MessageQueue.js:134
invokeCallbackAndReturnFlushedQueue @ MessageQueue.js:130
t @ RNDebuggerWorker.js:1
VM344:24 Dynamic link: https://example.page.link/?link=https%3A%2F%2Fgoogle%2Eno
For some reason, createShortDynamicLink
produces the exception in the first part of the log above, while createDynamicLink
produces the expected output in the last line of the log above.
note: anything identifying our project is anonymised (replaced actual domains and identifiers with example.com
). We are using the correct page.link
, not only does it work to create a long dynamic link, but the rest of deep linking functionality works as expected.
ios/Podfile
:# Uncomment the next line to define a global platform for your project
platform :ios, '9.3'
def pods
pod 'Firebase/Core', '~> 5.11.0'
pod 'Firebase/DynamicLinks', '~> 5.11.0'
pod 'Fabric', '~> 1.8.0'
pod 'Crashlytics', '~> 3.11'
pod 'Intercom', '~> 5.1.8'
pod 'AdobeMobileSDK', '~> 4.17.0'
end
target 'prod' do
pods
end
target 'test' do
pods
end
target 'dev' do
pods
end
AppDelegate.m
:#import "AppDelegate.h"
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <CodePush/CodePush.h>
#import "MHEnvironment.h"
#import <Intercom/Intercom.h>
#import <Firebase.h>
#import "RNFirebaseLinks.h"
#import <ADBMobile.h>
#import "AdobeMobile.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Firebase
// Leave at top, to let all incidents during initialisation be reported to firebase
NSString *googleServicePlist = [NSString stringWithFormat:@"GoogleService-Info-%@",
[MHEnvironment sharedInstance].environment];
NSString *filePath = [[NSBundle mainBundle] pathForResource:googleServicePlist ofType:@"plist"];
[FIROptions defaultOptions].deepLinkURLScheme = [MHEnvironment sharedInstance].dynamicLinksScheme; // bundle identifier (multiple targets)
FIROptions *options = [[FIROptions alloc] initWithContentsOfFile:filePath];
[FIRApp configureWithOptions:options];
{...}
}
{...}
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
options:(NSDictionary<NSString *, id> *)options {
return [[RNFirebaseLinks instance] application:application openURL:url options:options];
}
- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray *))restorationHandler {
return [[RNFirebaseLinks instance] application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
}
@end
android/build.gradle
:import groovy.json.JsonSlurper
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.3.0'
repositories {
google()
jcenter()
maven {
url 'https://maven.fabric.io/public'
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.4'
classpath 'com.google.gms:google-services:4.0.1'
classpath 'io.fabric.tools:gradle:1.25.4'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
mavenLocal()
jcenter()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"
}
}
}
ext {
compileSdkVersion = 27
targetSdkVersion = 26
buildToolsVersion = "27.0.3"
supportLibVersion = "27.1.1"
}
subprojects {
ext {
def npmVersion = getNpmVersion()
versionMajor = npmVersion['major']
versionMinor = npmVersion['minor']
versionPatch = npmVersion['patch']
}
}
def getNpmVersion() {
def packageJsonFile = file("$projectDir.path/../package.json")
def packageJson = new JsonSlurper().parseText(packageJsonFile.text)
def versionString = packageJson['version']
def (major, minor, patch) = versionString.tokenize('.')
return [major: major, minor: minor, patch: patch]
}
android/app/build.gradle
:apply plugin: "com.android.application"
apply plugin: 'kotlin-android'
apply plugin: "io.fabric"
import com.android.build.OutputFile
project.ext.react = [
bundleInStaging : true,
devDisabledInStaging : true,
nodeExecutableAndArgs: ["/usr/local/bin/node"]
]
project.ext.vectoricons = [
iconFontNames: [ 'FontAwesome.ttf', 'MaterialIcons.ttf' ]
]
apply from: "../../node_modules/react-native/react.gradle"
apply from: "../../node_modules/react-native-code-push/android/codepush.gradle"
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
def enableSeparateBuildPerCPUArchitecture = false
def enableProguardInReleaseBuilds = false
android {
compileSdkVersion rootProject.compileSdkVersion
buildToolsVersion rootProject.buildToolsVersion
defaultConfig {
applicationId "com.example.app"
minSdkVersion 16
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode readBuildVersion()
versionName "${versionMajor}.${versionMinor}.${versionPatch}"
multiDexEnabled true
ndk {
abiFilters "armeabi-v7a", "x86"
}
}
signingConfigs {
release {
if (project.hasProperty('RELEASE_STORE_FILE')) {
storeFile file(RELEASE_STORE_FILE)
storePassword RELEASE_STORE_PASSWORD
keyAlias RELEASE_KEY_ALIAS
keyPassword RELEASE_KEY_PASSWORD
}
}
}
buildTypes {
dev {
initWith debug
applicationIdSuffix ".dev"
matchingFallbacks = ['debug']
}
staging {
initWith release
signingConfig signingConfigs.release
applicationIdSuffix ".test"
matchingFallbacks = ['release']
}
release {
signingConfig signingConfigs.release
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
include "armeabi-v7a", "x86"
}
}
// 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:
// http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits
def versionCodes = ["armeabi-v7a": 1, "x86": 2]
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
}
}
}
dexOptions {
jumboMode = true
}
}
dependencies {
implementation project(':react-native-webview')
implementation project(':react-native-svg')
implementation project(':react-native-contacts')
implementation project(':react-native-intercom')
implementation project(':react-native-image-picker')
implementation project(':react-native-code-push')
implementation project(':react-native-firebase')
implementation "com.google.android.gms:play-services-base:16.0.1"
implementation "com.google.firebase:firebase-core:16.0.4"
implementation "com.google.firebase:firebase-analytics:16.0.4"
implementation "com.google.firebase:firebase-invites:16.0.4"
implementation('com.crashlytics.sdk.android:crashlytics:2.9.5@aar') {
transitive = true
}
implementation "com.adobe.mobile:adobeMobileLibrary:4.17.0"
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "com.android.support:multidex:1.0.3"
implementation "com.android.support:appcompat-v7:${rootProject.supportLibVersion}"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "com.facebook.react:react-native:+" // From node_modules
}
apply plugin: 'com.google.gms.google-services'
// 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'
}
task doIncrementBuildVersion << {
println 'Incrementing build version...'
incrementBuildVersion()
}
def readVersion() {
new File(project.rootDir, 'version.properties').withInputStream { stream ->
def version = new Properties()
version.load(stream)
return version
}
}
def readBuildVersion() {
def version = readVersion()
return version['build'] as int
}
def incrementBuildVersion() {
def versionFile = new File(project.rootDir, 'version.properties')
def version = readVersion() as Properties
def build = version['build'] as int
build++
version['build'] = build.toString()
versionFile.withOutputStream { stream ->
version.store(stream, null)
}
println 'Build version is now ' + build
return build
}
android/settings.gradle
:rootProject.name = 'example'
include ':react-native-webview'
project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview/android')
include ':react-native-svg'
project(':react-native-svg').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-svg/android')
include ':react-native-contacts'
project(':react-native-contacts').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-contacts/android')
include ':react-native-intercom'
project(':react-native-intercom').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-intercom/android')
include ':react-native-image-picker'
project(':react-native-image-picker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-image-picker/android')
include ':react-native-firebase'
project(':react-native-firebase').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-firebase/android')
include ':react-native-code-push'
project(':react-native-code-push').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-code-push/android/app')
include ':app'
MainApplication.kt
:package com.example.app
import android.support.multidex.MultiDexApplication
import com.facebook.react.ReactApplication
import com.facebook.react.ReactNativeHost
import com.facebook.react.ReactPackage
import com.facebook.react.shell.MainReactPackage
import com.facebook.soloader.SoLoader
import com.horcrux.svg.SvgPackage
import com.imagepicker.ImagePickerPackage
import com.microsoft.codepush.react.CodePush
import com.reactnativecommunity.webview.RNCWebViewPackage
import com.robinpowered.react.Intercom.IntercomPackage
import com.rt2zz.reactnativecontacts.ReactNativeContacts
import io.intercom.android.sdk.Intercom
import io.invertase.firebase.RNFirebasePackage
import io.invertase.firebase.links.RNFirebaseLinksPackage
import io.invertase.firebase.analytics.RNFirebaseAnalyticsPackage
import io.invertase.firebase.fabric.crashlytics.RNFirebaseCrashlyticsPackage
{...}
class MainApplication : MultiDexApplication(), ReactApplication {
private val mReactNativeHost: ReactNativeHost = object : ReactNativeHost(this) {
override fun getJSBundleFile(): String? {
return CodePush.getJSBundleFile()
}
override fun getUseDeveloperSupport(): Boolean {
return BuildConfig.DEBUG
}
override fun getPackages(): List<ReactPackage> {
return listOf(
MainReactPackage(),
RNCWebViewPackage(),
SvgPackage(),
ReactNativeContacts(),
IntercomPackage(),
ImagePickerPackage(),
RNFirebasePackage(),
RNFirebaseLinksPackage(),
RNFirebaseAnalyticsPackage(),
RNFirebaseCrashlyticsPackage(),
CodePush(resources.getString(R.string.codePush_deploymentKey), applicationContext, BuildConfig.DEBUG)
}
}
override fun getReactNativeHost(): ReactNativeHost {
return mReactNativeHost
}
override fun onCreate() {
super.onCreate()
SoLoader.init(this, false)
Intercom.initialize(this, resources.getString(R.string.intercom_apiKey), resources.getString(R.string.intercom_appId))
}
}
AndroidManifest.xml
:<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.app">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.READ_PROFILE"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<application
android:name=".MainApplication"
android:allowBackup="true"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:theme="@style/AppTheme">
<activity
android:name=".SplashActivity"
android:label="@string/app_name"
android:theme="@style/SplashTheme"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize"
android:exported="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="example.page.link" android:scheme="http"/>
<data android:host="example.page.link" android:scheme="https"/>
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity"/>
<service
android:name="com.robinpowered.react.Intercom.IntercomIntentService"
android:exported="false">
<intent-filter
android:priority="999">
<action android:name="com.google.android.c2dm.intent.RECEIVE"/>
</intent-filter>
</service>
<receiver
android:name="io.intercom.android.sdk.push.IntercomPushBroadcastReceiver"
tools:replace="android:exported"
android:exported="true"/>
</application>
</manifest>
ios 12
10.14.1
N/A
N/A
Xcode 10.1
React Native
version:0.57.4
React Native Firebase
library version:5.1.0
Firebase
module(s) you're using that has the issue:TypeScript
?N/A
ExpoKit
?ExpoKit
N/A
Think react-native-firebase
is great? Please consider supporting the project with any of the below:
React Native Firebase
and Invertase
on Twitterme too!
but in my case it's just not work in ios 12.1
I think maybe this issues can help you
https://github.com/invertase/react-native-firebase/issues/1677#issuecomment-440175789
@mf-fengsheng Thanks for the tip, but unfortunately it didn't solve my issue.
I did however find the reason why it fails.
The URL shortener service of Firebase require a API key to be set. This key is defined in GoogleService-Info.plist
. Since we have multiple targets, that require different GoogleService-Info.plist
we initialise Firebase with [FIRApp configureWithOptions:options];
. It seems though that this configuration is not respected when trying to use the URL shortener service through react-native-firebase' createShortDynamicLink
function. Renaming one of our GoogleService-Info-<some target>.plist
file to GoogleService-Info.plist
makes createShortDynamicLink
work again.
I'm not sure if the problem lies in react-native-firebase
or firebase-ios-sdk
, but createShortDynamicLink
is looking for the API key in GoogleService-Info.plist
, even though Firebase Core is initialised with another location for the Google Service plist file.
The issue is not with react-native-firebase
. Issue is tracked in firebase/firebase-ios-sdk#2112
@forsen how can I fix it? Please show the solution.
@dantn93 the problem I had was fixed by https://github.com/firebase/firebase-ios-sdk/pull/2124, and a suitable workaround was described in https://github.com/invertase/react-native-firebase/issues/1702#issuecomment-441031458. Make sure your code finds your API key (as it is required by the url shortener service).
If you're on a enterprise network:
Some enterprise networks have a SSL gateway to spy on the traffic coming in and out of the network to detect malware etc. This kind of "man in the middle" setup is something that is not compatible with the url shortener service. I ran into this problem as well, but I guess it is most likely not your problem.
@forsen i am using react native firebase 5.5.5, but still i am getting this error in ios, android its working
I decided to use google api, the rn firebase deeplink is bad
On Sun, Jul 21, 2019 at 2:00 PM affanhashone notifications@github.com
wrote:
@forsen https://github.com/forsen i am using react native firebase
5.5.5, but still i am getting this error in ios, android its working—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/invertase/react-native-firebase/issues/1702?email_source=notifications&email_token=AE3VM2Z2G7QA2NNGHERHO4LQAQCRNA5CNFSM4GF36DHKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD2N5KAI#issuecomment-513529089,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AE3VM24DRMKAMZARQFNLY33QAQCRNANCNFSM4GF36DHA
.
@dantn93 Thank You Somuch for hinting out that.
I've just spent a good half an hour to figure out why it cannot create the short link, until I figured out the page link needs to contain https://
.
new firebase.links.DynamicLink('https://google.no', 'https://example.page.link');
domainURIPrefix: The Firebase project’s Dynamic Links domain. You can find this value in the Dynamic Links section of the Firebase console. It must begin with https://
This is indicated here https://rnfirebase.io/docs/v5.x.x/releases/v5.5.x#5.5.0 and here https://rnfirebase.io/docs/v5.x.x/releases/v5.5.x#Upgrade-instructions and https://rnfirebase.io/docs/v5.x.x/links/reference/DynamicLink#Constructor - I'm not sure what more we can do? That said, @markhomoki can you confirm that this works if you use https?
@markhomoki I was having same thing in code, but still i couldn't create shortLink In IOS. SO i move to REST Full Method
Yes, in my case it's working fine using https://
. I copied the example code from https://rnfirebase.io/docs/v5.x.x/links/reference/links which says abc123.page.link
. I'm using 5.5.4 btw.
That code didn't change after v5.5.0 that I'm aware of. It may be that there are further configurations required if you use more custom links vs page.link - I haven't implemented this in my app myself but I recall there were all sorts of things that needed to be done in google and apple-specific ways to enable really custom links. Really vague comment but main point is that maybe page.link works and others don't @affanhashone were you trying xyz.page.link or something different?
@mikehardy abc123.page.link this was the example, but i created my own page link, i used with https, and it was working only in android, not on ios
Hmm @affanhashone I would suspect some sort of config issue then, if I recall the config on the iOS side went as far as entitlements or some such, but as I mentioned I haven't integrated it myself so this is necessarily vague, sorry
@mikehardy maybe some configuration issue, but couldnt figure out. so now i am using firebase rest api.
In our case the issue was that there was a /
at the end of the domainURIPrefix
supplied when creating the DynamicLink
.
Then when we use it to create the short dynamic link, the longDynamicLink
used has two /
before the query params and that screws everything up..
We can probably put a PR up that gets rid of extra slashes at the end... It took me quite a while and Xcode debugging to find the cause.
On v6 we should probably handle these better. Does anyone know the exact rules for link
& domainUriPrefix
? Currently we just check they start with http or https: https://github.com/invertase/react-native-firebase/blob/master/packages/dynamic-links/lib/builder.js#L52
I'm using https:// and still fail with createShortDynamicLink
=> update: I figured out that I have to create a dynamic link in the same project I used with database
I've just spent a good half an hour to figure out why it cannot create the short link, until I figured out the page link needs to contain
https://
.
new firebase.links.DynamicLink('https://google.no', 'https://example.page.link');
domainURIPrefix: The Firebase project’s Dynamic Links domain. You can find this value in the Dynamic Links section of the Firebase console. It must begin with https://
Thanks alot bro. made my day
@mikehardy please add it to the docs
Docs are open sources with edit button on each page, please make PR to docs
Most helpful comment
I've just spent a good half an hour to figure out why it cannot create the short link, until I figured out the page link needs to contain
https://
.new firebase.links.DynamicLink('https://google.no', 'https://example.page.link');