そーす

福岡在住のプログラマ

Next.js+FirebaseHostingで構築するサーバレスWebアプリケーション

github.com

Next.jsというReactアプリケーションをデフォルトでServerSideRenderingしてくれるライブラリがあります。

これをFirebaseHosting上にFirebaseFunctionsを使って構築することで、無料でサーバレスSPAを作る事ができます。

Next.jsをclone

Next.jsはサンプルがとても豊富です。

github.com

今回使うサンプルはwith-firebase-hostingです。

github.com

cloneしたらwith-firebase-hostingディレクトリをコピーして使いましょう。

Hello World

まずは依存ライブラリをインストールします。

npm i

そして

npm run next

でローカルサーバが立ち上がります。

デフォルトではlocalhost:3000ですね

f:id:saburesan:20171114103843p:plain

これは単にnextjsを起動しているだけで、Firebaseは全く関係ありません。

デプロイ

事前にFirebaseでプロジェクトを作っておいてください。

プロジェクトを作ったら.firebasercの<project-name-here>をプロジェクト名に置き換えます

// .firebaserc
{
  "projects": {
    "default": "<project-name-here>"
  }
}

これでFirebaseの設定は完了です。

npm run deploy

これで設定したプロジェクトにデプロイされます。

(npm run serveは恐らく動かないと思います。)

デプロイが完了するとアップデート先のURLがログに出るのでアクセスしてみると、

先程ローカルで動かした内容が表示されてるかと思います。

もし404や500が出た場合はFirebase consoleのFunctionsでログを見ることができるので確認して見てください。

f:id:saburesan:20171114110616p:plain

プロジェクトの構造

app

実際にコードを書いていくディレクトリです。

nextjsのルートディレクトリです。

普段使うnextjsと違うのはnext.config.jsファイルでコンパイルの保存先がfunctions/nextになっているところくらいでしょうか。

functions

デフォルトではこのディレクトリがFirebaseにデプロイされます。

appのコンパイル先でもあります。

public

これはFirebaseHostingのルートとなるディレクトリです。

placdholder.htmlというのが入ってますが、基本的に触ることはないです。

FirebaseHostingの設定でpublicは必須です。

存在していなければデプロイに失敗します。

Deployment Configuration  |  Firebase

firebase.json

Firebaseの細かい設定を書きます。

{
  "hosting": {
    "public": "public",
    "rewrites": [
      {
        "source": "**/**",
        "function": "next"
      }
    ]
  },
  "functions": {
    "source": "functions"
  }
}

デフォルトでは上のようになっています。

hostingの設定ですが、publicは必須でrewritewですべてのアクセスに対してFirebaseFunctionsのnext関数を起動するようになっています。

functionsの設定でFirebaseFunctionsのソースを指定しています。functions/index.jsの内容は

const functions = require('firebase-functions')
const next = require('next')

var dev = process.env.NODE_ENV !== 'production'
var app = next({ dev, conf: { distDir: 'next' } })
var handle = app.getRequestHandler()

exports.next = functions.https.onRequest((req, res) => {
  console.log('File: ' + req.originalUrl) // log the page.js file that is being requested
  return app.prepare().then(() => handle(req, res))
})

となっていて、exports.nextでnextをエクスポートしています。

writesの"function":"next"はこのnextを呼んでいます。

面倒なところ

デフォルトではfunctionsがデプロイされますが、functionsに含まれるappのコンパイルの成果物distはpagesに紐づくファイルだけなので、

例えばapp/package.jsonのdependenciesはfunctions/package.jsonに書き足して上げないといけません。

画像を扱う場合はapp/staticというフォルダに入れて使うのがnextjsでは一般的なのですが、このstaticもコピーしなければなりません。

他にも、起動サーバーをカスタマイズする場合などもコピーして…

という感じになってしまいます。

appにまとめる

一応、今私はfunctionsの内容をappに移して運用しています。

そうなるとデプロイに不要なファイルが多く含まれるので、firebase.jsonのignoreで指定しています。

app/index.jsはfunctionsのindex.jsをそのまま使い、サーバーをカスタマイズする場合は

exports.start = () => {
  app.prepare().then(() => {
    createServer((req, res) => {
      ...
    }).listen(port, (err) => {
      if (err) throw err;
      console.log(`> Ready on http://localhost:${port}`);
    });
  });
};

という感じにしました。

package.json

"scritps" : {
  "dev": "node -e \"require('./').start()\"",
  ...
}

"dependencies": {
  "firebase-admin": "^5.5.0",    // 追加
  "firebase-functions": "^0.7.3", // 追加
  ...
}

firebase.json

{
  "hosting": {
    "public": "public",
    "rewrites": [
      {
        "source": "**/**",
        "function": "next"
      }
    ],
    "ignore": [
      "**/.*",
      "**/node_modules/**",
      "app/src",
      "app/flow-typed",
      "app/pages"
    ]
  },
  "functions": {
    "source": "app"
  }
}

package.json追記したりstaticコピーしてくるよりこっちのほうがいいなーと思いました。

よくわからない問題

firebase serveが上手く動かない

404だったり、変更が全然反映されなかったり…

謎です。

ローカルでは問題無く動くが、デプロイすると動かない

デプロイするとstyled-jsxとbabel-runtimeが無いというエラーが出ます。

ローカルではnode_modulesに入っているのですが…

追加でインストールすると動くようになりました。

まとめ

ハマリポイントありますが、無料でWebアプリ作れるのは良いですね〜

ちなみに、github pagesだった自分のポートフォリオをNext.js+FirebaseHostingに移行しました。

ryo-hlan.firebaseapp.com

サーバレスなんで、アクセスの感覚が空くとやっぱり遅いけどインスタンスが起動しているときなら速度は問題無さそう。

f:id:saburesan:20171114164328p:plain

まぁコンテンツ少ないんでね…

そのうちブログも移行します。

AndroidにHostsを書き換えずにPCのHostsを反映させる

PC側のHostsを変更

変更してください。

ローカルPCにプロキシサーバを立てる

なんでも良いです。

node作りたい、と思っていましたが先駆者がいらっしゃったのでありがたく使わせていただきました。   qiita.com

ペッと貼って起動

% node proxy-server.js 
http proxy server started on port 8080

デフォルトPortは8080です。

Androidにプロキシサーバの設定をする

AndroidWifi設定からProxyサーバーをlocalhost:8080に設定します。

f:id:saburesan:20171102093653p:plain

Portforwardingする

Android端末を開発者モードにする必要があります。

GoogleChromehttp://chrome://inspect#devicesにアクセスします。

Port forwardingからAndroidlocalhost:8080をPCの8080に接続するように設定します。

f:id:saburesan:20171102094026p:plain

完了

PCのHostsの設定がAndroid側にも反映されてると思います。

React Native Debuggerがすごすぎて笑ってしまった

github.com

何も言わずに入れて欲しい。

react-native-debuggerはReact Native公式のChrome Debuggerを元に作られたデバッグツールです。

Reduxを利用している場合に力を発揮します。

というかRedux使ってないと旨味無いかもです…

でもRedux入れてるなら絶対使って欲しい。

とりあえずスクリーンショット載せます。

f:id:saburesan:20171027193500p:plain

まだ試した機能は多くないでですが、使った感じで利点をならべるとすると

  • Action, Stateの可視化が素晴らしい
  • 時系列順にAction、Stateのログが取れる
  • 任意のタイミングのActionによるStateのDiffが取れる
  • 任意のタイミングのActionまで状態を戻すことができる
  • Debugger上でActionを発行できる

こんな感じです。

時系列順にAction、Stateのログが取れる。StateのDiffが取れる

f:id:saburesan:20171028104354p:plain

図の「Actionのログ」が発行されたActionを時系列に並べたものです。

「ActionやStateの状態」は「Actionのログ」から選択したActionに関する詳細が見れます。

例えば、図だとSETUP_PAGE_STATE#SET_SETUP_FETCH_STATEを選択して、

右側のパネルのタブでActionを選択すると発行されたActionの詳細を見ることが出来ます。

Stateタブではその左Actionが発行された後のStateの状態が、

DiffタブではActionによって起こったStateのDiffを表示してくれます。

また、それぞれの表示形式は「Tree」「Chart」「Raw」の3パターンから選べ、

デフォルトはTree(図の状態)です。

これをChartに変えると…

f:id:saburesan:20171028104817p:plain

このような表示になります。

Rawはただのテキスト表示です。

任意のタイミングのActionまで状態を戻すことができる。

Actionのログは詳細を見るだけでなく、「Jump」「Skip」という動作が可能です。

Jump

https://gyazo.com/d94ec482b603027187b5632ecc260a00

JumpはStateの状態をそのActionが発行されたタイミングまで戻すことが出来ます。 また、そのタイミングから時系列順にActionを再生することも出来ます。

Skip

Skipは任意のActionの発行を無効にします。

あるアクションがバグを引き起こしているかどうかの検証などに使えそうです。

Debugger上でActionを発行できる

https://gyazo.com/4584122879b6ebf6ad53a478ade7b06d

Debugger上でActionを発行する機能があります。

Debugger上で発行したActionもActionのログに記録されJump, Skipなどの操作が可能です。

ちなみに、ActionはSkipしてからSweepボタンを押すと完全にログから削除できます。

まだまだな所

全然使い物にならねーってほどではないのですが、偶に起きるエラーです。

不安定

ちょくちょく接続エラーになります。その時はアプリ側の再起動が必要です。

Navigation系でページを戻ったりはできない

react-navigationを使っているのですが、Jumpで前のページの状態まで持っていくとエラーになります。

List系の'same key'エラーたまになる

Viewの更新が偶にうまくいかないことがあります。

まとめ

まだまだ試せていない機能が多いのですが、少し使っただけでもデバッグが非常に楽になりました。

f:id:saburesan:20171028121918p:plain

デバッガ上でテストする機能があり、Jest, Mocha, Tape, Avaのテンプレートから選べるというすごく有用そうな機能もあるので試してみたいと思います。

VisualStudioCodeに入れておくと便利なプラグイン3選

ただ自分が使っててコレないと不便ってやつです。

advanced-new-file

marketplace.visualstudio.com

ファイルの新規作成にパスを先に指定して作成できます。

デフォルトだと先にファイル作成して保存時にGUIで指定なのですごく面倒なので。

⌘+nに割り当てると更に便利。

Relative Path

marketplace.visualstudio.com

開いているファイルからあるファイルへの相対パスを出してくれます。

JSでimportする時とかコレないと不便。

ただ、すごく重い時がある。

Active File in StatusBar

marketplace.visualstudio.com

何でデフォルトで開いているファイルのパス出してくれないんだろ…

React NativeでマテリアルデザインのRippleエフェクトを実装する

Inputフォームに続き、マテリアルデザインのRippleエフェクトをReact NativeのJSのみで実装してみました。

コード

transformの方で最初実装してたのですが、タブを連続でタップすると表示が上手く行かないことがあったのでこちらで実装しました。

iOS

https://gyazo.com/82f5641f4ea34f1d7a0821f814b2aa9f

Android

https://i.gyazo.com/9e209d9fa1c1e98f73ad18b46aabdf0f.gif

BitriseCIでAndroidSDKのライセンスエラー

install-missing-android-toolsを使っていると、SDKがアップデートされた時に

* What went wrong:
A problem occurred configuring project ':app'.
> You have not accepted the license agreements of the following SDK components:
  [Android SDK Platform 26].
  Before building your project, you need to accept the license agreements and complete the installation of the missing components using the Android Studio SDK Manager.
  Alternatively, to learn how to transfer the license agreements from one workstation to another, go to http://d.android.com/r/studio-ui/export-licenses.html
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
BUILD FAILED

というエラーでCIが失敗する事があります。

原因はこれです。

qiita.com

これをBitrise上でやります。

android-sdk-licenseをアップロード

自身のPCのの$ANDROID_HOME/licenses/android-sdk-licenseをアップロードします。

BitriseのWorkflow EditorCode signingタブのGENERATE FILE STORAGEでアップロードします。

Workflowでダウンロードする

install-missing-android-toolのStepの前にFile DownloaderStepを追加します。

そしてDownload source urlに先程アップロードしたandroid-sdk-licenseのダウンロードURLを、

Download destination pathには

$ANDROID_HOME/licenses/android-sdk-license

を指定します。

f:id:saburesan:20171011130303p:plain

これで問題なくCIが通ると思います。

React Nativeでマテリアルデザインのテキスト入力フォームを作った

gist.github.com

https://gyazo.com/7bf7d4237f42dd5630fd1af83e062c6d

AndroidiOSでInputTextのスタイルを合わせるのが面倒だなと思ってました。

iOSもマテリアルデザインに寄せてしまうのは最近だとアリだと思っているのでちょっと試しに作りました。

ざっと30分くらいで作ったので本物の機能は足りてないです。

ReactNativeは簡単にUIが作れるのであまりUIライブラリを入れずに自作したほうが良いですね。

今年はDroidkaigiのアプリをReact Nativeで作るんだ…

React Nativeで起動時にJS側にデータを渡す

facebook.github.io

iOSの方はちゃんとドキュメントがあるのに何でAndroidは無いんですかね…

といってもやり方は簡単です。

MainActivityでcreateReactActivityDelegateをオーバーライドして

ReactActivityDelegateのgetLaunchOptionsをオーバーライドするだけです。

public class MainActivity extends ReactActivity {

    @Override
    protected ReactActivityDelegate createReactActivityDelegate() {
        return new ReactActivityDelegate(this, getMainComponentName()) {
            @Nullable
            @Override
            protected Bundle getLaunchOptions() {
                Bundle initialProps = new Bundle();
                initialProps.putString("buildType", BuildConfig.BUILD_TYPE);
                return initialProps;
            }
        };
    }
}

熱盛りSlackBot

f:id:saburesan:20170913175357p:plain

Bitriseのビルド待っている間に熱盛り絵文字を投稿すると返信してくれるようにした。

笑ってもらえたので良かった(会社のSlack)

React Nativeのアニメーションで簡単なチュートリアル画面を作った

React Nativeで個人開発アプリを開発中なのですが、ちょうどアニメーション周りを結構調べていたので試しに簡単なチュートリアルを作ってみました。

f:id:saburesan:20170906143339g:plain

Component

ほとんどAnimatedを使いました。コンポーネントもデフォルトのAnimated.View, Text, Imageのみです。

一部LayoutAnimationを使っていますが、最初にLayoutAnimationを使っていた名残で変更していないだけです。

LayoutAnimationは導入も使い方も非常に楽なので気に入っていたのですが、複雑なアニメーションをしようとするとかなり面倒くさく、あまり自由度が無いところから使わなくなりました。

ARTを使ってSVGでやることも考えたのですが、Animatedでも十分パフォーマンスが出る結果だったので今回は採用していません。

saburesan.hatenablog.com

Easing

Easingもデフォルトのtimingとspringしか使っていません。

Sequence, Stagger, Parallel

アニメーションを連続で再生するためのAPIがありますが、今回はsequence, stagger, parallelを使いました。

facebook.github.io

ドキュメントを読んだだけだとStaggerの使い所があまりわからなかったのですが、

今回検索文字の入力の部分や、3回タップのアニメーションの所などで使っています。

Interpolate

今回、すべてのアニメーションはAnimated.Valueを使っています。

また、基本的にValueは0->1の変化のみとして、Animated.Valueの値をinterpolateメソッドで適宜補間する形にしました。

理由はValueの値をスタイルで直接使う値にしてしまうと同じタイミングで始まるアニメーションがあったときにAnimated.Valueを使いまわせないからです。

同じにタイミングでもEasingやDurationが違う場合などはParallelを使用していますが、

EasingもDurationも同じでいい場合が結構多くinterpolateで補間するほうがわかりやすくなりました。

Valueを0->1にしてinterpolateで補間することでアニメーション自体の定義とスタイルの定義を完全に別にして考える事ができるのでお薦めです。

ハマったこと

Animated.XXXにして無い時にエラーで上手く教えてくれない

これは私が初期にLayoutAnimationで組んで居た所をAnimatedに変更したときにコンポーネントの定義でAnimated.XXXにし忘れていたのですが、エラーが非常にわかりにくく手間取りました。

Devモードだとアニメーションが重すぎる

めっちゃ重たいです。デバッグ無理です。

Androidであれば開発モードのDev SettingsJS Dev Modeをオフにしてデバッグしましょう。

iOSAndroidと同じ事が出来ないのでReleaseビルドでデバッグしましょう。

position: ‘absolute'で指定しているAnimated.Viewを消すとそれ以降のアニメーションが重くなる

これはホントの所の原因がわかっていないのですが、フルスクリーン表示したViewをアニメーションが終わった後に描画しない処理(renderXXXがnullを返す)をすると、それ以降のアニメーションがやたら重くなる現象が起きました。

leftの値をいじって画面外に出しても重くなってしまい、完全な原因が謎です。

今回は一番最後にボタンをタップできるようにする必要があっただけのでフルスクリーンのViewにpointerEvents=“none”を設定することで解決できましたが、ちょっと謎ですね…

まとめ

今回やった範囲だとハマりどころが多いという感じはしませんでした。

JS側UIスレッド側のFPSも57~60位で特にパフォーマンスが悪くなるような事も特に無く、アニメーションは実際ネイティブで書くと面倒ですがAndroid/iOSどっちも同じように書けるのはかなりのメリットだなぁと思いました。

AnimatedのAPIはまだ使ってないものもありますし、ARTとAnimatedを組み合わせると更に自由度が上がると思いますので色々遊んでみたいと思います。

React Native ARTで遊ぶ

React Nativeでグラフを描くにはどうしたらいいのかと調べましたら、

React Native内にReact Native ARTというライブラリが在るのを知りました。

ドキュメントにも書いてないですが、開発はされているようです。

react-native/ReactNativeART.js at 0.48-stable · facebook/react-native · GitHub

Install

初期でiOSはライブラリの設定がされていないのでそれを行う必要があります。(RN47.0 現在)

Xcodeでプロジェクトを開き、

  1. Build Phases -> Link Binary with Libraries -> + (Add items) -> Add Other -> /node_modules/react-native/Libraries/ART/ART.xcodeproj

  2. Build Phases -> Link Binary with Libraries -> libART.a

で追加します。

これで完了です。Androidは最初から動きます

直線を描く

import React, { Component } from 'react';
import {
  AppRegistry,
  ART,
  Dimensions
} from 'react-native';

const {
  ClippingRectangle,
  Group,
  Surface,
  LinearGradient,
  Path,
  RadialGradient,
  Shape,
  Transform,
  Text
} = ART;

export default class ArtSample extends Component {
  render() {
    const { width, height } = Dimensions.get('window');
    const path = Path();
    path.moveTo(width / 2, height / 2);
    path.line(0, -100);

    return (
      <Surface width={width} height={height} >
        <Shape d={path} stroke="#AAA" strokeWidth="4" />
        <Shape d={`m${width / 2},${height / 2} v100`} stroke="#333333" strokeWidth="8" />
        <Text x={width / 2 + 30} y={height / 2} stroke="#444" strokeWidth="1">ポッキーだよ</Text>
      </Surface>
    );
  }
}

f:id:saburesan:20170831144354p:plain

ポッキーを描きました。

svgに慣れてるのであればあまり難しくなさそうですね。(pathじゃなくてShapeだけど)

ベジエ

2次ベジエ、3次ベジエ。

return (
      <Surface width={width} height={height} >
        <Shape d={path} stroke="#AAA" strokeWidth="4" />
        <Shape d={`m${width / 2},${height / 2} v100`} stroke="#333333" strokeWidth="8" />
        <Text x={width / 2 + 30} y={height / 2} stroke="#444" strokeWidth="1" font="bold 20px Futura">ポッキーだよ</Text>
        <Shape d={`m0,0 q0,${height},${width},${height} `} stroke="#777" strokeWidth="2" />
        <Shape d={`m0,0 c0,${height},${width},${height},${width},0`} stroke="#777" strokeWidth="2" />
      </Surface>
    );

f:id:saburesan:20170831151543p:plain

LayoutAnimation

LayoutAnimationも効きますね(意味のないアニメーション)

f:id:saburesan:20170831153914g:plain

ServiceWorkerのデバッグ時にはキャッシュを

ServiceWorker勉強中です。

saburesan.hatenablog.com

これやった時に通知の表記のデバッグ時に何変えても表記が上手くいかなくて悩んだんですが、

結局ServiceWorkerのキャッシュのせいでファイル更新しても上手く更新されなかったようです。

Chromeデバッグする際には

http://chrome://serviceworker-internals/

から対象のServiceWorkerをunregisterすると最新のファイルが読み込まれました。

Firebase Cloud Messagingを使ってWebサイトでPush通知を受け取る

仕事でReact SPAにPush通知を実装することになったので備忘録。

Firebase Cloud Messaging(以下FCM)をWebにやるにはHttpsに対応のサイトじゃないとだめなので、Firebase Hostingで構築するところまでを記載します。

Firebaseの説明はありません。

Firebase Hostingの準備

先にFirebase Hostingを使ってサイトを作る準備をします。

Firebaseのプロジェクトを作成する

Firebaseのアカウントを持ってないならば作りましょう。

firebase.google.com

プロジェクトを作ります。

f:id:saburesan:20170817224342p:plain

Command Line Toolsのインストール

github.com

Firebaseをコマンドで扱うためのツールです。Firebase Hostingを使う時に使用します。

npm i -g firebase-tools

インストールが完了したらログインします。

firebase login

ブラウザが開きますので連携を許可しましょう。

Firebase Hostingのプロジェクトの初期化

適当に今回プロジェクトを作成するディレクトリを作成します。

そこで以下のコマンドで初期化します

firebase init

Firebaseのどのサービスの初期化か聞かれますので一番したのHostingを選びましょう

f:id:saburesan:20170818134047p:plain

すると、プロジェクトの選択があるので、最初に作ったプロジェクトを選択します。

初期化は以上です。

下記の2つのファイルが作成されます。

.firebaserc
firebase.json

firebase.jsonは空のJSONオブジェクトしか入ってないと思います。

リファレンスでは

アプリを初期化する際、公開ルートとして使用するディレクトリについて確認を求められます(デフォルトは「public」)。その時点で公開ルート ディレクトリに有効な index.html ファイルがない場合、自動的にファイルが作成されます。

とありますが、私の環境(バージョン?)では聞かれなかったので手動で公開ルートを設定します。

とりあえずpublicにしましょう。

{
  "hosting": {
    "public": "public"
  }
}

あとはpublicフォルダを作ってindex.htmlを仕込んでデプロイします。

f:id:saburesan:20170818135253p:plain

ちなみに、デプロイ前の確認としてローカルサーバを建てる機能もあります。

firebase serve
firebase deploy
=== Deploying to '#####'...

i  deploying hosting
i  hosting: preparing public directory for upload...
✔  hosting: 1 files uploaded successfully
i  starting release process (may take several minutes)...

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/#####/overview
Hosting URL: https://#####.firebaseapp.com

Hosting URLをブラウザで開くとちゃんとデプロイされてるのが確認出来ます。

FCMの設定

ブラウザからプロジェクトを開くとウェブアプリにFirebaseを追加というボタンがあるので

クリックすると設定のスクリプトが書いてあるのでそれをコピペしてindex.htmlに貼り付けましょう。

通知の許可を取るまではローカルサーバでも大丈夫です。

index.html

<!doctype html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>FCM Sample</title>
</head>

<body>
  <script src="https://www.gstatic.com/firebasejs/4.3.0/firebase.js"></script>
  <script>
    // Initialize Firebase
    var config = {
      apiKey: ###
      authDomain: ###,
      databaseURL: ###,
      projectId: ###
      storageBucket: ###,
      messagingSenderId: ###,
    };
    firebase.initializeApp(config);
  </script>
</body>

</html>

これでプロジェクトの初期設定が完了です。

ServiceWorkerの作成

firebase-messaging-sw.js

FCMはルートディレクトリにあるfirebase-messaging-sw.jsというファイルをServiceWorkerに読み込ませます。

なので、このファイルをpublic以下に作成して中身を以下にします。

importScripts('https://www.gstatic.com/firebasejs/3.9.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.9.0/firebase-messaging.js');

firebase.initializeApp({
  messagingSenderId: "###", // index.htmlにあるconfigのmessagingSenderIdと同じもの
});

const messaging = firebase.messaging();

manifest.json

manifest.jsonもpublic以下に作成します。manifest.jsonの詳しい説明はこちら

ウェブアプリ マニフェスト  |  Web  |  Google Developers

とりあえず今回は中身を以下にします。

{
  "gcm_sender_id": "103953800507"
}

gcm_sender_idは固定値で、全ての環境で同じ値を使います。

通知の許可を取る

次にユーザに通知を受け取るかどうかの許可を取ります。

    firebase.initializeApp(config);

    const messaging = firebase.messaging();
    messaging.requestPermission()
      .then(() => console.log('granted'))
      .catch(e => console.log(e));

これで実行すると通知の許可を取るダイアログが表示されます。

f:id:saburesan:20170818164215p:plain

InstanceIDトークンの取得

InstanceIDとはFirebaseのプロジェクトごとにユーザに割り当てられるIDのことです(多分)

それを元にしたトークンを使って個人ユーザにメッセージを送信することができます。

   messaging.requestPermission()
      .then(() => messaging.getToken())
      .then((token) => console.log(token))
      .catch(e => console.log(e));

Push通知はサーバから送ると思うのでこの時点でtokenをサーバに送りましょう。

メッセージの受信設定

フォアグラウンド通知(サイトを開いている状態)を受け取るためには以下の方法で受け取れます。

    messaging.requestPermission()
      .then(() => messaging.getToken())
      .then((token) => {
        console.log(token);
        messaging.onMessage(payload => alert(JSON.stringify(payload)));
      })
      .catch(e => console.log(e));

Push通知を送ってみる

ではPush通知を送ってみましょう。

送るのに必要なのはFirebaseプロジェクトのサーバーキーとInstanceIDトークンです。

https://fcm.googleapis.com/fcm/send
Content-Type: application/json
Authorization: key="Firebaseプロジェクトのサーバーキー"

{ "notification": {
    "title": "Hello",
    "body": "body",
    "click_action" : "http://saburesan.hatenablog.com/"
  },

  "to" : "// InstanceIDトークン"
}

フォアグラウンド通知(サイトを開いている状態)

f:id:saburesan:20170818172817p:plain

バックグラウンド通知(サイトが開かれていない状態)

f:id:saburesan:20170818173244p:plain

通知のpayloadを

"notification : {
   "title: " string, // 通知のタイトル
   "body": string, // 通知の本文
   "click_action": string, // タップした時に開きたいリンク
   "icon": string // 通知のアイコンに設定したい画像のパス
}

の形式にしておくと上記のスクリーンショットのように勝手に設定されます。

これでPush通知の基本的な実装は終わりです。

Topicsの購読

Firebaseには複数のユーザに同時にPushを送るTopicsという機能があります。

そのTopicsを購読するには以下のようにしてIID_TOKEN, TOPIC_NAME, PROJECT_SERVER_KEYを用いてリクエストを送る必要ことで購読することが出来ます。

fetch(`https://iid.googleapis.com/iid/v1/IID_TOKEN/rel/topics/TOPIC_NAME`, {
    method: 'POST',
    headers: {
      Authorization: PROJECT_SERVER_KEY,
    },
  }),

まとめ

纏まってない。

React Native version 0.47.0 is out

github.com

Breaking changes

Android

Remove unused createJSModules calls (ce6fb33, 53d5504) - @javache

ReactPackageというinterfaceからcreateJSModulesが削除されました。

これ結構ヤバイ変更だと思っています。

Androidのネイティブにアクセスするライブラリは軒並みこの変更に対応しないとビルド通りません。

ほぼすべてのライブラリで変更が必要なんじゃないかな

前バージョンではDeprecatedですらなかったのにこの変更は辛い。

(タグはv0.48.0-rcなんだけどな…)

自前のブリッジモジュールもそうですが、ライブラリの対応に追われますね…