Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

documentation doesn't include workaround for using FCM with ngsw #1923

markgoho started this conversation in Ideas
Discussion options

So there's this:

AngularFireMessaging is not compatible with the Angular Service Worker

If you are using the Angular Service Worker, you are not currently able to use AngularFireMessaging. If you'd like this feature please file an issue in either repository. Your alternatives are to use WorkboxJS or just simply use the Firebase Messaging Service Worker, which is detailed below.

This kind of leads folks to believe that there isn't a solution to use FCM with the Angular Service Worker. However, some light searching has revealed a few workarounds to use both. I'm hoping we could unify around one solution and then add it to the docs to help others that might just give up.

Concatenate the ngsw and FCM sw files

This one is Jeff Delaney's (@codediodeio) solution posted on AngularFirebase from April 2018.

Basically, create a new service worker file and, sometime post-build, concatenate the FCM file and ngsw file together and reference this combined file in the app.module.ts file where the service worker is registered.

Use a native firebase method useServiceWorker() to use the ngsw in place of the FCM sw

This one has popped up in a number of places, most notably on StackOverflow in a question that Michael Bleigh (@mbleigh) from Firebase answered.

Here, from what I gather (particularly from this SO answer) you can wait for the ngsw to register and then take that registered worker and pass it into useServiceWorker() and FCM will use that worker.

I haven't implemented either solution yet. I'm just trying to put some ideas out here for discussion. I'll follow up with some pros and cons of each as I'm able to try each solution. What do you guys think? Have experience with either one?

You must be logged in to vote

Replies: 25 comments 23 replies

Comment options

Hey @markgoho!

You can do that, but I'm not certain it's a good idea. The ngsw is not meant to work with other SW libraries and it also works differently than your typical Service Worker. Trying to munge the two together could result in unpredictable results and that's not really good with something as powerful as a Service Worker.

Before we could recommend it we would need to see if it is a good idea, and at my early research suggests that it is not.

The long term solution is to have the ngsw use Firebase Messaging for push notifications. This is something I've spoken with the Angular team about and they are supportive of doing it.

In the meantime I recommend using Workboxjs with Firebase Messaging. It's really easy and it's a proven path.

You must be logged in to vote
1 reply
Comment options

Hi @davideast,

Sorry to botter you, but do you know if the angular team has plans to support this.

Comment options

Thank you for this input @davideast!

I'm not sure if your recommendation against the concatenation method includes the second option as well to use firebase.messaging().useServiceWorker(ngsw). Have you tried that at all?

For a current workaround are you suggesting to scrap the ngsw (via ng add @angular/pwa) altogether in favor of a pure workbox.js implementation? If that's the case, documentation on how to do that would be great to have.

Finally, I haven't seen an issue filed in the Angular repo for this specific issue (although this issue seems very similar). I'm not 100% sure what the actual issue is that prevents us from using FCM. Since you mentioned speaking to the core team about the issue, what do you suggest as far as creating an issue to track this need for a better FCM service worker integration?

I greatly appreciate the guidance here - and I'm more than willing to do the work to create the documentation for whatever solution we gather around. I just want to make sure I'm not spending hours researching a solution that someone already has in production.

You must be logged in to vote
0 replies
Comment options

🤯 to my amazement, firebase.messaging().useServiceWorker(ngsw) actually works. I never considered doing it this way, but was able to confirm that messages are handled by the SwPush service and caching still works as expected. This seems like an ideal solution at first glance, so thanks for putting it out there @markgoho.

This is much better than just concatenating files and also preserves the angular worker - are there issues I'm not aware of @davideast ?

You must be logged in to vote
0 replies
Comment options

It looks like you have to import * as firebase from firebase in the app.module.ts to get call the firebase.messaging() is that right?

That seems....sub-optimal, but maybe I'm missing something.

You must be logged in to vote
0 replies
Comment options

Can you use import { messaging } from 'firebase/app' ? Here's what I did for now to validate that it works with vanilla firebase. One issue I've noticed is that messages seem to be shown on the browser while the app is in the foreground.

import * as firebase from 'firebase/app';
import 'firebase/messaging';
firebase.initializeApp({...})
import { SwPush } from '@angular/service-worker';
@Component(...)
export class AppComponent {
 constructor(private sw: SwPush) {
 sw.messages.subscribe(console.log);
 const messaging = firebase.messaging();
 navigator.serviceWorker.getRegistration().then(registration => {
 console.log(registration);
 messaging.useServiceWorker(registration);
 messaging.requestPermission();
 messaging.getToken();
 });
}
You must be logged in to vote
0 replies
Comment options

I am able to use the way @codediodeio did to hook up firebase and default angular ngsw-worker by do following. The foreground also receive notifications like @codediodeio point out, haven't test the background app yet. I thought the SwPush might caused the alerts on foreground app so commented out and using my own service to do logging, but it looks like not related.

app.module.ts:

 imports: [
 ...
 AngularFireModule.initializeApp(environment.firebase),
 AngularFireMessagingModule,
 ...
 ],

app.component.ts:

import { Component } from '@angular/core';
import { SwPush } from '@angular/service-worker';
import { FirebaseApp } from '@angular/fire';
import { MessagingService } from 'services/firebase';
@Component({
 selector: 'app-root',
 templateUrl: './app.component.html',
 styleUrls: ['./app.component.scss']
})
export class AppComponent {
 title = '...';
 constructor(
 private sw: SwPush,
 private firebase: FirebaseApp,
 private messagingSvc: MessagingService
 ) {
 // this.sw.messages.subscribe(console.log);
 const messaging = this.firebase.messaging();
 navigator.serviceWorker.getRegistration().then(registration => {
 if (!!registration && registration.active && registration.active.state && registration.active.state === 'activated') {
 messaging.useServiceWorker(registration);
 this.messagingSvc.requestPermission('');
 this.messagingSvc.receiveMessage();
 } else {
 console.warn('No active service worker found, not able to get firebase messaging');
 }
 });
 }
}

messaging.service.ts:

 requestPermission(userId: string) {
 this.angularFireMessaging.requestToken.subscribe(
 (token) => {
 console.log(token);
 },
 (err) => {
 console.error('Unable to get permission to notify.', err);
 }
 );
 }
 receiveMessage() {
 this.angularFireMessaging.messages.subscribe(
 (payload) => {
 console.log('new message received: ', payload);
 // We decide not to do anything yet.
 });
 }
You must be logged in to vote
0 replies
Comment options

@zhangxin511
It seems to fail on the line const messaging = this.firebase.messaging(); with the error that firebase.messaging is not a function.

Is there something missing perhaps?

You must be logged in to vote
0 replies
Comment options

@SauceCode84 I think it may because you didn't initialize the firebase app properly in your module. Can you show how you initialize it? I think you might miss the messaging module?

For me, I end up not using angular dependency injection since I used dynamic firebase configurations that got from api, this is what the partial code looks like:
The module doing nothing for me now:
image

The component using the firebase messaging:

  1. import
    image

  2. defining as a class property:
    image

  3. initialize app and messaging object, note, the configs for the app is dynamic I don't know the proper way to include in module.ts therefore I get ride of the DI system of angular:
    image

You must be logged in to vote
0 replies
Comment options

I'm trying to call a Firebase Function and I'm getting this error:

An unknown error occurred when fetching the script.
Failed to load resource: net::ERR_FILE_NOT_FOUND

searching about this Error Message it seems related to the Service Worker
so I wonder if AngularFireFunctions relies on this firebase-messaging-sw
or if it's my function that needs to be deployed properly :-?

BTW, a smooth integration with the ngsw would be ideal

You must be logged in to vote
0 replies
Comment options

This may be a dumb question, but can we use the method described here to implement FCM with the Angular Service Worker?

You must be logged in to vote
0 replies
Comment options

@wooldridgetm That if app active, this thread is mainly talk about notifications working with ngsw, service worker, so the app can get notifications when it is not active or closed.

You must be logged in to vote
0 replies
Comment options

@zhangxin511 Just so I understand you...you're saying this method doesn't allow notifications when the app is closed or isn't active?

You must be logged in to vote
0 replies
Comment options

@wooldridgetm Sorry I may miss understand your question. If you are asking if we can not use FCM but use the method you mentioned, yes you can.
If you are asking if we can receive notification when app is off-load in any means, no we won't. I barely remember I do tried the method you mentioned for the project I worked on using angular 7, where the service worker doesn't not handle notifications when app offload. I think I also checked angular 8 source one day for this and it was still not supported by then.
The problem is, the following code only works when your app is active loaded by browser:

subscribeToNotifications() {
 this.swPush.requestSubscription({
 serverPublicKey: this.VAPID_PUBLIC_KEY
 })
 .then(sub => this.newsletterService.addPushSubscriber(sub).subscribe())
 .catch(err => console.error("Could not subscribe to notifications", err));
 }

I end up using some customized logic to overwrites the default angular service worker js file and made the the off-load notification works (not for safari, nor ios, which is another story).

You must be logged in to vote
0 replies
Comment options

Ah, yes, I understand now. Thanks!

You must be logged in to vote
0 replies
Comment options

@SauceCode84 @wooldridgetm @matheo

I created a post about this if you are still interested. Along with the demo repo here

You must be logged in to vote
0 replies
Comment options

This issue in the Angular repo requested by the documentation appears to have been created here: angular/angular#34352

You must be logged in to vote
0 replies
Comment options

@zhangxin511 I have been looking at implementing your method and fired up your demo. it all works except for subscribing to messages and notificationClicks from SwPush. Did you manage to get these two working?

You must be logged in to vote
0 replies
Comment options

@bevbomb I am not sure what exactly you did, but if you follow the post, should have worked for subscribing to messages and notificationClicks from SwPush. Did you check your angular versions? There is a picture for the app there:
https://miro.medium.com/max/1400/0*iV5jSHBYBpR5m0fS

@zhangxin511 I have been looking at implementing your method and fired up your demo. it all works except for subscribing to messages and notificationClicks from SwPush. Did you manage to get these two working?

You must be logged in to vote
0 replies
Comment options

@zhangxin511 i managed to get it working. I declared a local variable of firebase.messaging() to use In place of calling the instance each time and it worked after that. I’m not sure how that would have affected it.

You must be logged in to vote
0 replies
Comment options

FYI we now support VAPID_KEY and SERVICE_WORKER DI tokens, the latter where you can inject a factory that looks up a service worker registration. I'm hoping to standardize ngsw compatibility after I do some more testing.

You must be logged in to vote
0 replies
Comment options

Very pleased with @zhangxin511 's solution as he published under Kabbage Engineering blog.

Using the new getToken API, things went perfectly smooth. Not forgetting to amend manifest.json..

Snippets of code:

import { FirebaseApp } from '@angular/fire';
....
 constructor(
 private swUpdate: SwUpdate,
 private swPush: SwPush,
 private firebase: FirebaseApp,
 ) { }
....
 const serviceWorkerRegistration = await navigator.serviceWorker.getRegistration();
 const messaging = this.firebase.messaging();
 console.log('MESSAGING SW', serviceWorkerRegistration);
 messaging.getToken({
 vapidKey:
 'BEYQVzBmAoN4......X0mpSy9sHSinAHyhBT534cwwrkr49A1qqNg-M',
 serviceWorkerRegistration // this should be the fix for angularfire not working with ngsw
 }
 ....
You must be logged in to vote
3 replies
Comment options

Very pleased with @zhangxin511 's solution as he published under Kabbage Engineering blog.

Using the new getToken API, things went perfectly smooth. Not forgetting to amend manifest.json..

Snippets of code:

import { FirebaseApp } from '@angular/fire';
....
 constructor(
 private swUpdate: SwUpdate,
 private swPush: SwPush,
 private firebase: FirebaseApp,
 ) { }
....
 const serviceWorkerRegistration = await navigator.serviceWorker.getRegistration();
 const messaging = this.firebase.messaging();
 console.log('MESSAGING SW', serviceWorkerRegistration);
 messaging.getToken({
 vapidKey:
 'BEYQVzBmAoN4......X0mpSy9sHSinAHyhBT534cwwrkr49A1qqNg-M',
 serviceWorkerRegistration // this should be the fix for angularfire not working with ngsw
 }
 ....

@Tommertom @zhangxin511
Did you experience problems when reloading the page?
I used the suggested solution and updated it to the newest Angular and Firebase versions and everything works fine now
Just one little edit had to be made:

navigator.serviceWorker.ready.then(swr => firebase.messaging(myFirebaseApp).useServiceWorker(swr))

but when I refresh/reload the page the console.log of the pushmessage isn't working anymore as it was before reloading...
is this related to the way we implement it in the constructor?

Comment options

Hi. Not sure. I have not gone through this use case yet. Sorry, not able to help now.

Comment options

Not sure if this helps, but besides using getToken, I ultimately got it working in a more stable way by renaming the angular service worker to firebase-sw.js in a post-build script (and adjusting app.module.ts). Somehow there are hidden dependencies in Angularfire/Firebase sdk that rely on a service worker with that name.. @Cironic

Comment options

Hello all, can anyone tell me why AngularFireMessaging is not compatible with the Angular Service Worker and what's the impact of having both working simultaneously?

You must be logged in to vote
14 replies
Comment options

Cool. here my full routine I have placed in a separate pwa.service.ts

constructor(
 private platform: Platform,
 private swPush: SwPush,
 private GA: AngularFireAnalytics,
 private firebaseApp: FirebaseApp,
 private afMessaging: AngularFireMessaging,
 private appRef: ApplicationRef,
 private updates: SwUpdate,
 ) {
if ('serviceWorker' in navigator && environment.production && !this.platform.is('ios')) { // && !this.platform.is('ios')
 window.addEventListener('load', () => {
 console.log('🚀 ~ serviceWorker ~ waiting to stabilise');
 appRef.isStable
 .pipe(untilDestroyed(this),
 first(isStable => isStable === true))
 .subscribe(async ref => {
 await navigator.serviceWorker.register('/ngsw-worker.js');
 console.log('🚀 ~ serviceWorker ~ registered');
 // PWA - check for updates
 const appIsStable$ = this.appRef.isStable.pipe(first(isStable => isStable === true));
 const everySixHours$ = interval(6 * 60 * 60 * 1000);
 const everySixHoursOnceAppIsStable$ = concat(appIsStable,ドル everySixHours$).pipe(untilDestroyed(this));
 everySixHoursOnceAppIsStable$.subscribe(() => this.updates.checkForUpdate());
 this.updates.available.pipe(untilDestroyed(this)).subscribe(event => {
 this.pwaUpdateAvailable$.next(true);
 console.log('Update available', event);
 });
 /* updates.unrecoverable.subscribe(event => { */
 if (environment.production && ('serviceWorker' in navigator) && !this.platform.is('ios')) {
 this.setupNotificationListeners();
 }
 setTimeout(() => {
 this.checkAppUpdate();
 }, 10 * 1000);
 });
 });
 }
 }
 setupNotificationListeners() {
 this.swPush.messages.pipe(untilDestroyed(this))
 .subscribe((notification: Notification) => {
 this.pushNotifications$.next([...this.pushNotifications$.getValue(), notification]);
 // console.log('push message', msg);
 });
 if ('permissions' in navigator) {
 navigator.permissions.query({ name: 'push', userVisibleOnly: true })
 .then((permissionStatus) => {
 this.pushNotificationsEnabled = (permissionStatus.state === 'granted');
 console.log('push permission state is ', permissionStatus.state, this.pushNotificationsEnabled);
 // this.notificationsEnabled$.next(this.pushNotificationsEnabled);
 });
 navigator.permissions.query({ name: 'notifications' })
 .then((permissionStatus) => {
 console.log('notifications permission state is ', permissionStatus.state);
 });
 }
 this.swPush.notificationClicks.subscribe(click => {
 this.clickNotifications$.next(click);
 console.log('notification click', click);
 });
 this.afMessaging.tokenChanges.pipe(untilDestroyed(this))
 .subscribe(token => {
 if (token) {
 console.log('TOKEN RECEIVED', token);
 this.messageToken$.next(token);
 }
 });
 }
 checkAppUpdate() {
 if ('serviceWorker' in navigator && environment.production) {
 this.updates.checkForUpdate();
 }
 }

I use tokenChanges instead of getToken - as I need to listen for changes anyway. As you can see I broadcast stuff in my own behaviorsubjects - maybe boilerplate, but also convenient to keep lots of PWA logic out of my components

Comment options

@Tommertom thanks for sharing it, it will definitely be helpful here and for others as well. Are you using the PWA only for notifications?
The reason for asking you are not checking for updates or adding the service worker when the platform is IOS, in my case in IOS most of the things working fine, just the notifications that is not.

Comment options

I am stil doubting whether I need a service worker for iOS. The offline capabilities arent that key as my intended userbase is predominantly android. Those who have iOS are likely not bothered with bandwith. And while we are updating the app frequently, I rather keep the app update experience as easy as possible. For Android PWA gives the "nartive" feel, which is cool. So it is very much related to the status of the app and the intended userbase. I don't see what a service worker otherwise can bring to the table on iOS.

Comment options

@Tommertom I tried to implement your code, but what library are you using to fetch the platform and the pwaUpdateAvailable$ where is it defined? can't figure it out and would be glad to get a little help.

Comment options

 private pwaUpdateAvailable$ = new BehaviorSubject<boolean>(false);
 private updates: SwUpdate,
 this.updates.available.pipe(untilDestroyed(this)).subscribe(event => {
 this.pwaUpdateAvailable$.next(true);
 console.log('Update available', event);
 });
import { Platform } from '@ionic/angular';
 private platform: Platform,

https://github.com/ionic-team/ionic-framework/blob/master/angular/src/providers/platform.ts
https://ionicframework.com/docs/angular/platform

But you may want to resort to other ways of checking on iOS if you are terrified by Ionic.

Google is your best friend.

Comment options

Hi all,

Are there any news regarding documentation for FCM with ngsw. I feel a bit lost as I see a lot of scattered information about what needs to be done to implement FCM, and was hoping someone could point me to a stackblitz or an article explaining all the steps in detail?

P.S. Sorry if this is too random, I am not a very experienced developer :/

You must be logged in to vote
5 replies
Comment options

I currently have working FCM implementations in both the sample and sample-compat directories in the project root. Documentation pending.

Comment options

Thanks a lot, much appreciated!

Comment options

I currently have working FCM implementations in both the sample and sample-compat directories in the project root. Documentation pending.

Thank you for this. From the code I can see the preferred way will be to have a wrapper service worker script that imports the angular one and firebase-sw.js as a way to go around the sw handling

For the rest there is a bunch of api changes due to af7 - creating some additional challenges 😀

Thanks for sharing the view

Comment options

@Tommertom Just use v7 compat for now, very few API changes. Though this pattern should work in AngularFire v6. The trick is to use AngularFire's SERVICE_WORKER DI Token and create a Firebase service worker that is loaded in addition to (not in place of ngsw) add it to your angular app's static assets.

Comment options

Thank you for this. There seems a interesting mix of things happening choosing an API for messaging in combination with having to include one or two service workers (angular's and something named firebase-messaging-sw.js):

  • Angular's SwPush handles messages and notifcation clicks (latter not included in AngularFire (?)), but does not provide API for getting tokens
  • AngularFire's API gives me the token API which is also available only using Angular's SW, provided it is renamed to firebase-messsaging-sw.js
  • If I want to use AngularFire's onMessage API, I need to include the firebase-messaging-sw.js as per sample-compat. But because I also need a SW for other things I need to include Angular''s SW as well - using a loader script (as per sample-compat).

For now I am going for option 2 - the simplest solution for me - giving me one service worker, and two API ways.

Comment options

Hey just wondering has there been any updates on this? Above, on Aug 30th 2021 jamesdanies says to just use v7 compat, is it ok to use the new non compat version now?

You must be logged in to vote
0 replies
Comment options

Hey, do we have any news on this?

You must be logged in to vote
0 replies
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

AltStyle によって変換されたページ (->オリジナル) /