Skip to content
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

Dynamic links on IOS not working #3450

Open
TalEliel opened this issue Apr 12, 2020 · 44 comments
Open

Dynamic links on IOS not working #3450

TalEliel opened this issue Apr 12, 2020 · 44 comments

Comments

@TalEliel
Copy link

@TalEliel TalEliel commented Apr 12, 2020

Issue

Describe your issue here
Hey, I have been trying to use dynamic links in my app and ran into an issue on IOS where the dynamicLinks().getInitialLink() always returns null and the dynamicLinks().onLink() is not called upon entering the app from dynamic link

Project Files

Javascript

Click To Expand

package.json:

  "dependencies": {
    "@react-native-community/async-storage": "1.6.2",
    "@react-native-community/google-signin": "4.0.0",
    "@react-native-community/masked-view": "0.1.6",
    "@react-native-community/netinfo": "4.4.0",
    "@react-native-firebase/app": "6.3.4",
    "@react-native-firebase/crashlytics": "6.3.4",
    "@react-native-firebase/dynamic-links": "6.3.4",
    "@react-native-firebase/messaging": "6.3.4",
    "amplitude-js": "5.8.0",
    "lottie-react-native": "3.3.2",
    "mobx": "5.15.2",
    "mobx-react": "6.1.5",
    "moment": "2.24.0",
    "react": "16.9.0",
    "react-native": "0.61.2",
    "react-native-agora": "2.9.1-alpha.3",
    "react-native-appsflyer": "5.1.3",
    "react-native-deep-link": "0.2.9",
    "react-native-device-info": "5.2.1",
    "react-native-gesture-handler": "1.5.3",
    "react-native-haptic-feedback": "1.8.2",
    "react-native-home-indicator": "0.2.5",
    "react-native-keep-awake": "4.0.0",
    "react-native-linear-gradient": "2.5.6",
    "react-native-onesignal": "3.4.2",
    "react-native-orientation": "3.1.3",
    "react-native-reanimated": "1.7.0",
    "react-native-safe-area-context": "0.6.2",
    "react-native-screens": "2.0.0-alpha.26",
    "react-native-video": "5.0.2",
    "react-native-webview": "7.4.2",
    "react-navigation": "4.0.10",
    "react-navigation-stack": "1.10.3",
    "react-navigation-tabs": "2.7.0",
    "react-navigation-transitions": "1.0.12",
    "self-adjusting-interval": "1.0.0"
  },
  "devDependencies": {
    "@babel/core": "7.6.4",
    "@babel/plugin-proposal-decorators": "7.8.3",
    "@babel/runtime": "7.6.3",
    "@react-native-community/eslint-config": "0.0.3",
    "babel-jest": "24.9.0",
    "eslint": "6.5.1",
    "jest": "24.9.0",
    "metro-react-native-babel-preset": "0.51.1",
    "react-test-renderer": "16.9.0"
  },
  "jest": {
    "preset": "react-native"
  },
  "rnpm": {
    "assets": [
      "./assets/fonts/"
    ]
  }
}

firebase.json for react-native-firebase v6:

# N/A

iOS

Click To Expand

ios/Podfile:

  • I'm not using Pods
  • I'm using Pods and my Podfile looks like:
platform :ios, '10.0'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'

target 'jumper' do
  # Pods for jumper
  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-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/jscallinvoker', :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'

  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 'react-native-orientation', :path => '../node_modules/react-native-orientation'

  pod 'react-native-webview', :path => '../node_modules/react-native-webview'

  pod 'react-native-netinfo', :path => '../node_modules/@react-native-community/netinfo'


  pod 'react-native-onesignal', :path => '../node_modules/react-native-onesignal'

  pod 'RNDeviceInfo', :path => '../node_modules/react-native-device-info'

  pod 'RNCAsyncStorage', :path => '../node_modules/@react-native-community/async-storage'

  pod 'react-native-home-indicator', :path => '../node_modules/react-native-home-indicator'

  pod 'RNReactNativeHapticFeedback', :path => '../node_modules/react-native-haptic-feedback'
  
  pod 'lottie-ios', :path => '../node_modules/lottie-ios'
  
  pod 'lottie-react-native', :path => '../node_modules/lottie-react-native'

  pod 'react-native-keep-awake', :path => '../node_modules/react-native-keep-awake'


  pod 'react-native-video', :path => '../node_modules/react-native-video'
  
  pod 'BVLinearGradient', :path => '../node_modules/react-native-linear-gradient'
  
  pod 'Firebase/Core'
  pod 'RNFBApp', :path => '../node_modules/@react-native-firebase/app'
  pod 'Firebase/Analytics'
  pod 'RNGoogleSignin', :path => '../node_modules/@react-native-community/google-signin'
  pod 'RNFBCrashlytics', :path => '../node_modules/@react-native-firebase/crashlytics'
  pod 'RNFBMessaging', :path => '../node_modules/@react-native-firebase/messaging'
  pod 'RNFBDynamicLinks', :path => '../node_modules/@react-native-firebase/dynamic-links'
  pod 'react-native-agora', :path => '../node_modules/react-native-agora'

  target 'jumperTests' do
    inherit! :search_paths
    # Pods for testing
  end

  use_native_modules!
end

target 'jumper-tvOS' do
  # Pods for jumper-tvOS

  target 'jumper-tvOSTests' do
    inherit! :search_paths
    # Pods for testing
  end

end

target 'OneSignalNotificationServiceExtension' do
  pod 'OneSignal', '>= 2.9.3', '< 3.0'
end

AppDelegate.m:

/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#import "AppDelegate.h"
#import "Orientation.h"
#import <RNHomeIndicator.h>
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTLinkingManager.h>
#import <React/RCTRootView.h>
@import UIKit;
@import Firebase;
@import AppsFlyerLib;

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  [application registerForRemoteNotifications];
  [FIRApp configure];
  RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
  RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
                                                   moduleName:@"jumper"
                                            initialProperties:nil];

  rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
  
  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [HomeIndicatorViewController new];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  [self.window makeKeyAndVisible];
  return YES;
}

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
  return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
    return [Orientation getOrientation];
}

- (BOOL)application:(UIApplication *)application
   openURL:(NSURL *)url
   options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
  return [RCTLinkingManager application:application openURL:url options:options];
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
  [[AppsFlyerTracker sharedTracker] registerUninstall:deviceToken];
}
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler{
  return [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
}
@end


Android

Click To Expand

Have you converted to AndroidX?

  • my application is an AndroidX application?
  • I am using android/gradle.settings jetifier=true for Android compatibility?
  • I am using the NPM package jetifier for react-native compatibility?

android/build.gradle:

// N/A

android/app/build.gradle:

// N/A

android/settings.gradle:

// N/A

MainApplication.java:

// N/A

AndroidManifest.xml:

<!-- N/A -->


Environment

Click To Expand

react-native info output:

System:
    OS: macOS Mojave 10.14.6
    CPU: (4) x64 Intel(R) Core(TM) i7-2677M CPU @ 1.80GHz
    Memory: 19.49 MB / 4.00 GB
    Shell: 3.2.57 - /bin/bash
  Binaries:
    Node: 12.13.0 - /usr/local/bin/node
    npm: 6.12.0 - /usr/local/bin/npm
  SDKs:
    iOS SDK:
      Platforms: iOS 13.2, DriverKit 19.0, macOS 10.15, tvOS 13.2, watchOS 6.1
  IDEs:
    Xcode: 11.3.1/11C504 - /usr/bin/xcodebuild
  npmPackages:
    react: 16.9.0 => 16.9.0 
    react-native: 0.61.2 => 0.61.2 
  npmGlobalPackages:
    react-native-cli: 2.0.1
  • 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
  • react-native-firebase version you're using that has this issue:
    • 6.3.4
  • Firebase module(s) you're using that has the issue:
    • dynamic links
  • Are you using TypeScript?
    • No


@MarcoF09
Copy link

@MarcoF09 MarcoF09 commented Apr 13, 2020

I'm having the same issue, in addition, I'm not using a custom domain and in android works correctly.

@ahanusek
Copy link

@ahanusek ahanusek commented Apr 14, 2020

+1.

To work around this issue I'm using Linking package from RN:

   if (Platform.OS === 'ios') {
      url = await Linking.getInitialURL();
    } else {
      const res = await dynamicLinks().getInitialLink();
      if (res) {
        url = res.url;
      }
    }
@pabloluz
Copy link

@pabloluz pabloluz commented Apr 14, 2020

@TalEliel @MarcoF09 @ahanusek
Hey! I'm having similar issues as well. Where did you guys find information about configuring AppDelegate.m to allow dynamic links to work (back/foreground)?

I spent a day looking for those without luck. And it seems what needs to be added to AppDelegate.m on v5 is different from v6... right?

I appreciate in advance if you guys could point me to some documentation about that.

@cvharris
Copy link

@cvharris cvharris commented Apr 16, 2020

I'm in the same boat, why do the v6 docs not cover the necessary configurations for RNFB Dynamic Links in the AppDelegate.m?

Here's a snippet of what I found, but it only sometimes works:

AppDelegate.m

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
  return [RCTLinkingManager application:application openURL:url
                      sourceApplication:sourceApplication annotation:annotation];
}

- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
            options:(NSDictionary<NSString *, id> *)options {
    return [[RNFBDynamicLinksAppDelegateInterceptor sharedInstance] application:application openURL:url options:options];
}

- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
 restorationHandler:(void (^)(NSArray *))restorationHandler {
     return [[RNFBDynamicLinksAppDelegateInterceptor sharedInstance] application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
}

I try to handle a login link for sendSignInLinkToEmail in my Login component:

useEffect(() => {
    return dynamicLinks().onLink(async link => {
      if (auth().isSignInWithEmailLink(link.url)) {
        if (!email) {
          // TODO: If no email, have user input
          return
        }

        try {
          await auth().signInWithEmailLink(email, link.url)
        } catch (e) {
          console.error(e)
        }
      } else {
        // Not a dynamic link for auth, handle with router
      }
    })
  }, [email])

The issue this only sometimes gets triggered. I can't figure out why, maybe it's a dev only thing, but it's frustrating that the docs don't clearly say what the setup ought to be so I can troubleshoot just my code instead

@mikehardy
Copy link
Collaborator

@mikehardy mikehardy commented Apr 16, 2020

PRs always welcome, the docs all have an edit button at top right

@MarcoF09
Copy link

@MarcoF09 MarcoF09 commented Apr 17, 2020

@russellwheatley only a comment, in my case occurs also in version 5.6.0.

@KevinEdry
Copy link

@KevinEdry KevinEdry commented Apr 19, 2020

I am having the same issue, someone can address this issue?

@russellwheatley
Copy link
Member

@russellwheatley russellwheatley commented Apr 22, 2020

Hey @TalEliel, could you please confirm that you have actually added a URL type? Effectively, you need to add your Bundle ID to the URL Schemes property, and call the Identifier property whatever name you want (I've called mine "Bundle ID").
Screenshot 2020-04-22 at 11 22 40

If I did not set this, then the returned object from the dynamicLinks().getInitialLink() call would be null.

@TalEliel
Copy link
Author

@TalEliel TalEliel commented Apr 22, 2020

Hey @russellwheatley , yes i did add a utl type with the bundle identifie.
I just believe that there shoulb be some code to be implemented in the appDelegate.m file that is nowehre to be found in the documentaion and every code snipet i found online didn't work either

@russellwheatley
Copy link
Member

@russellwheatley russellwheatley commented Apr 22, 2020

Interesting. I created a fresh RN project, the only code I added to my appDelegate.m was the Firebase Config code you already have in your appDelegate.m:
Screenshot 2020-04-22 at 11 50 05

Do you mind if I see your GoogleServices-info.plist, please @TalEliel ?

@TalEliel
Copy link
Author

@TalEliel TalEliel commented Apr 22, 2020

@russellwheatley sure. this is the file.
and the strings are the actual data i replaced them for privacy
Screenshot_1

@russellwheatley
Copy link
Member

@russellwheatley russellwheatley commented Apr 22, 2020

do you mind pasting that into the comment box, please?

@TalEliel
Copy link
Author

@TalEliel TalEliel commented Apr 22, 2020

@russellwheatley

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd%22%3E
<plist version="1.0">
<dict>
    <key>CLIENT_ID</key>
    <string>CLIENT_ID</string>
    <key>REVERSED_CLIENT_ID</key>
    <string>REVERSED_CLIENT_ID</string>
    <key>API_KEY</key>
    <string>API_KEY</string>
    <key>GCM_SENDER_ID</key>
    <string>GCM_SENDER_ID</string>
    <key>PLIST_VERSION</key>
    <string>PLIST_VERSION</string>
    <key>BUNDLE_ID</key>
    <string>BUNDLE_IDr</string>
    <key>PROJECT_ID</key>
    <string>PROJECT_ID</string>
    <key>STORAGE_BUCKET</key>
    <string>STORAGE_BUCKET</string>
    <key>IS_ADS_ENABLED</key>
    <false></false>
    <key>IS_ANALYTICS_ENABLED</key>
    <false></false>
    <key>IS_APPINVITE_ENABLED</key>
    <true></true>
    <key>IS_GCM_ENABLED</key>
    <true></true>
    <key>IS_SIGNIN_ENABLED</key>
    <true></true>
    <key>GOOGLE_APP_ID</key>
    <string>GOOGLE_APP_ID</string>
    <key>DATABASE_URL</key>
    <string>DATABASE_URL</string>
</dict>
</plist>
@russellwheatley
Copy link
Member

@russellwheatley russellwheatley commented Apr 22, 2020

Hmmm, there is nothing out of the ordinary with your GoogleServices-info.plist. Unfortunately, I cannot debug if I cannot recreate. Do you mind creating a reproduction repository of this issue, please?

@rafalzawadzki
Copy link

@rafalzawadzki rafalzawadzki commented Apr 23, 2020

I'm also having problems with Dynamic Links on iOS. Both onLink and getInitialLink return null.
I can see in XCode debug console:

2020-04-23 16:11:08.614347+0200 App[52231:1275885] Task <3E55F296-87F6-43FE-BB40-46314F6FF585>.<5> finished with error [-1003] Error Domain=NSURLErrorDomain Code=-1003 "A server with the specified hostname could not be found." UserInfo={NSUnderlyingError=0x600001d29650 {Error Domain=kCFErrorDomainCFNetwork Code=-1003 "(null)" UserInfo={_kCFStreamErrorCodeKey=8, _kCFStreamErrorDomainKey=12}}, NSErrorFailingURLStringKey=https://firebasedynamiclinks-ipv6.googleapis.com/v1/installAttribution?key=<some_key>, NSErrorFailingURLKey=https://firebasedynamiclinks-ipv6.googleapis.com/v1/installAttribution?key=<some_key>, _kCFStreamErrorDomainKey=12, _kCFStreamErrorCodeKey=8, NSLocalizedDescription=A server with the specified hostname could not be found.}

Not sure if this is related? I see a similar report in firebase-ios-sdk

@mikehardy

This comment was marked as off-topic.

@taduyde
Copy link

@taduyde taduyde commented May 2, 2020

The same issue with me. Is there any update for this?

@jgersindemir
Copy link

@jgersindemir jgersindemir commented May 3, 2020

same problem.

@potatowave
Copy link

@potatowave potatowave commented May 3, 2020

Same issue. I was able to see the link with using React Native's Linking module, however it only interprets shortened URL's via the "Open link in app?" interstitial. Clicking the link from my Messages app outputs only the already shortened URL.

Also seeing this issue here, could it be related?

facebook/react-native#24429

@bobinrinder
Copy link
Contributor

@bobinrinder bobinrinder commented May 15, 2020

I had the same issue initially.
For me getInitialLink also sometimes did return null but then onLink returns the correct data.
This is my implementation if anybody wants to try it (top of App.js):

import { useEffect } from 'react';
import dynamicLinks from '@react-native-firebase/dynamic-links';

const useSetupDynamicLinks = () => {
  const handleDynamicLink = async (link) => {
    if (link && link.url) {
      // do something with it
    }
  };

  useEffect(() => {
    // for background/quit events
    const link = dynamicLinks().getInitialLink();
    handleDynamicLink(link);

    // for foreground events
    const unsubscribeLinkListener = dynamicLinks().onLink(handleDynamicLink);
    return () => unsubscribeLinkListener();
  }, []);
};

export default useSetupDynamicLinks;
@pabloluz
Copy link

@pabloluz pabloluz commented May 16, 2020

Hey @bobinrinder
Thanks for sharing that! Do you mind sharing also what you have in your AppDelegate.m?

@Aure77
Copy link
Contributor

@Aure77 Aure77 commented May 18, 2020

GULAppDelegateSwizzler seems to call RNFBDynamicLinksAppDelegateInterceptor with nil values.

So

- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication
         annotation:(id)annotation

openURL is nil, initialLinkUrl is not setup and remains nil/null.

image

@awinograd
Copy link

@awinograd awinograd commented May 26, 2020

I'm seeing this behavior on my app as well. Any additional context that would be helpful?

@awinograd
Copy link

@awinograd awinograd commented May 27, 2020

My issues appeared to be related to using a custom domain and needing to set FirebaseDynamicLinksCustomDomains as documented in the official FB docs: https://firebase.google.com/docs/dynamic-links/ios/receive#objective-c_3

EDIT: Scratch that... it was working in my Debug build but doesn't work on a cold app-start in my release version

@shirakaba
Copy link

@shirakaba shirakaba commented Jun 1, 2020

Background

I'm experiencing this issue too. Specifically, the Dynamic Link from a "passwordless email authentication" email, when clicked in Apple Mail, has failed to open my app, and has instead opened iOS Safari. This has been the case for 3 out of my 8 testers, which is a really awful failure rate. They're all on iOS 13.

@russellwheatley Could I please have a basic sanity check of whether my AppDelegate.m has been implemented correctly to expect that Dynamic Links should work?

I've following the latest Dynamic Links documentation for React Native Firebase (which, incidentally, have greatly improved in the last weeks – thank you!). It doesn't mention anything about how we should set up AppDelegate.m, however.

Firebase Dynamic Links for iOS official docs

For reference (please nobody copy this without reading on), in Google's Firebase Dynamic Links docs for iOS, they show the following:

- (BOOL)application:(UIApplication *)app
            openURL:(NSURL *)url
            options:(NSDictionary<NSString *, id> *)options {
  return [self application:app
                   openURL:url
         sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey]
                annotation:options[UIApplicationOpenURLOptionsAnnotationKey]];
}

- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication
         annotation:(id)annotation {
  FIRDynamicLink *dynamicLink = [[FIRDynamicLinks dynamicLinks] dynamicLinkFromCustomSchemeURL:url];

  if (dynamicLink) {
    if (dynamicLink.url) {
      // Handle the deep link. For example, show the deep-linked content,
      // apply a promotional offer to the user's account or show customized onboarding view.
      // ...
    } else {
      // Dynamic link has empty deep link. This situation will happens if
      // Firebase Dynamic Links iOS SDK tried to retrieve pending dynamic link,
      // but pending link is not available for this device/App combination.
      // At this point you may display default onboarding view.
    }
    return YES;
  }
  return NO;
}

React Native Dynamic Links

I notice that in React Native Firebase, however, some magic is going on. The library implements RNFBDynamicLinksAppDelegateInterceptor, which swizzles the app's AppDelegate.

I have no clue exactly what the swizzling is doing behind the scenes, and it would be great if it were documented!

My own code

My guess from debugging, though, is that RNFBDynamicLinksAppDelegateInterceptor runs before our own AppDelegate runs, and therefore when using React Native Firebase, we don't need to write anything in openURL:options:completionHandler: at all.

Nonetheless, I've put a guard in to make sure that my app (which also handles URLs passed to it from an Action Extension) doesn't accidentally handle a URL that has already been handled. Could you please clarify whether this guard is redundant @russellwheatley.

openURL:options:completionHandler:

// For an app called Sundial, with the bundle ID: "com.sundial.app"
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
  if(url){
    if([url.absoluteString hasPrefix:@"com.sundial.app://google"]){
      RCTLogInfo(@"This is a Firebase Dynamic Link! Should have been handled by RNFBDynamicLinksAppDelegateInterceptor already.");
      return YES;
    }

    if([url.absoluteString hasPrefix:@"com.sundial.app://sundial/openURL/"]){
      RCTLogInfo(@"Our Action Extension (e.g. 'Open in Sundial') passed us a link! TODO: handle.");
      return YES;
    }
  }
  return NO;
}

application:didFinishLaunchingWithOptions:

For my application:didFinishLaunchingWithOptions:, I've written the following:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  // As per: https://rnfirebase.io/#configure-firebase-with-ios-credentials
  if ([FIRApp defaultApp] == nil) {
    [FIRApp configure];
  }

 // Strictly set up RCTBridge, etc. only AFTER configuring FIRApp!

  // ...

  // I was advised from somewhere to add in this line, possibly from the React Native
  // Firebase Issues. Or maybe it was recommended by some other unrelated library.
  // I've long since forgotten.
  //
  // Either way, could you please clarify whether this line should be removed or not,
  // given that React Native Firebase has this mysterious AppDelegate swizzling?
  [super application:application didFinishLaunchingWithOptions:launchOptions];

  return YES;
}

Thank you very much for any help! Happy to provide more details.

@shirakaba
Copy link

@shirakaba shirakaba commented Jun 1, 2020

+1.

To work around this issue I'm using Linking package from RN:

   if (Platform.OS === 'ios') {
      url = await Linking.getInitialURL();
    } else {
      const res = await dynamicLinks().getInitialLink();
      if (res) {
        url = res.url;
      }
    }

@ahanusek Do the Dynamic links work for iOS in all cases when you use Linking, where they'd been failing before by using React Native Firebase Dynamic Links?

@shirakaba shirakaba mentioned this issue Jun 1, 2020
1 of 10 tasks complete
@alex-mironov
Copy link

@alex-mironov alex-mironov commented Jun 11, 2020

Has anyone faced the issues when it says:

NSErrorFailingURLStringKey=https://firebasedynamiclinks-ipv6.googleapis.com/v1/installAttribution?key=AIzaSyC4MXdWE2_EwJQXU1kC8hZZ7Vm1XOvCufA,
NSErrorFailingURLKey=https://firebasedynamiclinks-ipv6.googleapis.com/v1/installAttribution?key=AIzaSyC4MXdWE2_EwJQXU1kC8hZZ7Vm1XOvCufA

The whole error:

Task <401C75F4-F8F0-47F4-A92D-7778E3F49A72>.
<5> finished with error [-1003]
Error Domain=NSURLErrorDomain Code=-1003
"A server with the specified hostname could not be found."
UserInfo={NSUnderlyingError=0x600002cdc420
{Error Domain=kCFErrorDomainCFNetwork Code=-1003 "(null)"
UserInfo={
_kCFStreamErrorCodeKey=8,
_kCFStreamErrorDomainKey=12}},
NSErrorFailingURLStringKey=https://firebasedynamiclinks-ipv6.googleapis.com/v1/installAttribution?key=AIzaSyC4MXdWE2_EwJQXU1kC8hZZ7Vm1XOvCufA,
NSErrorFailingURLKey=https://firebasedynamiclinks-ipv6.googleapis.com/v1/installAttribution?key=AIzaSyC4MXdWE2_EwJQXU1kC8hZZ7Vm1XOvCufA,
_kCFStreamErrorDomainKey=12,
_kCFStreamErrorCodeKey=8,
NSLocalizedDescription=A server with the specified hostname could not be found.}

🙏

@mikehardy
Copy link
Collaborator

@mikehardy mikehardy commented Jun 11, 2020

@alex-mironov if I recall correctly someone mentioned it in another issue and it was their project setup was incomplete. #3755 (comment)

@alex-mironov
Copy link

@alex-mironov alex-mironov commented Jun 11, 2020

@mikehardy thanks, from what I can tell setup is fine, the link is opened. The problem I'm seeing, is that this failure on start increases startup time significantly. I'm trying to find a way to postpone deeplinks initialization, but it seems to be native part, which I cannot influence much from the JS...

Here is related issue btw firebase/firebase-ios-sdk#5032

@mikehardy
Copy link
Collaborator

@mikehardy mikehardy commented Jun 11, 2020

How unfortunate. A quick read of that issue (thanks for the link!) shows this as prior art: googleapis/google-api-dotnet-client#1186 (comment) and indicating that the hostname is either misconfigured or as a recent PR indicates, they need to handle it better

In the meantime, there is nothing as nifty that I have seen as patch-package for cocoapods, but I wonder if you could hand-hack https://github.com/firebase/firebase-ios-sdk/blob/master/FirebaseDynamicLinks/Sources/FIRDynamicLinkNetworking.m#L36 to be v4 in both cases if it would work 🤔 for now

@Appkidd
Copy link

@Appkidd Appkidd commented Jun 20, 2020

So after reading the entire thread we're still nowhere near to understanding what configuration is actually required for AppDelegate.m. All I can say for certain is that in a completely fresh project, the current docs for Dynamic Links at rnfirebase.io are inconclusive and do not trigger events when links are opened.

@mrbrentkelly
Copy link

@mrbrentkelly mrbrentkelly commented Jun 23, 2020

Hitting this issue also on iOS. Followed the guide here https://rnfirebase.io/dynamic-links/usage#listening-for-dynamic-links. App opens correctly when clicking on the dynamic link but dynamicLinks().getInitialLink() and dynamicLinks().onLink() aren't working?

@mrbrentkelly
Copy link

@mrbrentkelly mrbrentkelly commented Jun 23, 2020

Oh apologies, it seems like its actually an issue with the firebase sdk itself (as mentioned by others above). firebase/firebase-ios-sdk#5032

Check Xcode and I'm getting the same error:

Task <0B09E54B-6A0D-46EF-8410-2DE8B08B2313>.<3> finished with error [-1003] Error Domain=NSURLErrorDomain Code=-1003 "A server with the specified hostname could not be found." UserInfo={NSUnderlyingError=0x282c99ce0 {Error Domain=kCFErrorDomainCFNetwork Code=-1003 "(null)" UserInfo={_kCFStreamErrorCodeKey=8, _kCFStreamErrorDomainKey=12}}, NSErrorFailingURLStringKey=https://firebasedynamiclinks-ipv6.googleapis.com/v1/installAttribution?key=AIzaSyAlb7tK2gxW34JefQLI7uEm5j22ICCiILk, NSErrorFailingURLKey=https://firebasedynamiclinks-ipv6.googleapis.com/v1/installAttribution?key=AIzaSyAlb7tK2gxW34JefQLI7uEm5j22ICCiILk, _kCFStreamErrorDomainKey=12, _kCFStreamErrorCodeKey=8, NSLocalizedDescription=A server with the specified hostname could not be found.}
@mikehardy
Copy link
Collaborator

@mikehardy mikehardy commented Jun 23, 2020

@mrbrentkelly reach right into that cocoapod and hack out the ipv6 thing. I bet it works for you. If it does, then I would write a tiny idempotent change in ruby in your podfile as a post_install hook https://guides.cocoapods.org/syntax/podfile.html#post_install

That hook should take the patch you generate from running patch after making your change, and should attempt to apply it (a la the javascript-level patch-package)

You'll have reproducibly applied your change then and you can maybe move forward until the upstream issue is resolved

There are many in this position, any report you had of success with this style would probably be met with a lot of thanks

@mrbrentkelly
Copy link

@mrbrentkelly mrbrentkelly commented Jun 23, 2020

Thanks @mikehardy 👍. I just removed what I thought to be the code ipv6 code in FirebaseDynamicLinks Pod. The error goes away from the console but dynamicLinks().getInitialLink() and dynamicLinks().onLink() still aren't getting called unfortunately 🤔

@mrbrentkelly
Copy link

@mrbrentkelly mrbrentkelly commented Jun 24, 2020

Ok I've managed to get this working by simply adding FirebaseDynamicLinksCustomDomains to my Info.plist (as suggested by others above 👍).

Works on Debug and Release builds thankfully.

https://firebase.google.com/docs/dynamic-links/ios/receive#open-dynamic-links-in-your-app

<key>FirebaseDynamicLinksCustomDomains</key>
<array>
  <string>https://mycustomdomain.com/</string>
</array>

Note, I'm still seeing the "A server with the specified hostname could not be found." error in my Xcode console but it doesn't appear to affect the dynamic link going through.

@awinograd
Copy link

@awinograd awinograd commented Jun 24, 2020

@mrbrentkelly I had a similar experience to you where adding the plist key/value let my links staart working as expected. However I quickly realized that the flow was pretty flaky. I would say I had about 50/50 success/failure when using the link through an app install

@RodolfoGS
Copy link

@RodolfoGS RodolfoGS commented Jun 25, 2020

I fixed with this simple line in the openURL delegate in AppDelegate.m:
[[FIRDynamicLinks dynamicLinks] dynamicLinkFromCustomSchemeURL:url];

And my final code with Firebase Dynamic Links + React Native Linking:

- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
  // fix Firebase Dynamic Links
  [[FIRDynamicLinks dynamicLinks] dynamicLinkFromCustomSchemeURL:url];

  // Deep Links required: https://reactnative.dev/docs/linking#enabling-deep-links
  return [RCTLinkingManager application:app openURL:url options:options];
}

- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
{
  // Deep Links required: https://reactnative.dev/docs/linking#enabling-deep-links
  return [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
}

Versions:

react-native: 0.61.5
@react-native-firebase/dynamic-links: 7.2.0
@mikehardy
Copy link
Collaborator

@mikehardy mikehardy commented Jun 25, 2020

@RodolfoGS something went wrong with AppDelegate swizzling for you, that should not be necessary and is not documented because it is not supposed to be necessary:

+ (instancetype)sharedInstance {
static dispatch_once_t once;
static RNFBDynamicLinksAppDelegateInterceptor *sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[RNFBDynamicLinksAppDelegateInterceptor alloc] init];
sharedInstance.initialLinkUrl = nil;
sharedInstance.initialLinkMinimumAppVersion = nil;
[GULAppDelegateSwizzler proxyOriginalDelegate];
[GULAppDelegateSwizzler registerAppDelegateInterceptor:sharedInstance];
});
return sharedInstance;
}

@melvynhills
Copy link
Contributor

@melvynhills melvynhills commented Jun 30, 2020

Haven't had problems with current lib version, except with Detox e2e tests using device.openURL().
@RodolfoGS 's snippet fixed it for me.

I thought RCTLinkingManager implementation was not needed when using Firebase Dynamic Links, but apparently it is?

@mcorner
Copy link

@mcorner mcorner commented Jul 3, 2020

I was getting very inconsistent results when I set up the onLink listener before calling getInitialLink. Sometimes it would trigger the listener first and getInitialLink would be null. YMMV.

@melvynhills
Copy link
Contributor

@melvynhills melvynhills commented Jul 3, 2020

Now considering just using the standard React Native Linking lib with Linking.getInitialURL() and Linking.addEventListener('url', handler) combined with the new dynamicLinks.resolveLink() to get the best of both worlds. Will I miss anything from the Dynamic Links counterparts?

@efstathiosntonas
Copy link
Contributor

@efstathiosntonas efstathiosntonas commented Jul 3, 2020

Hey guys, I really don't know if it matters or not but have you tried setting FIROptions before FIRApp configure like so:

[FIROptions defaultOptions].deepLinkURLScheme = @"deep_link_alias_name_or_whatever";
[FIRApp configure];

ps. I'm using v5.6.0

@ebrahimhassan121
Copy link

@ebrahimhassan121 ebrahimhassan121 commented Jul 8, 2020

from google docs you need to add that FirebaseDynamicLinksCustomDomains in info.plist .
iOS only: In your Xcode project's Info.plist file, create a key called FirebaseDynamicLinksCustomDomains and set it to your app's Dynamic Links URL prefixes. For example:


<key>FirebaseDynamicLinksCustomDomains</key>
<array>
  <string>https://example.com/link</string>
  <string>https://example.com/promos</string>
</array>

https://firebase.google.com/docs/dynamic-links/custom-domains#using_your_web_domain_for

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

Successfully merging a pull request may close this issue.

None yet
You can’t perform that action at this time.