Skip to content

Failed to create Short Dynamic Link on iOS #1702

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
6 of 30 tasks
forsen opened this issue Nov 22, 2018 · 21 comments
Closed
6 of 30 tasks

Failed to create Short Dynamic Link on iOS #1702

forsen opened this issue Nov 22, 2018 · 21 comments

Comments

@forsen
Copy link
Contributor

forsen commented Nov 22, 2018

Issue

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.


Project Files

iOS

ios/Podfile:

  • I'm not using Pods
  • I'm using Pods and my Podfile looks like:
# 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

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>

Environment

  • Platform that you're experiencing the issue on:
    • iOS
    • Android
    • iOS but have not tested behavior on Android
    • Android but have not tested behavior on iOS
    • Both
  • If known, the version of the platform are you experiencing the issue on:
    • ios 12
  • Operating System:
    • MacOS, version: 10.14.1
    • Windows, version: N/A
    • Other, please specify: N/A
  • Build Tools:
    • 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:
    • Authentication
    • Analytics
    • Cloud Firestore
    • Cloud Messaging (FCM)
    • Crashlytics
    • Dynamic Links
    • Functions Callable
    • Invites
    • Instance ID
    • Notifications
    • Performance Monitoring
    • Realtime Database
    • Remote Config
    • Storage
  • Are you using TypeScript?
    • No
    • Yes, version: N/A
  • Are you using Expo, e.g. ExpoKit?
    • No
    • Yes, I've not ejected
    • Yes, but I have ejected to ExpoKit
    • Yes, but I have ejected to vanilla React Native
    • Expo version: N/A

Think react-native-firebase is great? Please consider supporting the project with any of the below:

@mf-fengsheng
Copy link

mf-fengsheng commented Nov 22, 2018

me too!
but in my case it's just not work in ios 12.1

I think maybe this issues can help you
#1677 (comment)

@forsen
Copy link
Contributor Author

forsen commented Nov 22, 2018

@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.

@forsen
Copy link
Contributor Author

forsen commented Nov 23, 2018

The issue is not with react-native-firebase. Issue is tracked in firebase/firebase-ios-sdk#2112

@dantn93
Copy link

dantn93 commented Jan 20, 2019

@forsen how can I fix it? Please show the solution.

@forsen
Copy link
Contributor Author

forsen commented Jan 22, 2019

@dantn93 the problem I had was fixed by firebase/firebase-ios-sdk#2124, and a suitable workaround was described in #1702 (comment). 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.

@affanhashone
Copy link

@forsen i am using react native firebase 5.5.5, but still i am getting this error in ios, android its working

@dantn93
Copy link

dantn93 commented Jul 21, 2019 via email

@affanhashone
Copy link

@dantn93 Thank You Somuch for hinting out that.

@markhomoki
Copy link

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://

@mikehardy
Copy link
Collaborator

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?

@affanhashone
Copy link

@markhomoki I was having same thing in code, but still i couldn't create shortLink In IOS. SO i move to REST Full Method

@markhomoki
Copy link

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.

@mikehardy
Copy link
Collaborator

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?

@affanhashone
Copy link

@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

@mikehardy
Copy link
Collaborator

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

@affanhashone
Copy link

@mikehardy maybe some configuration issue, but couldnt figure out. so now i am using firebase rest api.

@santiagofm
Copy link

santiagofm commented Aug 1, 2019

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.

@Ehesp
Copy link
Member

Ehesp commented Aug 2, 2019

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

@roger-ngx
Copy link

roger-ngx commented Sep 18, 2019

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

@byteab
Copy link

byteab commented Dec 8, 2020

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

@mikehardy
Copy link
Collaborator

Docs are open sources with edit button on each page, please make PR to docs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants