'Angular / Ionic mobile app ios does not fetch from Firebase using angularfire

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?



Solution 1:[1]

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.

Solution 2:[2]

TL;DR: in capacitor.config set server: { iosScheme: "ionic" }

It looks like the issue is Firebase incorrectly detecting the environment (see @alistairheath's comment here).

There's a check in the auth/compat library here which checks for the URL scheme "ionic://", which was used by older versions of Capacitor (it's now "capacitor://") and if you set it back to "ionic://" (see the config option iosScheme) it seems to work again and doesn't attempt to load the gapi libraries.

Solution 3:[3]

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()

Solution 4:[4]

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;

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Will Madden
Solution 3 SeppeDev
Solution 4 Jameson Saunders