Ionic 3的PWA& Firebase云消息传递注册 [英] Ionic 3's PWA & Firebase Cloud Messaging registration

查看:194
本文介绍了Ionic 3的PWA& Firebase云消息传递注册的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在这里关注这篇文章(遗憾的是不完整),试图学习如何与基于Ionic 3的PWA和Firebase云消息传递进行交流:使用FCM推送通知



我做了什么:


  1. 根据文章中的建议将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({//得到此信息来自Firebase控制台,云消息传递部分'messagingSenderId':'47286327412'}); const messaging = firebase。 messaging(); messaging.setBackgroundMessageHandler((payload)=> {console.log('收到的背景信息',有效载荷); //这里你可以覆盖一些描述消息内容的选项; //但是,实际内容将来自发送消息的服务const notificationOptions = {icon:'/ assets / img / appicon.png'}; return self.registration.showNotification(notificationTitle,notificationOptions);}); self.toolbox.options.cache = {name:'ionic-cache'}; //预缓存我们的密钥assetsself.toolbox.precache(['./ build / main.js','。/ build / vendor.js','。/ build / main.css','。/ build / polyfills.js','index.html','manifest.json']) ; //动态缓存任何其他本地assetsself.toolbox.router.any('/ *',self.toolbox.cacheFirst); //对于任何其他请求转到网络,缓存,//然后只使用该缓存资源如果你的用户离开了offlineself.toolbox.router.default = self.toolbox.networkFirst;  


  1. 然后在此创建基于Firebase Messaging的提供商:

 从{@ /导入{Injectable} core; import * as firebase from'firebase'; import {Storage} from'@ ionic / storage'; @Injectable()export class FirebaseMessagingProvider {private messaging:firebase.messaging.Messaging; private unsubscribeOnTokenRefresh =()=> {};构造函数(私有存储:存储){this.messaging = firebase.messaging(); public enableNotifications(){console.log('Requesting permission ...');返回this.messaging.requestPermission()。then(()=> {console.log('Permission granted'); //令牌可能会改变 - 我们需要监听对它的更改并将其更新为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){//我们从Firebase获得了令牌,现在让它将它存储在数据库中返回。 storage.set('fcmToken',currentToken);} else {console.log('没有可用的实例ID令牌。请求生成一个的权限。');}}); }私人setupOnTokenRefresh():无效{this.unsubscribeOnTokenRefresh = this.messaging.onTokenRefresh(()=> {的console.log( 令牌刷新); this.storage.set( 'fcmToken', ''),然后(。 ()=> {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中做同样的事情。

解决方案

更新:下面截至今天(02/12/2018)是有效的,一旦AngularFire2支持消息模块,很可能不太相关。所以请按照以下假设...



好的我研究过并最终使其在我的Ionic 3 PWA上工作,所以我在这里发布解决方案:


  1. 先决条件:


    • 我创建了离子空白应用程序(只是一个主页)

    • 使用npm install安装了angularfire2和firebase(angularfire2:5.0.0-rc.4,firebase:4.9.1),我特意使用5.0.0- rc.4因为我遇到了最新版本的稳定性问题;(

    • 创建了config(src文件夹中的文件名environment.ts):


  export const firebaseConfig = {apiKey:你的东西来自FB,authDomain:YOURAPPNAME.firebaseapp.com,databaseURL:https://YOURAPPNAME.firebaseio.com,projectId:YOURAPPNAME,storageBucket:YOURAPPNAME.appspot .com,messagingSenderId:FROMFIREBASECONEOLE};  


  1. 我修改app.module.ts以这种方式添加firebase和angularfire2:

  ...从{angularfire2'导入{AngularFireModule};导入'firebase / messaging'; //仅导入firebase消息或根据需要;从'../environment'导入{firebaseConfig};从'../ providers / firebase-messaging'导入{FirebaseMessagingProvider}; ... @ NgModule({声明:[MyApp,主页],进口:[BrowserModule,IonicModule.forRoot(MyApp的),AngularFireModule.initializeApp(firebaseConfig),IonicStorageModule.forRoot()],自举:[IonicApp],entryComponents:MyApp的,主页],供应商:[FirebaseMessagingProvider,状态栏, SplashScreen,{提供:ErrorHandler,useClass:IonicErrorHandler}]})导出类AppModule {}  



这里我们还导入我们的提供商,其代码如下:


  1. 在providers文件夹中我创建了firebase -messaging.ts是这样的:

  import {Injectable};从'angularfire2'导入{FirebaseApp}; //我正在导入简单的离子存储(本地一个),这应该是prod来自'@ ionic / storage'的某种sort.import {Storage}的远程存储; @Injectable()导出类FirebaseMessagingProvider {private messaging; private unsubscribeOnTokenRefresh =()=> {};构造函数(私有存储:存储,私有应用程序: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 ...');返回this.messaging.requestPermission()。then(()=> {console.log('Permission granted'); //令牌可能会改变 - 我们需要监听对它的更改并将其更新为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){//我们从Firebase获得了令牌,现在让它将它存储在数据库console.log中(currentToken)返回this.storage.set('fcmToken',currentToken);} else {console.log('没有可用的实例ID令牌。请求生成一个的权限。');}}); }私人setupOnTokenRefresh():无效{this.unsubscribeOnTokenRefresh = this.messaging.onTokenRefresh(()=> {的console.log( 令牌刷新); this.storage.set( 'fcmToken', ''),然后(。 ()=> {this.updateToken();});}); }  



请注意我初始化firebase应用程序然后在构造函数中我们注册离子的默认服务工作者(service-worker.js),默认情况下包含以下内容:


  1. service-worker.js:

  // firebase消息传递部分:importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-app.js'); importScripts('https://www.gstatic.com/firebasejs/4.9 0.0 /火力-messaging.js'); firebase.initializeApp({//从火力地堡控制台,云消息传递部分得到这个 'messagingSenderId': 'YOURIDFROMYOURFIREBASECONSOLE'}); const的消息= firebase.messaging(); messaging.setBackgroundMessageHandler( function(payload){console.log('Received background message' ,payload); //在这里你可以覆盖一些描述消息内容的选项; //但是,实际内容将来自Webtask const notificationOptions = {icon:'/ assets / images / logo-128.png'}; return self.registration.showNotification(notificationTitle,notificationOptions);});  



此时你还需要确保你的应用程序已启用PWA,Josh Morony有一个很好的指南,今天有一个视频流在youtube上覆盖它。在TLDR中,您需要在index.html中取消注释:


  1. index.html in src uncomment:

 <! - 取消评论此代码启用服务工作者 - > <脚本> if(导航器中的'serviceWorker'){navigator.serviceWorker.register('service-worker.js')。then(()=> console.log('service worker installed'))。catch(err => console .error('错误',错误)); }< / script>  


  1. 好的几乎是最后一件事 - 你的manifest.json(在src中)应该有一行:
    gcm_sender_id:103953800507

这结束了客户端的初始内容。请注意,当用户在应用程序本身时,我没有实现处理通知的任何内容,现在想想它只是在您的选项卡不在焦点时从服务器发送消息时处理(这是我测试的)。


  1. 现在你要去你的firebase控制台获取服务器密钥(点击设置齿轮图标,然后看那里的云消息部分)。复制服务器密钥。还运行客户端(离子服务并捕获你的本地令牌(我只是控制台。记录它)。现在尝试使用POST方法向自己发送消息。(我用Postman做过)

  //方法:POST,// url:https:// fcm .googleapis.com / fcm / send,//从Firebase控制台标题获取密钥:{授权:`key = $ {fcmServerKey}`},json:{notification:{title:消息标题, body:消息正文,click_action:您应用的URL?},// userData是您的客户端为给定用户存储FCM令牌的位置//应该从数据库to读取: userData.fcmRegistrationKey}  



所以通过这一切,我能够可靠的时候给自己发一条消息e app在后台。我还没有处理前景,但是这个问题是关于如何初始化默认服务工作者并将其与FCM结合。



我希望这将有助于未来的学习者。 / p>

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

What I did:

  1. as advised in the article added FCM libraries into 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;

  1. Then created Firebase Messaging based provider here:

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(); });
    });
  }
    
}

And now during app initialization I call enableNotifications() and get error that says that default service worker is not found (404):

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

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).

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.

解决方案

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...

OK I researched and finally made it work on my Ionic 3 PWA, so I am posting solution here:

  1. Prerequisites:
    • I created ionic blank app (just a home page)
    • installed angularfire2 and firebase ("angularfire2": "5.0.0-rc.4","firebase": "4.9.1") using npm install, I used specifically 5.0.0-rc.4" cause I had stability issues with latest one;(
    • created config (filename environment.ts in src folder):

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"
};

  1. I modified app.module.ts to add firebase and angularfire2 this way:

...
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 {}

Here we also import our provider whose code is below:

  1. in providers folder I created firebase-messaging.ts like this:

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(); });
    });
  }
    
}

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:

  1. 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);
});

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:

  1. index.html in src uncomment:

 <!-- 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>

  1. OK almost the last thing - your manifest.json (in src) should have exact line: "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).

  1. Now you want to go to your firebase console and obtain server key (click setting gear icon, then see cloud messaging section there). Copy server key. Also run the client (ionic serve and capture your local token (i just console.logged it). Now try sending yourself the message using a POST method. ( I did it with 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 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&amp; Firebase云消息传递注册的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆