Angular / Ionic mobile app ios does not fetch from Firebase using angularfire
Asked Answered
P

4

10

I am trying to test a little Ionic/Angular sample app on an iOS Emulator.

On the web, all the requests to firestore using angularfire work perfectly fine.

Somehow if I try to execute the same app on the emulator, it keeps loading for the response of the request (if it was a empty response it would say that no results could be retrieved).

Fetching results forever

What is going on? Do i need to set something specifically for the Emulator to work and perform requests to Firestore?

Pneuma answered 30/10, 2021 at 11:1 Comment(5)
Open up the console in the safari dev inspection or look up the error logs in xcodeJeaz
Have you added the firebase config for an IOS app?Progestin
@Progestin yes, the firebase connection works as a authentication request is successfully performed but the firestore request does not workPneuma
Ok, as minsha said, we need to see more information. Please share the safari console with the error (Develop => Your computer => The simulator)Progestin
@Progestin there is just one Error displayed in the Console: TypeError: undefined is not an object (evaluating 'gapi.iframes.getContext')Pneuma
A
9
import { initializeApp } from 'firebase/app';
import { getFirestore } from 'firebase/firestore';
import { Capacitor } from '@capacitor/core';
import { initializeAuth, indexedDBLocalPersistence } from 'firebase/auth';
import { getAuth } from 'firebase/auth';

const firebaseApp = initializeApp({
 apiKey: process.env.VUE_APP_FIREBASE_API_KEY,
 authDomain: process.env.VUE_APP_FIREBASE_AUTH_DOMAIN,
 databaseURL: process.env.VUE_APP_FIREBASE_DATABASE_URL,
 projectId: process.env.VUE_APP_FIREBASE_PROJECT_ID,
 storageBucket: process.env.VUE_APP_FIREBASE_STORAGE_BUCKET,
 messagingSenderId: 
 process.env.VUE_APP_FIREBASE_MESSAGING_SENDER_ID,
 appId: process.env.VUE_APP_FIREBASE_APP_ID,
});


function whichAuth() {
  let auth
  if (Capacitor.isNativePlatform()) {
    auth = initializeAuth(firebaseApp, {
      persistence: indexedDBLocalPersistence
    })
  } else {
    auth = getAuth()
  }
  return auth
}

export const auth = whichAuth()
const db = getFirestore();

export const auth = whichAuth();
export { firebaseApp, db };

Then in your component, cal your method like this await signInAnonymously(auth);. Don't forget to import the auth we exported at the top.

Arterialize answered 1/11, 2021 at 6:16 Comment(7)
How to do this with AngularFire?Callboard
@Callboard did you solve this? I have the same error using AngularFireWrite
@ErnestoSchiavo You can see my workaround here: https://mcmap.net/q/1163650/-angularfireauth-and-ionic-5-login-issueCallboard
Guys, any other solution beside downgrade to version 8?Gamez
After an Upgrade to Firebase 9.6.0 and AngularFire 7.2.0 this did indeed solve my problem. Thank you!Pneuma
@ejro what is 'app' in your this line of code? initializeAuth(app, { persistence: indexedDBLocalPersistence }) ? can u plz full app.module.ts ?Injun
@MoblizeIT 'app' refers to 'firebaseApp'. I've updated the snippet. ThanksArterialize
C
5

[Edit: updated with instructions Firebase JS SDK version 9 (modular)]

This error occurs because Firebase Auth incorrectly detects its environment as a normal browser environment and tries to load remote Google APIs, which results in the error you see in the console:

TypeError: undefined is not an object (evaluating 'gapi.iframes.getContext')

Fortunately, Firebase Auth already has logic to handle running in Cordova/Ionic apps, you just need to tell it which platform it's on.

For Firebase JS SDK version 9 (modular)

Simply import the Cordova Firebase Auth implementation:

import { getAuth } from 'firebase/auth';

For Firebase JS SDK <9 or the compatibility modules (auth/compat)

In capacitor.config set server: { iosScheme: "ionic" }:

// capacitor.config.json
{
  "server": {
    "iosScheme": "ionic"
  }
}

There's a check in the auth/compat library here which, when it sees the URL scheme "ionic://", uses its Ionic/Cordova loading logic, and otherwise falls back to normal browser logic which fails with the error above.

Recent versions of Capacitor changed the URL scheme to "capacitor://" which fails this test but you can override it in your capacitor.config file (see the config option iosScheme).

(See also @alistairheath's comment here).

Cavuoto answered 3/5, 2022 at 15:36 Comment(0)
G
1

Been struggling a lot with this issue too but I managed to fix it. For those who need help here's my code.

You can delete all Firebase related imports from app.module.ts since this solution only uses Firebase. The packages rxfire and @angular/fire can be removed from your package.json. The only dependency I have is "firebase": "^9.6.1". I used observables for the getObject and list functions since that's what I'm used to and I didn't want to rewrite my original code.

import { Injectable } from '@angular/core';
import { Capacitor } from '@capacitor/core';
import { environment } from '@environment';
import { initializeApp } from 'firebase/app';
import { Auth, getAuth, indexedDBLocalPersistence, initializeAuth, signInWithCustomToken } from 'firebase/auth';
import { Database, getDatabase, onValue, orderByChild, query, ref } from 'firebase/database';
import { Observable, Observer, from } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class FirebaseService {
  private readonly database: Database;
  private readonly auth: Auth;

  constructor() {
    const firebaseApp = initializeApp(environment.firebase);

    if (Capacitor.isNativePlatform()) {
      initializeAuth(firebaseApp, {
        persistence: indexedDBLocalPersistence
      });
    }

    this.database = getDatabase(firebaseApp);
    this.auth = getAuth(firebaseApp);
  }

  connectFirebase(firebaseToken) {
    return from(signInWithCustomToken(this.auth, firebaseToken));
  }

  disconnectFirebase() {
    return from(this.auth.signOut());
  }

  getObject<T>(path: string): Observable<T> {
    return new Observable((observer: Observer<T>) => {
      const dbRef = ref(this.database, path);
      const listener = onValue(dbRef, snapshot => {
        const data = snapshot.val();
        observer.next(data);
      });

      return {
        unsubscribe() {
          listener();
        }
      };
    });
  }

  public list<T>(path: string, orderChildBy?: string): Observable<Array<T>> {
    return new Observable<Array<T>>((observer: Observer<Array<T>>) => {
      const dbRef = ref(this.database, path);
      const dbReference = !orderChildBy ? dbRef : query(dbRef, orderByChild(orderChildBy));

      const listener = onValue(dbReference, snapshot => {
        const data = Object.values<T>(snapshot.val() || {});
        console.log(path, data);
        observer.next(data);
      });

      return {
        unsubscribe() {
          listener();
        }
      };
    });
  }
}

For those who can't see the error message thrown by firebase try the following command in your Safari console to see the error.

window.location.reload()
Garrettgarrick answered 5/1, 2022 at 14:56 Comment(0)
D
0

The real problem: firebase-js-sdk on mobile iOS assumes google API (gapi) exists on the window, even when it isn't used.

I found a work around: Mock window.gapi before using firebase auth login:

window['gapi'] = {
  load: (name: string) => Promise.resolve(),
  iframes: {
    getContext: () => {
      return {
        iframe: {
          contentWindow: {
            postMessage: (message: any) => {
              console.log("gapi iframe message:", message);
            }
          }
        }
      }
    }
  }
} as any;
Deduce answered 4/2, 2022 at 17:53 Comment(2)
This doesn't work for me... Only remove the errore message...Subjective
I also found I needed to hide and show the firebase ui (open and close then re-open a modal in my case). Real hacky I know. But that plus the gapi code fixed it for me.Deduce

© 2022 - 2024 — McMap. All rights reserved.