React-native-firebase: iOS Using Multiple Firebase Projects in App causes native exception on App Restart

Created on 8 Jul 2019  路  20Comments  路  Source: invertase/react-native-firebase

:fire: :fire: :fire:


Issue



We are using [email protected].

We're working on a React Native App at my company. We're trying to use 2 separate Firbase Projects within our app. One is used for push notifications, and the other is used for Firestore.

On iOS, the push notification project is initialized using GoogleService-Info.plist. The Firestore project is initialized using JavaScript calls in React Native Firebase. Both projects make React Native Firebase calls to do auth anonymously.

When we save files while running the iOS app, we get native exceptions in React Native Firebase when the React Native hot reloading tries to reload the app.

image

It seems related to the native code that handles authorization.


Project Files






iOS

Click To Expand

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

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '10.0'

target 'cca-platform-v5' do
  pod 'ExpoKit',
    :git => "http://github.com/expo/expo.git",
    :tag => "ios/2.11.0",
    :subspecs => [
      "Core"
    ],
    :inhibit_warnings => true

  # Install unimodules
  require_relative '../node_modules/react-native-unimodules/cocoapods.rb'
  use_unimodules!(exclude: ['expo-face-detector'])

  pod 'React',
    :path => "../node_modules/react-native",
    :inhibit_warnings => true,
    :subspecs => [
      "Core",
      "ART",
      "RCTActionSheet",
      "RCTAnimation",
      "RCTCameraRoll",
      "RCTGeolocation",
      "RCTImage",
      "RCTNetwork",
      "RCTText",
      "RCTVibration",
      "RCTWebSocket",
      "DevSupport",
      "CxxBridge"
    ]
  pod 'yoga',
    :path => "../node_modules/react-native/ReactCommon/yoga",
    :inhibit_warnings => true
  pod 'DoubleConversion',
    :podspec => "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec",
    :inhibit_warnings => true
  pod 'Folly',
    :podspec => "../node_modules/react-native/third-party-podspecs/Folly.podspec",
    :inhibit_warnings => true
  pod 'glog',
    :podspec => "../node_modules/react-native/third-party-podspecs/glog.podspec",
    :inhibit_warnings => true


  pod 'react-native-add-calendar-event', :path => '../node_modules/react-native-add-calendar-event'

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

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


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

  pod 'react-native-add-calendar-event', :path => '../node_modules/react-native-add-calendar-event'

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


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


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

  post_install do |installer|
    installer.pods_project.main_group.tab_width = '2';
    installer.pods_project.main_group.indent_width = '2';

    installer.target_installation_results.pod_target_installation_results
      .each do |pod_name, target_installation_result|

      if pod_name == 'ExpoKit'
        target_installation_result.native_target.build_configurations.each do |config|
          config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)']
          config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << 'EX_DETACHED=1'

          # Enable Google Maps support
          config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << 'HAVE_GOOGLE_MAPS=1'
          config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << 'HAVE_GOOGLE_MAPS_UTILS=1'

        end
      end


      if ['Amplitude-iOS','Analytics','AppAuth','Branch','CocoaLumberjack','FBSDKCoreKit','FBSDKLoginKit','FBSDKShareKit','GPUImage','JKBigInteger2'].include? pod_name
        target_installation_result.native_target.build_configurations.each do |config|
          config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '10.0'
        end
      end

      # Can't specify this in the React podspec because we need to use those podspecs for detached
      # projects which don't reference ExponentCPP.
      if pod_name.start_with?('React')
        target_installation_result.native_target.build_configurations.each do |config|
          config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '10.0'
          config.build_settings['HEADER_SEARCH_PATHS'] ||= ['$(inherited)']
        end
      end

      # Build React Native with RCT_DEV enabled and RCT_ENABLE_INSPECTOR and
      # RCT_ENABLE_PACKAGER_CONNECTION disabled
      next unless pod_name == 'React'
      target_installation_result.native_target.build_configurations.each do |config|
        config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)']
        config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << 'RCT_DEV=1'
        config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << 'RCT_ENABLE_INSPECTOR=0'
        config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] << 'ENABLE_PACKAGER_CONNECTION=0'
      end

    end
  end
end

#### `AppDelegate.m`:
// Copyright 2015-present 650 Industries. All rights reserved.

#import "AppDelegate.h"
#import <Firebase.h>
#import "RNFirebaseNotifications.h"
#import "RNFirebaseMessaging.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    [FIRApp configure];
    [RNFirebaseNotifications configure];
    return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

- (void)applicationWillEnterForeground:(UIApplication *)application
{
    [super applicationWillEnterForeground:application];
}

#pragma mark - Background Fetch

- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
    [super application:application performFetchWithCompletionHandler:completionHandler];
}

#pragma mark - Handling URLs

- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
    return [super application:app openURL:url options:options];
}

- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
{
    return [super application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
}

#pragma mark - Notifications

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)token
{
    [super application:application didRegisterForRemoteNotificationsWithDeviceToken:token];
}

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)err
{
    [super application:application didFailToRegisterForRemoteNotificationsWithError:err];
}

// firebase

- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
    [[RNFirebaseNotifications instance] didReceiveLocalNotification:notification];
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo
fetchCompletionHandler:(nonnull void (^)(UIBackgroundFetchResult))completionHandler{
    [[RNFirebaseNotifications instance] didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}

- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
    [[RNFirebaseMessaging instance] didRegisterUserNotificationSettings:notificationSettings];
}

@end
## React Native Firebase Javascript code ### firebase.js (initializes from GoogleService-Info.plist
// @flow
import firebase from 'react-native-firebase';
const apiDebug = require('debug')('ccaapp:firebase:api');

const initializeFirebase = () => {
  apiDebug('initializeFirebase() ');
  // sign in anonymously to gain access to firestore
  const firebaseSignInPromise = firebase
    .auth()
    .signInAnonymously()
    .then(data => {
      apiDebug('firebase authed anonymously, uid: ', data.user.uid);
    })
    .catch(ex => {
      apiDebug('error initing firebase: ', ex);
    });
  // enable offline persistence
  const firebaseSettingsPromise = firebase
    .firestore()
    .settings({ isPersistenceEnabled: true })
    .then(() => apiDebug('set firebase to offline persistence mode'));
  return Promise.all([firebaseSignInPromise, firebaseSettingsPromise]);
};

const FirebaseAPI = {
  initializeFirebase,
};

export default FirebaseAPI;
### mobiledb.js (initialized from code)
import firebase from 'react-native-firebase';
import { firebaseConfig } from '../config/mobiledb';

const apiDebug = require('debug')('ccaapp:mobiledb:api');

function initializeMobileDBApp() {
  apiDebug('initializeMobileDBApp start');
  apiDebug('initializeMobileDBApp firebase.apps ', JSON.stringify(firebase.apps, null, 2));

  let mobileDBApp;

  if (firebase.apps && firebase.apps.length === 2) {
    apiDebug('initializeMobileDBApp mobileDBApp is initialized');
    mobileDBApp = firebase.app('cca-mobiledb');
  } else {
    apiDebug('initializeMobileDBApp mobileDBApp was not initialized');
    mobileDBApp = firebase.initializeApp(firebaseConfig, 'cca-mobiledb');
  }

  return mobileDBApp.onReady().then(() => {
    apiDebug('initializeMobileDBApp mobileDBApp.onReady().then()');
    apiDebug('initializeMobileDBApp firebase.apps ', JSON.stringify(firebase.apps, null, 2));
    return firebase
      .app('cca-mobiledb')
      .auth()
      .signInAnonymously()
      .then(data => {
        apiDebug('configureMobileDB() firebase authed anonymously');
        return firebase.app('cca-mobiledb');
      })
      .catch(error => {
        apiDebug('configureMobileDB() error initing firebase: ', JSON.stringify(error, null, 2));
      });
  });
}

async function getMobileDBFirestore() {
  return Promise.resolve(firebase.app('cca-mobiledb').firestore());
}

const MobileDBAPI = {
  initializeMobileDBApp,
  getMobileDBFirestore,
};

export default MobileDBAPI;


Environment

Click To Expand

**`react-native info` output:**

  React Native Environment Info:
    System:
      OS: macOS 10.14.5
      CPU: (12) x64 Intel(R) Core(TM) i9-8950HK CPU @ 2.90GHz
      Memory: 1.30 GB / 32.00 GB
      Shell: 5.3 - /bin/zsh
    Binaries:
      Node: 10.15.3 - ~/.nvm/versions/node/v10.15.3/bin/node
      npm: 6.4.1 - ~/.nvm/versions/node/v10.15.3/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
      Android SDK:
        API Levels: 22, 23, 24, 25, 26, 27, 28, 29
        Build Tools: 27.0.3, 28.0.2, 28.0.3
        System Images: android-22 | Google APIs Intel x86 Atom_64, android-23 | Google APIs Intel x86 Atom, android-24 | Google APIs Intel x86 Atom, android-Q | Google APIs Intel x86 Atom
    IDEs:
      Android Studio: 3.4 AI-183.6156.11.34.5522156
      Xcode: 10.2.1/10E1001 - /usr/bin/xcodebuild
    npmPackages:
      react: 16.8.3 => 16.8.3 
      react-native: https://github.com/expo/react-native/archive/sdk-33.0.0.tar.gz => 0.59.8 
    npmGlobalPackages:
      react-native-cli: 2.0.1
      react-native-video: 4.4.1
      react-native: 0.59.9
- **Platform that you're experiencing the issue on**: - [X] 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:** - `5.5.4` - **`Firebase` module(s) you're using that has the issue:** - `Seems to be an issue in React Native Firebase Auth, I see the error in com.facebook.react.RNFirebaseAuthQueue` - **Are you using `TypeScript`?** - `Nope`




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]

Most helpful comment

ExpoKit 35 will have this fix. It was just merged over there (so I suppose if you were brave you could depend on that commit hash - https://github.com/expo/expo/commit/6f64291f6423ec04c87d8af109d47d7fa7e3b452

All 20 comments

@paulrenenichols - is there anymore on the stack trace, the screenshot only shows the 1 thread, I imagine there was more and also an error message?

@Salakar Does this help?

image

@Salakar Here's a clearer view of those logs

image

@annie-elequin thank you - that's a much clearer view of the actual issue. 馃帀

I can confirm this issue was fixed in the v5.5.5 release - [changelog]

Could you update if possible and report back if you have any further issues. Thanks

Thanks @Salakar for your help on this!
That issue referenced in the PR is definitely our problem - I was optimistic updating would fix it. Unfortunately we're still seeing this even with version 5.5.6 installed.

I updated the package and then totally removed node_modules, updated pods and whatnot, cleaned gradle, cleared the cache.

Here's the logs from the latest run:
image

Again, this is specifically when there are two initialized firebase apps - commenting out the initialization for one of them actually solves the problem.

Other random info:

  • MacOS Mojave & Catalina 10.15 Beta
  • Xcode 10.3
  • Simulator iPhone Xr 12.4

Let me know if there's any more information you need from me.

only commenting to say that without running a full 'clean-project-auto' from react-native-clean-project, I wouldn't be 100% sure the rebuild actually rebuilt. I've had bad luck on iOS rebuilding with certainty if I didn't also clear out DerivedData (which is one of the things clean-project-auto does - it also clear out pods and other caches so it's pretty network-expensive, if that is prohibitive you can run 'clean-project' and pick and choose what to do

Hmm we just use a custom "reset" script, I've never seen that library - I'll try using that @mikehardy and follow up with the results!

@annie-elequin and if your reset script has anything it's missing - the author there integrates PRs quickly. That library has saved my bacon countless times as a quick rec for others while troubleshooting things.

Alright, update from my end.

Turns out TOTALLY totally cleaning the project did the trick (thanks for that recommendation @mikehardy !)

However, here's a fun one:
image

So, I guess I should have mentioned this - we're using Expo SDK v33, mostly bare workflow (we still have expokit for OTA updates).

Upon googling I found this:
https://github.com/expo/expo/issues/5242
(which also links to the react-native-firebase issue that I've looked at)

We were about to update to Expo SDK v34, and since the only workaround is to downgrade to 5.5.4 for now, it looks like we'll go that route until we move on from ExpoKit.

@annie-elequin well that's a shame! glad the project clean worked at least, but what I don't understand from the admob dependency snarl of expo/expo#5242 and #2363 here is why people can't just update the AdMob dependency when they are using ExpoKit 33? Is there something preventing the adoption of the newer AdMob pods on the ExpoKit side?

The change in react-native-firebase was https://github.com/invertase/react-native-firebase/pull/2333/files

The API we are calling is: https://developers.google.com/admob/ios/api/reference/Classes/GADRewardBasedVideoAd#/c:objc(cs)GADRewardBasedVideoAd(py)customRewardString

I looked and can't find any sort of release notes for 'customRewardString' in the AdMob release notes page, and the API isn't versioned, is this some very new API that we're using and there is some reason ExpoKit can't work with it?

@mikehardy

I was actually just about to test upgrading the AdMob pods on the ExpoKit side but I've been away.

Let me get to that and see if there's any further issues with regards to that and if so, people should probably just peg to the new AdMob pod in their Podfile.

@mikehardy I see what youre saying - I can't think why this error would be thrown.

I don't know if this would be useful to the both of you, but I just upgraded to Expo SDK v34 and I still have react-native-firebase at 5.5.4 - but I'm seeing this same "AdMob" issue. I'll come back and update if I find anything else out.

did you make sure you were using the most up to date AdMob library? That would be the 6.6.0 Firebase/AdMob pod in the Podfile

hmm these are the only Firebase related items in our Podfile

image

well that's a head-scratcher. I wonder what would happen if you did specify Firebase/AdMob as a dependency? It should pull in a version of the ads SDK that works? Or it might blow up spectacularly, or have no change... https://github.com/invertase/react-native-firebase/blob/v5.x.x/tests/ios/Podfile.lock#L15

The pods are moving fast btw - they are on 6.6.0 now, and near as I can tell work just fine with react-native-firebase v5.5.x - I'm using all 4 of those in my work project as well, at 6.6.0 right now

I just tested the change and was unable to get it working as the main ExpoKit core explicitly depends on v7.22.0 of the Google-Mobile-Ads-SDK. Firebase/AdMob v6.3.0 requires at least v7.44.0 of the Google-Mobile-Ads-SDK. But I think this shouldn't take a priority at all and we should just peg our version to v5.5.4 of the react-native-firebase package since ExpoKit is going to be deprecated in the future.

image

Thanks for trying that! I agree it should not really take priority although I will say there is a firebase iOS crash bug that is tickled by Firebase pods <= 6.30 on iOS 13+, so when iOS13 is released there will be a scramble to move to ExpoKit 34 if you aren't on it already, so you can move up to higher version pods if you are using Firebase/AdMob as well. This isn't something we can control in react-native-firebase but folks with complicated dependency tangles (and expo + react-native-firebase counts as that, I think) need to plan ahead I think

The main thing I appreciate about your update is that the AdMob SDK does not always indicate what changed and they don't version their API, so I wasn't sure when the customRewardString came in.

Now we know at least that was some time after 7.22, so that's something.

I tested on ExpoKit v34 and that's still pegged to v7.22. So people who need iOS 13 support for AdMob will probably have to get the expo team to update the dependency or go full react-native which is what I am intending to do. I am not using the AdMob SDK so it doesn't affect me but perhaps it might affect someone else.

I mentioned it on the linked expo issue with an @ for an Expo person, perhaps they can bump their AdMob SDK dependency, but they will be aware now at least. Thanks again for the research

ExpoKit 35 will have this fix. It was just merged over there (so I suppose if you were brave you could depend on that commit hash - https://github.com/expo/expo/commit/6f64291f6423ec04c87d8af109d47d7fa7e3b452

Was this page helpful?
0 / 5 - 0 ratings

Related issues

callmejm picture callmejm  路  3Comments

jonaseck2 picture jonaseck2  路  3Comments

alizahid picture alizahid  路  3Comments

ODelibalta picture ODelibalta  路  3Comments

escobar5 picture escobar5  路  3Comments