-
Notifications
You must be signed in to change notification settings - Fork 2.2k
-
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?
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 14 -
❤️ 3
Replies: 25 comments 23 replies
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 5
-
Hi @davideast,
Sorry to botter you, but do you know if the angular team has plans to support this.
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 4
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 6
-
🤯 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 ?
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 9
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
-
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();
});
}
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 4 -
😕 2 -
🚀 1
-
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.
});
}
Beta Was this translation helpful? Give feedback.
All reactions
-
@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?
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 2
-
@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:
Beta Was this translation helpful? Give feedback.
All reactions
-
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
Beta Was this translation helpful? Give feedback.
All reactions
-
This may be a dumb question, but can we use the method described here to implement FCM with the Angular Service Worker?
Beta Was this translation helpful? Give feedback.
All reactions
-
@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.
Beta Was this translation helpful? Give feedback.
All reactions
-
@zhangxin511 Just so I understand you...you're saying this method doesn't allow notifications when the app is closed or isn't active?
Beta Was this translation helpful? Give feedback.
All reactions
-
@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).
Beta Was this translation helpful? Give feedback.
All reactions
-
Ah, yes, I understand now. Thanks!
Beta Was this translation helpful? Give feedback.
All reactions
-
@SauceCode84 @wooldridgetm @matheo
I created a post about this if you are still interested. Along with the demo repo here
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 8 -
🎉 3
-
This issue in the Angular repo requested by the documentation appears to have been created here: angular/angular#34352
Beta Was this translation helpful? Give feedback.
All reactions
-
@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?
Beta Was this translation helpful? Give feedback.
All reactions
-
@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?
Beta Was this translation helpful? Give feedback.
All reactions
-
@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.
Beta Was this translation helpful? Give feedback.
All reactions
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 6 -
🚀 3
-
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
}
....
Beta Was this translation helpful? Give feedback.
All reactions
-
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?
Beta Was this translation helpful? Give feedback.
All reactions
-
Hi. Not sure. I have not gone through this use case yet. Sorry, not able to help now.
Beta Was this translation helpful? Give feedback.
All reactions
-
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
Beta Was this translation helpful? Give feedback.
All reactions
-
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?
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 3
-
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
Beta Was this translation helpful? Give feedback.
All reactions
-
@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.
Beta Was this translation helpful? Give feedback.
All reactions
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
-
@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.
Beta Was this translation helpful? Give feedback.
All reactions
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
-
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 :/
Beta Was this translation helpful? Give feedback.
All reactions
-
I currently have working FCM implementations in both the sample
and sample-compat
directories in the project root. Documentation pending.
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 2
-
Thanks a lot, much appreciated!
Beta Was this translation helpful? Give feedback.
All reactions
-
I currently have working FCM implementations in both the
sample
andsample-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
Beta Was this translation helpful? Give feedback.
All reactions
-
@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.
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 1
-
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.
Beta Was this translation helpful? Give feedback.
All reactions
-
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?
Beta Was this translation helpful? Give feedback.
All reactions
-
😕 1
-
Hey, do we have any news on this?
Beta Was this translation helpful? Give feedback.
All reactions
-
👀 1