IonicでFCMによるpush通知を行う

この記事は [Ionic Advent Calendar](https://qiita.com/advent-calendar/2017/ionic) の6日目の記事です

push通知はアプリにはだいたい必要かつ割とめんどくさいイメージがありますがFCM（[Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging/?hl=ja)）ならサクッと実装できちゃいます
IonicにもFCMのプラグインがあるので今回はそれを使っていきたいと思います

（Firebaseの登録、証明書の作成などは省略します）

## 使い方

### インストール

以下のコマンドで [Ionic Native - FCM](https://ionicframework.com/docs/native/fcm/) をインストールします

```bash
$ ionic cordova plugin add cordova-plugin-fcm
$ npm install --save @ionic-native/fcm
```

### 設定ファイル

次にFirebaseの設定ファイル（iOS用：`GoogleService-Info.plist`、Android用：`google-services.json`）が必要なのでルートディレクトリに置いておきます
この辺を参考に取得してください
https://support.google.com/firebase/answer/7015592

### 実装

まず`app.module.ts`でインポート

```src/app/app.module.ts
import { FCM } from '@ionic-native/fcm';

...

@NgModule({

  ...

  providers: [

    ...

    FCM,

    ...

  ],

  ...

})
export class AppModule { }
```

あとは起動時やログイン後にDI＆初期化します
例えば`page-home`で呼ぶ場合は以下の通りです

```src/page/home/home.ts
import { Component } from '@angular/core';
import { IonicPage } from 'ionic-angular';
import { FCM } from '@ionic-native/fcm';

@IonicPage()
@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

  constructor(private fcm: FCM) {
  }

  ionViewDidLoad() {
    this.fcm.getToken().then(token => this.registerToken(token));

    this.fcm.onTokenRefresh().subscribe(token => this.registerToken(token));

    this.fcm.onNotification().subscribe(data => {
      if (data.wasTapped) {
        // バックグラウンド時の処理

        ...

      } else {
        // フォアグラウンド時の処理

        ...

      };
    });
  }

  private registerToken(token: string) {
    // WebAPI呼んでトークンを自前のDBなりに登録するなど

    ...

  }

}
```

あとはサーバ側からFCMに対してこんな感じのJSONを送りましょう

```json
{
  "notification": {
    "title": "通知テスト",
    "body": "いろはにほへと",
    "sound": "default"
  },
  "to": "xxxxxxxxxxxxxxxx[アプリから登録されたトークン]",
  "data": {
    "hoge": 1,
    "fuga": 2,
    "piyo": 3
  }
}
```

以上
たったこれだけです、お疲れ様でした

## と、思いきや

2つほどハマりポイントにぶつかってしまったので共有させていただきたいと思います
（どちらかというと`cordova-plugin-fcm`の問題ではありますが）

### ・其の壱 iOSで起動時に落ちる

どうやらビルド時に設定ファイルである`GoogleService-Info.plist`を`platforms/ios/[プロジェクト名]/Resources/GoogleService-Info.plist`に置いてくれているのですが、これはipaファイルには入らず（！？）実際は更に一段掘った`platforms/ios/[プロジェクト名]/Resources/Resources/GoogleService-Info.plist`を見に行っているようでした
見に行った先の中身は空でそこでコケちゃってるみたいです

なので対処するためにplistを正しい位置にコピーします
といっても`cp`するわけではなくビルド時に自動でやってもらいたいので`config.xml`に記述していきます

```config.xml
<?xml version='1.0' encoding='utf-8'?>
<widget ...>

    ...

    <platform name="ios">

        ...

        <resource-file src="GoogleService-Info.plist" target="Resources/GoogleService-Info.plist" />

        ...

    </platform>

    ...

</widget>
```

この状態でビルドすると`platforms/ios/[プロジェクト名]/Resources/Resources/GoogleService-Info.plist`にファイルが置かれ無事起動できました

### ・其の弐 Androidの通知用アイコンを設定する

https://github.com/fechanique/cordova-plugin-fcm の`README.md` には以下のように書かれています

>
 For Android >5.0 status bar icon, you must include transparent solid color icon with name 'fcm_push_icon.png' in the 'res' folder in the same way you add the other application icons. If you do not set this resource, then the SDK will use the default icon for your app which may not meet the standards for Android >5.0.
>

Android5以上から通知のアイコンはアルファチャンネルを使用していない場合に四角い豆腐になってしまいます
アプリのアイコンとして使用している画像が背景を透過していない場合は別途通知用のアイコンを作成する必要があります

参考：https://qiita.com/syarihu/items/95788cbab9b63100c4fb

通知用の透過アイコンを用意したら`README.md`の通りに`res`ディレクトリ内に置いておきましょう
`res`ディレクトリ下にも複数のディレクトリがありますが以下に置いておけば良さそうです

* platforms/android/res/mipmap-ldpi
* platforms/android/res/mipmap-mhdpi
* platforms/android/res/mipmap-hdpi
* platforms/android/res/mipmap-xxhdpi
* platforms/android/res/mipmap-xxhdpi
* platforms/android/res/mipmap-xxxhdpi

ここでも先程の要領で`config.xml`に記述していきます

```config.xml
<?xml version='1.0' encoding='utf-8'?>
<widget ...>

    ...

    <platform name="android">

        ...

        <resource-file src="resources/android/fcm_push_icon/drawable-ldpi-icon.png" target="res/mipmap-ldpi/fcm_push_icon.png" />
        <resource-file src="resources/android/fcm_push_icon/drawable-mdpi-icon.png" target="res/mipmap-mdpi/fcm_push_icon.png" />
        <resource-file src="resources/android/fcm_push_icon/drawable-hdpi-icon.png" target="res/mipmap-hdpi/fcm_push_icon.png" />
        <resource-file src="resources/android/fcm_push_icon/drawable-xhdpi-icon.png" target="res/mipmap-xhdpi/fcm_push_icon.png" />
        <resource-file src="resources/android/fcm_push_icon/drawable-xxhdpi-icon.png" target="res/mipmap-xxhdpi/fcm_push_icon.png" />
        <resource-file src="resources/android/fcm_push_icon/drawable-xxxhdpi-icon.png" target="res/mipmap-xxxhdpi/fcm_push_icon.png" />

        ...

    </platform>

    ...

</widget>
```

コピー元のディレクトリ名/ファイル名は一例ではありますが上記のように書いた場合は`resources/android/fcm_push_icon/`以下に各サイズのアイコンを置いておけば`platforms/android/res/`に適宜コピーされます

各アイコンは適当な生成サービスを利用するか本アイコンを退避させてから`ionic cordova resources`で生成するなどしてください
こんな感じでもいいです

```bash
$ cp /path/to/fcm_push_icon.png resources/icon.png
$ ionic cordova resources --android --icon
$ mv resources/android/icon resources/android/fcm_push_icon
$ git checkout -- resources/android/icon.png resources/android/icon
```

あとは通知のパラメータにアイコンと透過部分の色を指定するだけです

```json
{
  "notification": {

    ...

    "icon": "fcm_push_icon",
    "color": "#A3C642",

    ...

  },

  ...

}
```

これで豆腐アイコンじゃなくなりFCMによるpush通知が問題なく行えるようになりました

## まとめ

Ionic最高！！ :octopus: