Monaca×BaaS@rakuzaでプッシュ通知してみた

[前の記事](http://qiita.com/kishisuke/items/be9f2af76f5231fb84e7)で作成したお知らせをPush通知するアプリを作ってみます。

# Push通知のイメージ図
Push通知は以下のイメージで構成されています。デバイストークンを、iOSはAPNs（AppleのPush通知サーバ）、AndroidはGCM/FCM（GoogleのPush通知サーバー）より取得して、BaaS@rakuzaに保存します。BaaS@rakuzaは宛先となるデバイストークンとメッセージをAPNs/GCM/FCMに送り、デバイスにPush通知が届きます。

<img width="760" alt="スクリーンショット 2017-03-21 23.08.36.png" src="https://qiita-image-store.s3.amazonaws.com/0/51815/17314b9c-afb1-11b1-0825-0349d0ab2ba1.png">

# 必要なもの
- Mac（できれば）
    - 必須ではないですが、iOSの証明書の作成はMacの方が楽です。
- iPhone実機
- Android実機（エミュレータでもたぶんOK）
- Apple Developer Program
    - 証明書などの作成で必要です。有料
- Googleアカウント
    - Google Developer Console用。APIキーの作成に必要です。
- Monacaアカウント（Goldプラン以上）
    - サードパーティのCordovaプラグインを使用するため、Goldプラン以上が必要です
    - (2017/3/26追記)[2017/4/27から料金プランが改定される](https://ja.monaca.io/important-info/info-new-plan.html)とのことです。改定後はProプラン以上でOKです。
- BaaS@rakuzaのアカウント（無料体験版でもOK！）

# 1. iOSの設定

## 証明書などの作成
イメージ図の「①デバイストークン取得」、「④プッシュ通知」に必要な、証明書などを作成/登録します。以下、作成/登録が必要な項目です。

1. CSRの作成
2. ビルド用証明書の作成
3. AppIDの作成
4. 端末の登録
5. プロビジョニングプロファイルの作成

１〜５については、[Monacaさんの公式ページ](https://docs.monaca.io/ja/manual/build/ios/build_ios/)が詳しいので、こちらを参照します。ステップ１〜２まで行えばOKです。ただし、一部については以下に読み替えて進めます。

- 証明書は「配布用証明書」を作成します。
    - BaaS@rakuzaはPush通知をAPNsの実稼働用サーバ（api.push.apple.com:443）に送信します。そのため、アプリ証明書および後述するPush通知証明書も配布用証明書が必要になります。
- AppIDの登録は「Explicit App ID」で行います。
- AppIDの「App Services」→「Push Notifications」にチェックをします。
- Provisioning Profilesは「Ad Hoc」を選択します。
    - こちらも配布用にします。リリースするわけではないので、「Ad Hoc」（評価用）でOKです。  

## Push通知証明書作成
BaaS@rakuzaからAPNsにPush通知を送信する際に必要となる証明書を作成します。（イメージ図の「③プッシュ通知依頼」に該当）この証明書はビルド用証明書とは別物なので注意です。

1. アプリビルド時の証明書作成と同様に、「Certificates, Identifiers & Profiles」→「Certificates」→右上の「＋」をクリックし、「Production」→「Apple Push Notification service SSL (Sandbox & Production)」を選択して、Continue。
2. 作成したAppIDを選択して、Continue。
3. CSRを作成して、作成したファイルをアップロードします。
    - CSRの作成はhttp://qiita.com/takasan/items/2e9b1f7c3f78ea093195 が参考になります。（Windows/Linuxの方は、http://qiita.com/shtnkgm/items/2c9c5eb432c940d66e05 を参考にOpenSSLで作成してもOK）
4. 作成した証明書はダウンロードして、ファイル形式をp12かpemに変換します。
    - macOSであれば、ダブルクリックして一旦インストールし、キーチェーンアクセスツールで証明書を右クリックして書き出せばp12に変換できます。書き出す際、パスフレーズを設定しないよう注意です。（Windows/Linuxの方は「cer pem 変換」でググりましょう！）

![スクリーンショット 2017-03-19 22.30.54.png](https://qiita-image-store.s3.amazonaws.com/0/51815/75123330-083d-5f01-efc5-1de0703abd75.png)


# 2. Androidの設定
イメージ図の「①デバイストークン取得」には「SenderID」が、「③プッシュ通知依頼」には「APIキー」が必要です。SenderID、APIキーの作成は[Firebase](https://firebase.google.com/)で行います。（AndroidのPush通知はGCMからFCMに移行し、新規にGCM向けのAPIキーを[Google Developer Console](https://console.developers.google.com/)を通して作成することはできなくなりました。[^1]）

[コンソール画面](https://console.firebase.google.com/)に移動し、「新規プロジェクトを作成」よりプロジェクトを作成します。
![fb3.png](https://qiita-image-store.s3.amazonaws.com/0/51815/8a9b0048-2125-d0b7-2b10-7f963e84333a.png)

プロジェクト作成後、左上の歯車マーク→「プロジェクトの設定」→「クラウドメッセージング」タブを開きます。
![fb4.png](https://qiita-image-store.s3.amazonaws.com/0/51815/aed32207-f867-48f2-2e49-a3e2bf565c34.png)

表示された画面の「送信者 ID」がSenderID、「Legacy server key」がAPIキーです。（2017/03/22時点でBaaS@rakuzaは古い形式のAPIキーのみをサポートしています。そのため、「Server key」ではなく「Legacy server key」を使用する必要があります）
![fb5.png](https://qiita-image-store.s3.amazonaws.com/0/51815/7edd7e8b-c0e6-1033-bba8-ef6f5ee02945.png)

[^1]: [公式ドキュメント](https://developers.google.com/cloud-messaging/http-server-ref#error-codes)の「Authentication Error」を参照

# 3. Push通知証明書、APIキーをBaaS管理者画面で登録
管理者画面の「プッシュ通知管理」→「プッシュ通知環境設定」を開き、前章で作成した証明書＆APIキーをBaaS@rakuzaに設定します。iOSのPush通知証明書はファイル名が日本語だと変な感じになるので、英数字のファイル名に変換しておくことをおすすめします。（push.p12とか）

![スクリーンショット 2017-03-19 22.46.41.png](https://qiita-image-store.s3.amazonaws.com/0/51815/02601a90-8858-6832-fe24-5c890725b246.png)


# 4. 「phonegap-plugin-push」のインストール
Push通知の各種機能（デバイストークン取得など）を使用するためには、Cordovaプラグインが必要になります。ここでは、汎用的なPush通知プラグインの「phonegap-plugin-push」を使用します。

バージョンは[1.8.4](https://github.com/phonegap/phonegap-plugin-push/releases/tag/v1.8.4)にします。（1.9.x以降はCordovaのバージョンが6.4.0以降となっていますが、Monacaは6.2をサポートしているため）
いつもの様にgitのURLを設定すると最新版が読み込まれてしまうので、一旦1.8.4のソースコードをダウンロードして、「圧縮されたZIPパッケージをアップロード」でzipファイルをアップロードします。
<img width="660" alt="スクリーンショット 2017-03-19 23.26.29.png" src="https://qiita-image-store.s3.amazonaws.com/0/51815/c7566049-b1f6-0150-1fe3-cd5d76cd2f60.png">

また、インストール後に「Push Plugin」→「設定」より、SenderIDを設定します。

```
SENDER_ID=<「2. Androidの設定」で作成したSenderID>
```
![スクリーンショット 2017-03-19 23.26.51.png](https://qiita-image-store.s3.amazonaws.com/0/51815/00c0503b-9368-e60a-ceeb-708a431dc8d6.png)


※余談ですが、phonegap-plugin-pushは2.0.0（まだrcですが）からFCMに対応しています。しかし、Cordovaのバージョンが6.4.0以降のため、Monacaでは使用できません・・。6.4.0以降の対応はやくきてくれーー！！

# 5. ユーザーの登録
BaaS@rakuzaでは１ユーザー＝１デバイストークンとなっているため、デバイストークンの登録にユーザーの登録が必要です。ユーザーはID/パスワードでも登録できますが、とりあえず匿名ユーザーを登録します。

```js:MainController.js
const USER_ACCESS_TOKEN_KEY = 'userAccessToken';

function setupRakuza() {
  // 第一引数で指定されたテナントキーの値をもとに楽座のテナントを設定します
  var tenantKey = 'ここにテナントキー';
  RKZClient.setTenantKey(tenantKey, function() {
    // 成功時の処理
    alert('RKZClientクラスの初期化に成功しました。');
    // ユーザーの登録
    registerUser();
  }, function(error) {
    // エラー時にアラートでエラー内容を表示します
    alert(JSON.stringify(error, null, ' '));
  });
}

function registerUser() {
  var deferred = $q.defer();

  // ローカルからユーザーアクセストークンを取得
  var userAccessToken = localStorage.getItem(USER_ACCESS_TOKEN_KEY);
  if (!userAccessToken) {
    // ユーザーアクセストークンがなければ、新規でユーザーを作成
    var userData = {};
    RKZClient.registUser(userData, function (userData) {
      alert(userData.user_access_token + "," + userData.user_no);

      // ユーザーアクセストークンをローカルに保存する
      localStorage.setItem(USER_ACCESS_TOKEN_KEY, userData.user_access_token);
      // ユーザーアクセストークンを返す
      deferred.resolve(userData.user_access_token);
    }, function (error) {
      alert(JSON.stringify(error, null, ' '));
      deferred.reject(error);
    });
  } else {
    // あれば、ユーザーアクセストークンを返す
    deferred.resolve(userAccessToken);
  }

  // 結果はPrimiseを返して、呼び出し元で結果をハンドリングできる様にする
  return deferred.promise;
}
```

上記コードを実行後に管理者画面でユーザー一覧画面を表示すると、ユーザーが登録されたことが確認できます。
![スクリーンショット 2017-03-19 23.48.48.png](https://qiita-image-store.s3.amazonaws.com/0/51815/6761bbcf-064d-f7df-bcd0-01ac103a6cb6.png)


# 6. デバイストークンの取得＆登録
続いて、デバイストークンの取得＆登録を行います。Push通知を行うには、デバイストークンが必要なため、アプリ側でデバイストークンを取得し、BaaS@rakuzaに送信＆登録を行う必要があります。先ほどのユーザー登録処理後に、デバイストークンの取得＆登録処理の関数を呼び出す様修正します。

```js:MainController.js
function setupRakuza() {
  // 第一引数で指定されたテナントキーの値をもとに楽座のテナントを設定します
  var tenantKey = 'ここにテナントキー';
  RKZClient.setTenantKey(tenantKey, function() {
    // 成功時の処理
    alert('RKZClientクラスの初期化に成功しました。');
    // ユーザーの登録
    registerUser().then(function (userAccessToken) {
      // プッシュ通知のセットアップ
      setupPushNotification(userAccessToken);
    });
  }, function(error) {
    // エラー時にアラートでエラー内容を表示します
    alert(JSON.stringify(error, null, ' '));
  });
}

function registerUser() {
  // 略...
}

function setupPushNotification(userAccessToken) {
  // プッシュ通知初期設定
  var push = PushNotification.init({
    android: {
      senderID: '「2. Androidの設定」で作成したSenderID'
    },
    ios: {
      alert: 'true',
      badge: 'true',
      sound: 'true'
    }
  });

  // デバイストークン取得時のイベント
  push.on('registration', function(data) {
    console.info('registrationId: ' + data.registrationId);

    // デバイストークンをBaaS@rakuzaに送信＆登録
    RKZClient.registPushDeviceToken(userAccessToken, data.registrationId, function (statusCode) {
      alert('デバイストークンを登録しました。');
    }, function (error) {
      alert(JSON.stringify(error, null, ' '));
    });
  });

  // 通知受信時のイベント
  push.on('notification', function(data) {
    console.info('message: ' + data.message);
    console.info('title: ' + data.title);
    console.info('count: ' + data.count);
    console.info('sound: ' + data.sound);
    console.info('image: ' + data.image);
    console.info('additionalData: ' + data.additionalData);
  });
  
  push.on('error', function(e) {
    console.error('message: ' + e.message);
  });
}
```

# 7. アプリのビルド
前述までの状態でアプリをビルドして実機にインストールします。iOSはAd Hocでビルドしてください。Androidはデバッグビルドで構いません。

# 8. Push通知実行
管理者画面より、お知らせをPush通知します。「お知らせ管理」→「お知らせ一覧」よりお知らせ一覧を表示し、Push通知したいお知らせの「Push」列のボタンをクリックします。
プッシュ通知入力画面でタイトルを編集できます。（ちなみに、実際に送信されるのはタイトルのみです）
配信日時を変更して、指定時間に送信も可能です。すぐに送りたい場合は変更せずそのまま「次へ」ボタンをクリックして、予約をします。
![スクリーンショット 2017-03-20 0.21.27.png](https://qiita-image-store.s3.amazonaws.com/0/51815/80827eaf-76d4-9a65-52eb-be26b6bbe34f.png)

しばらくすると、Push通知が届きます。
<img width="375" alt="IMG_0001.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/51815/d182f53c-8b3a-1af0-cf9d-656f77eb33b2.jpeg">

---
もし、Push通知が届かない！？場合は管理画面（「プッシュ通知管理」→「プッシュ通知履歴」）から送信ログを参照できるので確認してみて下さい。「ログ詳細」からダウンロードできます。
![無題5.png](https://qiita-image-store.s3.amazonaws.com/0/51815/244c5ee3-6506-91f5-63c6-b1ce71d92ef5.png)

エラーメッセージが以下の場合、APIキーが間違っているか、FCM移行後にGoogle Developer ConsoleでAPIキーを作成した可能性があります。後者の場合は、Firebaseより「Google プロジェクトをインポート」を実行して生成された「Legacy server key」を指定して下さい。
> Android端末へのプッシュ通知の送信に失敗しました。 : HTTP/1.0 401 Unauthorized

# ソースコード
今回修正したソースコードです。
https://github.com/kishino/BaaSSampleApp/commit/0400042c1fb001644c1d3cf39701edd5e6ecdf2a

# おわりに
おそらく、一番ハマるのはiOSの証明書などの作成だと思います。（私もはまりました・・:sob:）
ただ、そこを乗り越えれば面倒なサーバの処理をBaaSが行なってくれるので、簡単にPush通知が実装できて良い感じでした:grinning: