the angular/fire method docData works on chrome/safari macos but never emits on capacitor-ios (capacitor uses webkit-safari on ios)
I can see that the game-object is really exisiting since it shows up & emtis on my desktop browsers and I am even able to interact with this object on my capacitor-ios-native-app but just not able to receive any stream updates. The game doc ref console log correclty logs the game too, so the error really needs to lie inside the docData function (?)
All other operations like reading/writing/deleting (without streams) work on ios.
Any hints for debugging would be much appreciated.
Here is my code, remember that this works perfectly on desktop.
const gameDocRef = doc(this.firestore, 'game', game.id);
if (showGameLogs) {
let toLog: any = (gameDocRef as any)._key;
console.log("gameDocRef:", gameDocRef);
console.log("🟩 CALLING DOC_DATA ON THIS GAME DOC REF:", toLog?.path?.segments);
}
const game$ = docData(gameDocRef, { idField: 'id' }).pipe(
map(data => {
if (showGameLogs) {
console.log("🟩 docData EMISSION:", data);
}
if (data === undefined) {
return null;
} else {
return data;
}
})
) as Observable<Game | null>;
I inspected the network activity before docData gets called on ios and it happens that nothing happens at all, there is no stream created or so:
comparing to the network logs I get on my chrome macbook, there IS indeed network requests happening, with “channel”:
Also do note that there is a custom capacitor plugin for firestore but it does not have a function like docData that would return an observable. It only has a function that does this with a callback approach. And the plugin is not widely used so I would prefer to stick to the more popular angular/fire library.
A minimum reproducible example for capacitor-ios would be hard to create for me and hard to test for you of course, but I will create one as soon as someone answered my other minimiumum reproducible example question (see my accounts last question).
update:
I wrote my own custom docData function with the bare Web-modular-api and it still does not create a channel on ios native capacitor (but works perfectly on desktop chrome/safari):
import { onSnapshot } from "firebase/firestore";
function docData<T>(docRef: DocumentReference<T>, options?: { idField?: string }): Observable<T> {
return new Observable<T>(observer => {
const unsubscribe = onSnapshot(docRef,
(docSnapshot) => {
if (docSnapshot.exists()) {
const data = docSnapshot.data() as T;
if (options?.idField) {
(data as any)[options.idField] = docSnapshot.id;
}
observer.next(data);
} else {
observer.next(undefined as any);
}
},
(error) => {
console.error("Error fetching document:", error);
observer.error(error);
}
);
return { unsubscribe };
});
}
even doing this does not do anything, but only on capacitor-ios (it DOES log ALWAYS on desktop safari/chrome):
onSnapshot(gameDocRef, () => console.log("EMISSION DETECTED"));
why does this not create a channel, even though I can clearly calling onSnapshot? Is this a bug inside the firebase/firestore web sdk onSnapshot method?
I ended up using this plugin which solves it on capacitor-ios:
https://github.com/capawesome-team/capacitor-firebase/tree/main/packages/firestore
Note that this library does not solve the underlying issue but instead it uses the native IOS/Android APIs from firebase, which also come with performance improvements. Read more here:
https://capawesome.io/blog/announcing-the-capacitor-firebase-cloud-firestore-plugin/
This is how I am using this plugin, to mimic the angular/fire docData function to return an observable. I tried to get the same API as the Angular docData function. But use the below code with caution as I did not test it thorougly:
import {
AddDocumentSnapshotListenerCallback,
AddDocumentSnapshotListenerOptions,
CallbackId, DocumentData,
FirebaseFirestore, RemoveSnapshotListenerOptions
} from '@capacitor-firebase/firestore';
function docData<T extends DocumentData>(docRef: DocumentReference<T>, options?: { idField?: string }): Observable<T | null> {
return new Observable<T | null>(observer => {
let callbackId: CallbackId;
const listenerOptions: AddDocumentSnapshotListenerOptions = {
reference: docRef.path
};
const callback: AddDocumentSnapshotListenerCallback<T> = (event, error) => {
if (error) {
console.error("Error fetching document:", error);
observer.error(error);
return;
}
if (event && event.snapshot) {
const data = event.snapshot.data;
if (data && options?.idField) {
try {
(data as any)[options.idField] = event.snapshot.id || null;
} catch (e) {
console.error("Error setting idField:", e);
}
}
observer.next(data || null);
} else {
observer.next(null);
}
};
FirebaseFirestore.addDocumentSnapshotListener(listenerOptions, callback)
.then(id => callbackId = id)
.catch(error => observer.error(error));
return {
unsubscribe: () => {
if (callbackId) {
const removeOptions: RemoveSnapshotListenerOptions = { callbackId };
FirebaseFirestore.removeSnapshotListener(removeOptions)
.catch(error => console.error("Error removing listener:", error));
}
}
};
});
}