EddyVerbruggen / nativescript-plugin-firebase

:fire: NativeScript plugin for Firebase
https://firebase.google.com
MIT License
1.01k stars 441 forks source link

Error calling Firebase functions within worker threads - "Default app has already been configured" #1856

Closed nhatter closed 3 years ago

nhatter commented 3 years ago

Hey @EddyVerbruggen, we've just switched to {N} from Ionic :) I'm really excited to see how our app will perform and what it can do with truly native rendering rather than hybrid...

Unfortunately, we're having some trouble running Firebase from within a {N} worker.

As a test, we tried this:

const ref = require("@nativescript/firebase");
onmessage =   (msg) => {
    ref.firebase.firestore.collection('/Users').where('gender','==','m').orderBy('number').limit(50).startAfter(0).get().then(e=>{
        postMessage("JS worker good");
    })
}
onerror = (e)=>{
      console.log(e);
}

This gives us: "Error: Please run firebase.init() before running firebase"

So we changed it to:

const ref = require("@nativescript/firebase");
onmessage =   (msg) => {
    ref.firebase.init().then(()=>{
        ref.firebase.firestore.collection('/Users').where('gender','==','m').orderBy('number').limit(50).startAfter(0).get().then(e=>{
            postMessage("JS worker good");
        })
    })
}
onerror = (e)=>{
      console.log(e);
}

Now we get a different error: "Error in firebase.init - Error: Default app has already been configured"

My CTO confirmed that onmessage is only being called once...

Is it possible, @EddyVerbruggen, that your plugin may be trying to call FIRApp.configure() twice? I see it being called from:

  1. NSNotificationCenter.defaultCenter.addObserverForNameObjectQueueUsingBlock, and
  2. In the.init() function - see: https://github.com/EddyVerbruggen/nativescript-plugin-firebase/blob/master/src/firebase.ios.ts

(Note: we are building for iOS)

HELPFUL DEBUG INFO

package.json:


{
  "name": "swiper-ns",
  "main": "src/main.ts",
  "version": "1.0.0",
  "private": true,
  "dependencies": {
    "@angular/animations": "^12.2.0",
    "@angular/common": "^12.2.0",
    "@angular/compiler": "^12.2.0",
    "@angular/core": "^12.2.0",
    "@angular/forms": "^12.2.0",
    "@angular/platform-browser": "^12.2.0",
    "@angular/platform-browser-dynamic": "^12.2.0",
    "@angular/router": "^12.2.0",
    "@nativescript/angular": "^12.2.0",
    "@nativescript/core": "~8.1.1",
    "@nativescript/firebase": "^11.1.3",
    "@nativescript/theme": "~3.0.1",
    "moment": "^2.29.1",
    "nativescript-ngx-fonticon": "^7.0.0",
    "nativescript-ui-sidedrawer": "~10.0.1",
    "ng-recaptcha": "^8.0.1",
    "reflect-metadata": "~0.1.13",
    "require-from-url": "^3.1.3",
    "rxjs": "~7.3.0",
    "xhr": "^2.6.0",
    "zone.js": "~0.11.4"
  },
  "devDependencies": {
    "@angular/compiler-cli": "^12.2.0",
    "@nativescript/ios": "8.1.0",
    "@nativescript/types": "~8.1.1",
    "@nativescript/webpack": "~5.0.0",
    "@ngtools/webpack": "^12.2.13",
    "nativescript-worker-loader": "^0.12.1",
    "ts-loader": "^9.2.6",
    "typescript": "~4.3.5"
  }
}

firebase.nativescript.json

{
    "using_ios": true,
    "using_android": false,
    "analytics": false,
    "firestore": true,
    "realtimedb": true,
    "authentication": true,
    "remote_config": false,
    "performance_monitoring": false,
    "external_push_client_only": false,
    "messaging": false,
    "in_app_messaging": false,
    "crashlytics": false,
    "storage": true,
    "functions": true,
    "facebook_auth": false,
    "google_auth": false,
    "admob": false,
    "dynamic_links": false,
    "ml_kit": false
}

Note: we are trying to build for iOS

nhatter commented 3 years ago

UPDATE:

So I spoke to my CTO and apparently we are calling firebase.init() in two different contexts:

  1. The main thread
  2. The worker thread

The problem is that main and worker threads have totally different contexts... and there is no easy way (AFAIK) to pass object references between the main thread and worker threads.

We must call firebase.init from the main thread or else we get "missing initialisation".

But when we try to use firebase from the worker thread, it tells us to call firebase.init()... the problem is, calling init in the worker thread then gives us the error: Error in firebase.init - Error: Default app has already been configured

My guess is, however, that there is some persistence between the two as far as Google Firebase is concerned, otherwise we wouldn't be getting this error...

Possible solution: Ideally, @EddyVerbruggen's nativescript-plugin-firebase would have an argument in .init which contains a flag like SKIP_CONFIGURE

And then in the firebase init code line 538 in https://github.com/EddyVerbruggen/nativescript-plugin-firebase/blob/master/src/firebase.ios.ts#L538:

if (!firebase._configured) {
        firebase._configured = true;
        if (typeof (FIRApp) !== "undefined" && !SKIP_CONFIGURE) {
          FIRApp.configure();
        }
} 

This way, FIRApp.configure() can be optional and hopefully not keep causing the error....

Or, better yet, find a way to make the plugin perhaps more worker-friendly or to have a "callFromWorker" mode...

Multi-threading is always a bit complex anyway :)

nhatter commented 3 years ago

So, I semi-jokingly suggested to my CTO that we "hack core" and try to modify the state variables of @EddyVerbruggen's plugin manually... and it actually worked!

Setting firebase.initialized = true did the trick :) Apparently we are now able to use Firebase from workers now.

wardourdigital commented 3 years ago

Hi @nhatter I'm attempting to do the same thing. I would be really grateful if you are able to elaborate on this. I am also trying to run a call to firebase from a worker.

What were your exact steps to success?

Thanks.