Ionic 3 的 PWA 和 Firebase 云消息注册

Posted

技术标签:

【中文标题】Ionic 3 的 PWA 和 Firebase 云消息注册【英文标题】:Ionic 3's PWA & Firebase Cloud Messaging registration 【发布时间】:2018-07-18 01:46:18 【问题描述】:

我在这里关注这篇文章(不幸的是,这篇文章并不完整),试图了解如何将基于 Ionic 3 的 PWA 和 Firebase 云消息传递给朋友: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):

获取脚本时收到错误的 HTTP 响应代码 (404)。 :8100/firebase-messaging-sw.js 加载资源失败:net::ERR_INVALID_RESPONSE

如果我将 service-worker.js 与 firebase 相关的内容移动到 WWW 文件夹中的默认服务工作者中 - 我从 Firebase 收到一般错误(错误,未能注册服务工作者)。

问题: - 是否有关于 Ionic 3 的 PWA 和 FCM 的新指南? - 在高层次上,在 Ionic 3 和 Angular 中注册服务工作者有什么区别?我确实看过关于 Angular 的教程,但不知道如何在 Ionic 3 中做同样的事情。

【问题讨论】:

查看this group forum,有几个类似documentation 和this one 的引用。它适用于 androidios 等两个平台。 不幸的是,它仅适用于“本机”(cordova)平台。我正在尝试为基于离子的 PWA - 渐进式网络应用程序解决这个问题 好的,我进行了更深入的研究,并改变了我实例化 firebase 应用程序的方式,但问题仍然存在 - 尽管默认的 service-worker.js 具有所有功能,但 firebase 仍然给出错误的请求 404代码;( 也通读了这个:serviceworke.rs/push-simple_index_doc.html 但那里的上下文有些不同...... 【参考方案1】:

更新:以下内容自今天(2018 年 2 月 12 日)起有效,一旦 AngularFire2 支持消息传递模块,相关性很可能会降低。所以采取以下假设......

好的,我研究并最终让它在我的 Ionic 3 PWA 上运行,所以我在这里发布解决方案:

    先决条件: 我创建了 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,其代码如下:

    在 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),它在默认情况下包含以下内容:

    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 中取消注释:

    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>
    OK 几乎是最后一件事 - 您的 manifest.json(在 src 中)应该有准确的行: "gcm_sender_id": "103953800507"

这结束了客户端的初始内容。请注意,当用户在应用程序中时,我还没有实现任何处理通知的方法,现在认为它只是在您的标签不在焦点时从服务器发送消息时处理(这是我测试的)。

    现在您想要前往您的 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 结合。

我希望这对将来的一些学习者有所帮助。

【讨论】:

它位于 Ionic 最初放置它的默认位置。为什么要问? 我尝试了你的方法,在 chrome 中成功注册了 service worker,但在 SAFARI 和 FIREFOX 中,有时在 Chrome 中也出现错误“navigator.serviceWorker is undefined”。现在还没有调用 messing.getToken() 。不知道我错过了什么 你在 iOS 设备上测试 pwa 吗?如果是这样,请记住 iOS 浏览器(无论“品牌”如何)尚不支持服务人员。他们很快就会但还没有;/ 不,我正在测试 MAC 书。适用于所有浏览器。只有 chrome 工作但间歇性地工作 我明白了,仍然在那里检查服务人员支持,Apple 直到最近才支持它【参考方案2】:

我已成功实施该流程并获得 API 调用的成功响应。但是我的浏览器上没有弹出通知。 有什么想法吗?

api: https://fcm.googleapis.com/fcm/send

得到回复:

"multicast_id":6904414188195222649,"success":1,"failure":0,"canonical_ids":0,"results":["message_id":"0:1545375125056264%e609af1cf9fd7ecd"]

我的控制台的附加网址:

【讨论】:

以上是关于Ionic 3 的 PWA 和 Firebase 云消息注册的主要内容,如果未能解决你的问题,请参考以下文章

使用 mysql 数据库在 ionic 中托管 PWA

Ionic 4 + Angular 6 PWA 样式不起作用/损坏

PWA

[Firebase + PWA] Keynote: Progressive Web Apps on Firebase

Ionic Framework 和 Firebase 3.x 版本:此域未授权您的 Firebase 项目的 OAuth 操作

如何从 Firebase 获取 PWA 数据