Ionic 3 的 PWA 和Firebase 云消息注册 [英] Ionic 3's PWA & Firebase Cloud Messaging registration
问题描述
我在这里关注这篇文章(不幸的是,这篇文章并不完整),试图了解如何将基于 Ionic 3 的 PWA 和 Firebase 云消息传递给朋友:使用 FCM 推送通知
I was following this article here (which is not complete unfortunately) in attempt to learn how to friend Ionic 3 based PWA and Firebase Cloud Messaging: Push Notifications with FCM
我做了什么:
- 按照文章中的建议,将 FCM 库添加到 service-worker.js:
'use strict';
importScripts('./build/sw-toolbox.js');
importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-messaging');
firebase.initializeApp({
// get this from Firebase console, Cloud messaging section
'messagingSenderId': '47286327412'
});
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler((payload) => {
console.log('Received background message ', payload);
// here you can override some options describing what's in the message;
// however, the actual content will come from the service sending messages
const notificationOptions = {
icon: '/assets/img/appicon.png'
};
return self.registration.showNotification(notificationTitle, notificationOptions);
});
self.toolbox.options.cache = {
name: 'ionic-cache'
};
// pre-cache our key assets
self.toolbox.precache(
[
'./build/main.js',
'./build/vendor.js',
'./build/main.css',
'./build/polyfills.js',
'index.html',
'manifest.json'
]
);
// dynamically cache any other local assets
self.toolbox.router.any('/*', self.toolbox.cacheFirst);
// for any other requests go to the network, cache,
// and then only use that cached resource if your user goes offline
self.toolbox.router.default = self.toolbox.networkFirst;
- 然后在此处创建基于 Firebase 消息传递的提供程序:
import { Injectable } from "@angular/core";
import * as firebase from 'firebase';
import { Storage } from '@ionic/storage';
@Injectable()
export class FirebaseMessagingProvider {
private messaging: firebase.messaging.Messaging;
private unsubscribeOnTokenRefresh = () => {};
constructor(
private storage: Storage
) {
this.messaging = firebase.messaging();
}
public enableNotifications() {
console.log('Requesting permission...');
return this.messaging.requestPermission().then(() => {
console.log('Permission granted');
// token might change - we need to listen for changes to it and update it
this.setupOnTokenRefresh();
return this.updateToken();
});
}
public disableNotifications() {
this.unsubscribeOnTokenRefresh();
this.unsubscribeOnTokenRefresh = () => {};
return this.storage.set('fcmToken','').then();
}
private updateToken() {
return this.messaging.getToken().then((currentToken) => {
if (currentToken) {
// we've got the token from Firebase, now let's store it in the database
return this.storage.set('fcmToken', currentToken);
} else {
console.log('No Instance ID token available. Request permission to generate one.');
}
});
}
private setupOnTokenRefresh(): void {
this.unsubscribeOnTokenRefresh = this.messaging.onTokenRefresh(() => {
console.log("Token refreshed");
this.storage.set('fcmToken','').then(() => { this.updateToken(); });
});
}
}
现在在应用程序初始化期间,我调用 enableNotifications() 并收到错误消息,指出未找到默认服务工作者 (404):
And now during app initialization I call enableNotifications() and get error that says that default service worker is not found (404):
获取脚本时收到错误的 HTTP 响应代码 (404).:8100/firebase-messaging-sw.js 加载资源失败:net::ERR_INVALID_RESPONSE
A bad HTTP response code (404) was received when fetching the script. :8100/firebase-messaging-sw.js Failed to load resource: net::ERR_INVALID_RESPONSE
如果我将 service-worker.js 与 firebase 相关的内容移动到 WWW 文件夹中的默认服务工作者中 - 我从 Firebase 收到一般错误(错误,未能注册服务工作者).
If I move service-worker.js firebase related stuff into default service worker in WWW folder - I get general error from Firebase (Error, failed to register service worker).
问题:- 是否有关于 Ionic 3 的 PWA & 的新指南?流式细胞仪?- 在高层次上,在 Ionic 3 和 Angular 中注册服务工作者有什么区别?我确实看过关于 Angular 的教程,但不知道如何在 Ionic 3 中做同样的事情.
QUESTIONS: - is there a fresh guide on Ionic 3's PWA & FCM? - at high level what is the difference in registering service workers in Ionic 3 vs Angular? I did watch the tutorial about Angular but can't figure how to do the same in Ionic 3.
推荐答案
更新:以下内容自今天(2018 年 2 月 12 日)起有效,一旦 AngularFire2 支持消息传递模块,相关性可能会降低.所以采取以下假设......
UPDATE: the below is valid as of today (02/12/2018) and most likely will be less relevant once AngularFire2 supports messaging module. So take the below with that assumption...
好的,我研究并最终让它在我的 Ionic 3 PWA 上运行,所以我在这里发布解决方案:
OK I researched and finally made it work on my Ionic 3 PWA, so I am posting solution here:
- 先决条件:
- 我创建了 ionic 空白应用(只是一个主页)
- 使用 npm install 安装了 angularfire2 和 firebase ("angularfire2": "5.0.0-rc.4","firebase": "4.9.1"),我专门使用了 5.0.0-rc.4" 因为我有最新版本的稳定性问题;(
- 已创建配置(src 文件夹中的文件名 environment.ts):
export const firebaseConfig = {
apiKey: "Your Stuff Here from FB",
authDomain: "YOURAPPNAME.firebaseapp.com",
databaseURL: "https://YOURAPPNAME.firebaseio.com",
projectId: "YOURAPPNAME",
storageBucket: "YOURAPPNAME.appspot.com",
messagingSenderId: "FROMFIREBASECONEOLE"
};
- 我修改了 app.module.ts 以通过这种方式添加 firebase 和 angularfire2:
...
import { AngularFireModule } from 'angularfire2';
import 'firebase/messaging'; // only import firebase messaging or as needed;
import { firebaseConfig } from '../environment';
import { FirebaseMessagingProvider } from '../providers/firebase-messaging';
...
@NgModule({
declarations: [
MyApp,
HomePage
],
imports: [
BrowserModule,
IonicModule.forRoot(MyApp),
AngularFireModule.initializeApp(firebaseConfig),
IonicStorageModule.forRoot()
],
bootstrap: [IonicApp],
entryComponents: [
MyApp,
HomePage
],
providers: [
FirebaseMessagingProvider,
StatusBar,
SplashScreen,
{provide: ErrorHandler, useClass: IonicErrorHandler}
]
})
export class AppModule {}
这里我们还导入了我们的provider,其代码如下:
Here we also import our provider whose code is below:
- 在 providers 文件夹中,我创建了这样的 firebase-messaging.ts:
import { Injectable } from "@angular/core";
import { FirebaseApp } from 'angularfire2';
// I am importing simple ionic storage (local one), in prod this should be remote storage of some sort.
import { Storage } from '@ionic/storage';
@Injectable()
export class FirebaseMessagingProvider {
private messaging;
private unsubscribeOnTokenRefresh = () => {};
constructor(
private storage: Storage,
private app: FirebaseApp
) {
this.messaging = app.messaging();
navigator.serviceWorker.register('service-worker.js').then((registration) => {
this.messaging.useServiceWorker(registration);
//this.disableNotifications()
this.enableNotifications();
});
}
public enableNotifications() {
console.log('Requesting permission...');
return this.messaging.requestPermission().then(() => {
console.log('Permission granted');
// token might change - we need to listen for changes to it and update it
this.setupOnTokenRefresh();
return this.updateToken();
});
}
public disableNotifications() {
this.unsubscribeOnTokenRefresh();
this.unsubscribeOnTokenRefresh = () => {};
return this.storage.set('fcmToken','').then();
}
private updateToken() {
return this.messaging.getToken().then((currentToken) => {
if (currentToken) {
// we've got the token from Firebase, now let's store it in the database
console.log(currentToken)
return this.storage.set('fcmToken', currentToken);
} else {
console.log('No Instance ID token available. Request permission to generate one.');
}
});
}
private setupOnTokenRefresh(): void {
this.unsubscribeOnTokenRefresh = this.messaging.onTokenRefresh(() => {
console.log("Token refreshed");
this.storage.set('fcmToken','').then(() => { this.updateToken(); });
});
}
}
请注意,我初始化了 firebase 应用程序,然后在构造函数中我们注册了 ionic 的默认服务工作者 (service-worker.js),它在默认情况下包含以下内容:
Please note I init the firebase app and then in constructor we register ionic's default service worker (service-worker.js) that contains the following right after whatever is there by default:
- service-worker.js:
// firebase messaging part:
importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-messaging.js');
firebase.initializeApp({
// get this from Firebase console, Cloud messaging section
'messagingSenderId': 'YOURIDFROMYOURFIREBASECONSOLE'
});
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function(payload) {
console.log('Received background message ', payload);
// here you can override some options describing what's in the message;
// however, the actual content will come from the Webtask
const notificationOptions = {
icon: '/assets/images/logo-128.png'
};
return self.registration.showNotification(notificationTitle, notificationOptions);
});
此时,您还需要确保将您的应用启用为 PWA,Josh Morony 提供了一个很好的指南,今天 youtube 上有一个视频流涵盖了它.在 TLDR 中,您需要在 index.html 中取消注释:
At this point you also need to make sure you enabled your app as PWA, there is a good guide from Josh Morony and today there was a video stream on youtube that covers it. In TLDR you need to uncomment this in your index.html:
- src 中的 index.html 取消注释:
<!-- un-comment this code to enable service worker -->
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('service-worker.js')
.then(() => console.log('service worker installed'))
.catch(err => console.error('Error', err));
}
</script>
- 几乎是最后一件事——你的 manifest.json(在 src 中)应该有准确的行:"gcm_sender_id": "103953800507"
这结束了客户端的初始内容.请注意,当用户在应用程序中时,我还没有实现任何处理通知的方法,现在认为它只是在您的标签不在焦点时从服务器发送消息时处理(这是我测试的).
This concludes initial stuff on the client. Please note I didn't implement yet anything to handle notifications while user is in app itself, think for now it just handles when a message is sent from a server while your tab is not in focus (that is what I tested).
- 现在您想前往 Firebase 控制台并获取服务器密钥(点击设置齿轮图标,然后查看那里的云消息部分).复制服务器密钥.还运行客户端(离子服务并捕获您的本地令牌(我只是 console.logged 它).现在尝试使用 POST 方法向自己发送消息.(我用 Postman 做到了)
// method: "POST",
//url: "https://fcm.googleapis.com/fcm/send",
// get the key from Firebase console
headers: { Authorization: `key=${fcmServerKey}` },
json: {
"notification": {
"title": "Message title",
"body": "Message body",
"click_action": "URL to your app?"
},
// userData is where your client stored the FCM token for the given user
// it should be read from the database
"to": userData.fcmRegistrationKey
}
因此,通过执行所有这些操作,我能够在应用处于后台时可靠地向自己发送消息.我还没有处理前台,但是这个 SO 问题是关于如何初始化默认服务工作者并将其与 FCM 结合.
So by doing all this I was able to reliable send myself a message WHILE the app was in background. I am yet to handle foreground but this SO question is about how to init default service worker and marry it with FCM.
我希望这对将来的一些学习者有所帮助.
I hope this will help some learners in future.
这篇关于Ionic 3 的 PWA 和Firebase 云消息注册的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!