React-native-firebase: Firebase functions not using local firebase emulator

Created on 27 May 2019  路  8Comments  路  Source: invertase/react-native-firebase


Issue



Hey Invertase team 馃敟 馃敟 馃敟!
You are doing a great job (:
I'm facing some strange issue, I can't make the function app to use my local function emulator.
I followed all installation steps, everything is working except pointing the code to use my local machine, it runs directly on the cloud.
My app is IOS only, therefor android not tested.

My react code that uses Functions:

import firebase from "react-native-firebase";

firebase.functions().useFunctionsEmulator("http://localhost:5000");

export async function getCode() {
  const getCodeFromBackend = firebase.functions().httpsCallable("getCode");
  const result = await getCodeFromBackend();
  return result.data.code;
}

Project Files






iOS

Click To Expand

#### `ios/Podfile`: - [ ] I'm not using Pods - [x] I'm using Pods and my Podfile looks like:

platform :ios, '9.0'

target 'street' do
  # Uncomment the next line if you're using Swift or would like to use dynamic frameworks
  # use_frameworks!

  # https://github.com/facebook/react-native/blob/v0.59.4/React.podspec
  pod 'React', :path => '../node_modules/react-native', :subspecs => [
    'Core',
    'CxxBridge',
    'DevSupport',
    'RCTAnimation',
    'RCTNetwork',
    'RCTText',
    'RCTWebSocket',
    'RCTImage',
  ]
  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 'FBSDKCoreKit', '~> 4.42.0'
  pod 'FBSDKLoginKit', '~> 4.42.0'
  pod 'FBSDKShareKit', '~> 4.42.0'
  pod 'react-native-fbsdk', :path => '../node_modules/react-native-fbsdk'

  pod 'Firebase/Core', '~> 5.20.1'
  pod 'Firebase/Functions', '~> 5.20.1'
  pod 'Firebase/Messaging', '~> 5.20.1'
  pod 'Firebase/Auth', '~> 5.20.1'
  pod 'RNFirebase', :path => '../node_modules/react-native-firebase/ios'

  pod 'RNCAsyncStorage', :path => '../node_modules/@react-native-community/async-storage'
  pod 'RNGestureHandler', :path => '../node_modules/react-native-gesture-handler'
  pod 'RNLocalize', :path => '../node_modules/react-native-localize'
  pod 'RNSVG', :path => '../node_modules/react-native-svg'
  pod 'RNVectorIcons', :path => '../node_modules/react-native-vector-icons'

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 <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>

#import <FBSDKCoreKit/FBSDKCoreKit.h>
#import <FBSDKLoginKit/FBSDKLoginKit.h>
#import <FBSDKShareKit/FBSDKShareKit.h>
#import <Firebase/Firebase.h>


@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  [FIRApp configure];
  [[FBSDKApplicationDelegate sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions];
  RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
  RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
                                                   moduleName:@"kipp"
                                            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 = [UIViewController 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
}

- (BOOL)application:(UIApplication *)application
 openURL:(NSURL *)url
 options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
BOOL handled = [[FBSDKApplicationDelegate sharedInstance] application:application
 openURL:url
 sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey]
 annotation:options[UIApplicationOpenURLOptionsAnnotationKey]
 ];
 // Add any custom logic here.
 return handled;
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
 [FBSDKAppEvents activateApp];
}

@end


Android

Click To Expand

#### `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:**

  React Native Environment Info:
    System:
      OS: macOS 10.14.3
      CPU: (8) x64 Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz
      Memory: 165.87 MB / 16.00 GB
      Shell: 5.7.1 - /usr/local/bin/zsh
    Binaries:
      Node: 11.14.0 - ~/.nvm/versions/node/v11.14.0/bin/node
      Yarn: 1.15.2 - ~/.yarn/bin/yarn
      npm: 6.7.0 - ~/.nvm/versions/node/v11.14.0/bin/npm
      Watchman: 4.9.0 - /usr/local/bin/watchman
    SDKs:
      iOS SDK:
        Platforms: iOS 12.2, macOS 10.14, tvOS 12.2, watchOS 5.2
    IDEs:
      Xcode: 10.2/10E125 - /usr/bin/xcodebuild
    npmPackages:
      react: 16.8.3 => 16.8.3 
      react-native: 0.59.8 => 0.59.8 
- **Platform that you're experiencing the issue on**: - [x] iOS - [ ] Android - [x] **iOS** but have not tested behavior on Android - [ ] **Android** but have not tested behavior on iOS - [ ] Both - **`Firebase` module(s) you're using that has the issue:** - `Functions` - **Are you using `TypeScript`?** - `N`




Think react-native-firebase is great? Please consider supporting all of the project maintainers and contributors by donating via our Open Collective where all contributors can submit expenses. [Learn More]

Stale

Most helpful comment

I created a patchfile with patch-package and got this working w/ the iOS simulator. Thanks @moshe

diff --git a/node_modules/react-native-firebase/ios/RNFirebase/functions/RNFirebaseFunctions.m b/node_modules/react-native-firebase/ios/RNFirebase/functions/RNFirebaseFunctions.m
index ef5eba7..5e3ed35 100644
--- a/node_modules/react-native-firebase/ios/RNFirebase/functions/RNFirebaseFunctions.m
+++ b/node_modules/react-native-firebase/ios/RNFirebase/functions/RNFirebaseFunctions.m
@@ -7,6 +7,8 @@
 #import <FirebaseFunctions/FIRError.h>

 @implementation RNFirebaseFunctions
+FIRFunctions *functions;
+
 RCT_EXPORT_MODULE();

 RCT_EXPORT_METHOD(httpsCallable:
@@ -22,8 +24,11 @@ @implementation RNFirebaseFunctions
                   rejecter:
                   (RCTPromiseRejectBlock) reject
                  ){
-    FIRApp *firebaseApp = [RNFirebaseUtil getApp:appName];
-    FIRFunctions *functions = [FIRFunctions functionsForApp:firebaseApp region:region];
+    if (functions == nil) {
+        FIRApp *firebaseApp = [RNFirebaseUtil getApp:appName];
+        functions = [FIRFunctions functionsForApp:firebaseApp region:region];
+    }
+

     FIRHTTPSCallable *callable = [functions HTTPSCallableWithName:name];

@@ -64,8 +69,10 @@ @implementation RNFirebaseFunctions
                   rejecter:
                   (RCTPromiseRejectBlock) reject
                  ){
-    FIRApp *firebaseApp = [RNFirebaseUtil getApp:appName];
-    FIRFunctions *functions = [FIRFunctions functionsForApp:firebaseApp region:region];
+    if (functions == nil) {
+        FIRApp *firebaseApp = [RNFirebaseUtil getApp:appName];
+        functions = [FIRFunctions functionsForApp:firebaseApp region:region];
+    }
     [functions useFunctionsEmulatorOrigin:origin];
     resolve([NSNull null]);
 }

All 8 comments

Hey @moshe which version of RNFB are you using?

from package.json:
{"react-native-firebase": "^5.3.1"}
from package-lock.json:

{
          "react-native-firebase": {
      "version": "5.3.1",
      "resolved": "https://registry.npmjs.org/react-native-firebase/-/react-native-firebase-5.3.1.tgz",
      "integrity": "sha512-eOuSqIl+DZPNmN5ivSLgkEcsl5m0sXxttoPBn3bOHfI+apEd4I8F+9fcbHerUFUvLzesXmHR9locjhR6IJ+q3w==",
      "requires": {
        "opencollective-postinstall": "^2.0.0",
        "prop-types": "^15.6.2"
      }
    }

It was a long night but we finally able to solve it.
Seems like calling [FIRFunctions functionsForApp:firebaseApp region:region] in RNFirebaseFunctions.m creates a new instance of Cloud Functions client every time (docs) and losing the emulator context.
We added a small patch to RNFB locally

diff --git 1/node_modules/react-native-firebase/ios/RNFirebase/functions/RNFirebaseFunctions.m 2/scripts/files/RNFirebaseFunctions.m
index ef5eba7..2e73c22 100644
--- 1/node_modules/react-native-firebase/ios/RNFirebase/functions/RNFirebaseFunctions.m
+++ 2/scripts/files/RNFirebaseFunctions.m
@@ -7,6 +7,8 @@
 #import <FirebaseFunctions/FIRError.h>

 @implementation RNFirebaseFunctions
+FIRFunctions *functions;
+
 RCT_EXPORT_MODULE();

 RCT_EXPORT_METHOD(httpsCallable:
@@ -22,8 +24,10 @@ RCT_EXPORT_METHOD(httpsCallable:
                   rejecter:
                   (RCTPromiseRejectBlock) reject
                  ){
-    FIRApp *firebaseApp = [RNFirebaseUtil getApp:appName];
-    FIRFunctions *functions = [FIRFunctions functionsForApp:firebaseApp region:region];
+    if (functions == nil) {
+        FIRApp *firebaseApp = [RNFirebaseUtil getApp:appName];
+        functions = [FIRFunctions functionsForApp:firebaseApp region:region];
+    }

     FIRHTTPSCallable *callable = [functions HTTPSCallableWithName:name];

@@ -64,8 +68,10 @@ RCT_EXPORT_METHOD(useFunctionsEmulator:
                   rejecter:
                   (RCTPromiseRejectBlock) reject
                  ){
-    FIRApp *firebaseApp = [RNFirebaseUtil getApp:appName];
-    FIRFunctions *functions = [FIRFunctions functionsForApp:firebaseApp region:region];
+    if (functions == nil) {
+        FIRApp *firebaseApp = [RNFirebaseUtil getApp:appName];
+        functions = [FIRFunctions functionsForApp:firebaseApp region:region];
+    }
     [functions useFunctionsEmulatorOrigin:origin];
     resolve([NSNull null]);
 }

It's probably not the right change as it not putting the region and the app into account.
Probably need to create a map of (region, app) -> functionsClient but we not feeling comfortable as yesterday was the first time we declared a variable in Objective-C 馃榾

Or maybe it is something we missing?

Correct change or not, that's a huge +1 for being so resourceful and finding at least the apparent cause...

This is handled on v6 correctly;

We keep the specified origin in JS:
https://github.com/invertase/react-native-firebase/blob/master/packages/functions/lib/index.js#L82

and send it as an argument on every function request:
https://github.com/invertase/react-native-firebase/blob/master/packages/functions/ios/RNFBFunctions/RNFBFunctionsModule.m#L34

If you're looking to get this on v5, perhaps backporting the v6 changes would be the way to go here; keeping a singleton on native works but isn't a long term solution and will cause issues with app.delete() and potentially RN reloads.

This is only needed on iOS as the Android SDK already persists this.

I created a patchfile with patch-package and got this working w/ the iOS simulator. Thanks @moshe

diff --git a/node_modules/react-native-firebase/ios/RNFirebase/functions/RNFirebaseFunctions.m b/node_modules/react-native-firebase/ios/RNFirebase/functions/RNFirebaseFunctions.m
index ef5eba7..5e3ed35 100644
--- a/node_modules/react-native-firebase/ios/RNFirebase/functions/RNFirebaseFunctions.m
+++ b/node_modules/react-native-firebase/ios/RNFirebase/functions/RNFirebaseFunctions.m
@@ -7,6 +7,8 @@
 #import <FirebaseFunctions/FIRError.h>

 @implementation RNFirebaseFunctions
+FIRFunctions *functions;
+
 RCT_EXPORT_MODULE();

 RCT_EXPORT_METHOD(httpsCallable:
@@ -22,8 +24,11 @@ @implementation RNFirebaseFunctions
                   rejecter:
                   (RCTPromiseRejectBlock) reject
                  ){
-    FIRApp *firebaseApp = [RNFirebaseUtil getApp:appName];
-    FIRFunctions *functions = [FIRFunctions functionsForApp:firebaseApp region:region];
+    if (functions == nil) {
+        FIRApp *firebaseApp = [RNFirebaseUtil getApp:appName];
+        functions = [FIRFunctions functionsForApp:firebaseApp region:region];
+    }
+

     FIRHTTPSCallable *callable = [functions HTTPSCallableWithName:name];

@@ -64,8 +69,10 @@ @implementation RNFirebaseFunctions
                   rejecter:
                   (RCTPromiseRejectBlock) reject
                  ){
-    FIRApp *firebaseApp = [RNFirebaseUtil getApp:appName];
-    FIRFunctions *functions = [FIRFunctions functionsForApp:firebaseApp region:region];
+    if (functions == nil) {
+        FIRApp *firebaseApp = [RNFirebaseUtil getApp:appName];
+        functions = [FIRFunctions functionsForApp:firebaseApp region:region];
+    }
     [functions useFunctionsEmulatorOrigin:origin];
     resolve([NSNull null]);
 }

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.

Closing this issue after a prolonged period of inactivity. If this is still present in the latest release, please feel free to create a new issue with up-to-date information.

Was this page helpful?
0 / 5 - 0 ratings