Ionic プッシュ通知を送るテスト（Capacitor）

#はじめに
この記事は[こちら](https://capacitor.ionicframework.com/docs/guides/push-notifications-firebase/)の公式ページを元にしてあります。（ってか内容は全く同じ）

[こちら](https://capacitor.ionicframework.com/docs/guides/push-notifications-firebase/)の公式ページはかなり丁寧に作られてるので、あまりつまずく点はありませんが、、、一応少しだけ追加情報を足しておきます。
**（2021/7月　Capacitor3に対応した内容に更新しました！）**

と言う訳で、**Firebase Cloud Messagingを使ってプッシュ通知を実装します〜**

#前提条件
・Ionic + Angularのionicアプリが既にある

・FirebaseプロジェクトとIonicアプリが、既に紐ずけされてある
→Android用の`google-services.json`と、iOS用の`GoogleService-Info.plist`が、ダウンロード済み！
<img width="320" alt="スクリーンショット 2019-12-31 17.15.41.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/3d4b66a5-5328-fcf4-bd5a-b609b1a75058.jpeg">
3つアイコンあればOK！

**・プッシュ通知をテストするには、iOSとAndroid共に実機でテストする必要があります。**
→両方でやりたい人は、両方のデバイスが必要！

#push.service.tsを作成
`home.page.ts`にプッシュ通知関連のコードをかくと、めちゃくちゃ長くなるので:expressionless:
serviceにまとめちゃいます。

```terminal
ionic g service service/push
```

これでserviceフォルダに、`push.service.ts`が作成されたかと。

#Capacitor Push Notification APIを使用して、プッシュ通知に登録

```typescript:push.service.ts
import { Injectable } from '@angular/core';
import {
  ActionPerformed,
  PushNotificationSchema,
  PushNotifications,
  Token,
} from '@capacitor/push-notifications';

@Injectable({
  providedIn: 'root'
})
export class PushService {
  constructor() {}

  pushRegister() {
    console.log('Initializing HomePage');

     PushNotifications.requestPermission().then(result => {
      if (result.receive === 'granted') {
        // Register with Apple / Google to receive push via APNS/FCM
        PushNotifications.register();
        console.log('push通知の登録できました！');
      } else {
        console.log('push通知の登録できないよ・・');
      }
    });

    // On success, we should be able to receive notifications
    PushNotifications.addListener(
      'registration',
      (token: Token) => {
        alert('Push registration success, token: ' + token.value);
      }
    );

    // Some issue with our setup and push will not work
    PushNotifications.addListener('registrationError', (error: any) => {
      alert('Error on registration: ' + JSON.stringify(error));
    });

      // もしアプリが開いてる状態で通知を受け取ったら
    PushNotifications.addListener(
      'pushNotificationReceived',
      (notification: PushNotificationSchema) => {
        alert('プッシュ通知を受け取ったよ: ' + JSON.stringify(notification));
        console.log(
          'プッシュ通知を受け取ったよ: ' + JSON.stringify(notification)
        );
      }
    );

     // 通知がタップされた時に発動する
    PushNotifications.addListener(
      'pushNotificationActionPerformed',
      (notification: ActionPerformed) => {
        alert('実行されたプッシュアクション: ' + JSON.stringify(notification));
        console.log(
          '実行されたプッシュアクション: ' + JSON.stringify(notification)
        );
      }
    );
}
```

`home.page.ts`で呼び出す

```typescript
import { PushService } from './../service/push.service';

constructor(
    private pushService: PushService
  ) { }

 async ngOnInit() {
    this.pushService.pushRegister();
  }
```

#capacitor.config.json
これが何をしてるのかは、わかりません。。。

```json
{
  "appId": "com.app.test",
  "appName": "push",
  "bundledWebRuntime": false,
  "npmClient": "npm",
  "webDir": "www",
  "plugins": {
    "PushNotifications": {
      "presentationOptions": [
        "badge",
        "sound",
        "alert"
      ]
    }
  }
}
```


#iOSの設定
[こちら](https://capacitor.ionicframework.com/docs/guides/push-notifications-firebase/)の公式ページでも言われていますが、~~iOSの設定すげーややこしいです。。。~~

まず、下の記事を見てくださ。
[iOS プッシュ通知に必要な証明書 p12とp8に関する疑問解消　（Firebase Cloud Messaging）](https://qiita.com/kokogento/items/6dc0ba04ef517158dc09)

###結論、p8の証明書を取りましょう！
[Capacitorの公式ページ](https://capacitor.ionicframework.com/docs/guides/push-notifications-firebase#ios)では、p12の証明書を取得する方法について解説されていました（解説と言う程では無いが、Apple公式ページのドキュメントへのリンクが貼られてある）

なので[こちら](https://qiita.com/matsuyoro/items/77408e5d09ef00be8577)の記事を参考にして、p8の証明書を取ってください。

##p8証明書を取得してFirebaseにアップロード
下の記事を参考に、p8証明書をFirebaseにアップロードしてください。
[お手軽firebaseプッシュ通知証明書作成メモ](https://qiita.com/matsuyoro/items/77408e5d09ef00be8577)

[Apple Developer Member Center](https://idmsa.apple.com/IDMSWebAuth/signin?appIdKey=891bd3417a7776362562d2197f89480a8547b108fd934911bcbea0110d07f757&path=%2Faccount%2F&rv=1)の画面の見た目が、変わっているので気をつけましょう。

<img width="567" alt="スクリーンショット 2020-01-01 9.18.32.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/698a3984-8f04-47ad-33f6-6bd507693e8b.jpeg">

 [Certificates, Identifiers & Profiles] に移動

<img width="217" alt="スクリーンショット 2020-01-01 9.18.47.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/4bf1c0ed-162c-ea1f-7034-7093a59133bb.jpeg">

[Keys]に移動

<img width="570" alt="スクリーンショット 2020-01-01 9.19.16.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/48494bc4-c900-caf3-ac4e-8cac8c2baa22.jpeg">

プラスボタンで作成


<img width="920" alt="スクリーンショット 2020-01-01 9.22.07.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/f3187d5b-5b17-97d0-942a-16d29a6cb317.jpeg">

**Apple Push Notifications service (APNs)**を選択して、作成。

[こちら](https://qiita.com/matsuyoro/items/77408e5d09ef00be8577)を参考にして、Firebaseにp8証明書を設定します。

##Xcodeでプッシュ通知を許可する
<img width="562" alt="スクリーンショット 2020-01-01 14.57.01.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/1b7378fc-ca05-4c25-3305-1cc65405c409.jpeg">

[+Capabilities]を押して、プッシュ通知を追加

##GoogleService-Info.plistを移動
`ios/App/App/`の階層に`GoogleService-Info.plist`を移す。
<img width="273" alt="google-plist-location-ios.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/8929c051-e982-cd14-bc0f-88f67f6beec7.png">

##Firebase SDKをCocoaPodsを使って追加
<img width="441" alt="podfile-location-ios.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/2ade5ee9-d825-9662-851f-53318f65a137.png">


`Pods/Podfile`に`pod 'Firebase/Messaging'`を追加

```
platform :ios, '11.0'
use_frameworks!

# workaround to avoid Xcode 10 caching of Pods that requires
# Product -> Clean Build Folder after new Cordova plugins installed
# Requires CocoaPods 1.6 or newer
install! 'cocoapods', :disable_input_output_paths => true

def capacitor_pods
  # Automatic Capacitor Pod dependencies, do not delete
  pod 'Capacitor', :path => '../../node_modules/@capacitor/ios'
  pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios'

  # Do not delete
end

target 'App' do
  capacitor_pods
  # Add your Pods here
  pod 'Firebase/Messaging' #追加
end
```

##プロジェクトをアップデート
```terminal
npx cap update ios
```

##AppDelegate.swiftに初期化コードを追加
```swift
import UIKit
import Capacitor
import FirebaseMessaging //追加

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

  var window: UIWindow?


  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    FirebaseApp.configure() //追加
    return true
  }

//省略

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
 //以下を追加
       Messaging.messaging().apnsToken = deviceToken
        Messaging.messaging().token(completion: { (token, error) in
            if let error = error {
                NotificationCenter.default.post(name: .capacitorDidFailToRegisterForRemoteNotifications, object: error)
            } else if let token = token {
                NotificationCenter.default.post(name: .capacitorDidRegisterForRemoteNotifications, object: token)
            }
          })
    }
```

#Androidの設定
iOSに比べえらくシンプル
##google-services.jsonを移動
<img width="258" alt="google-services-location-android.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/c9828a79-33e1-4d28-d546-9e26ff241170.png">
`android/app/`の階層に`google-services.json`を移す。

##プッシュ通知のためのアイコンを用意
[こちら](https://capacitor.ionicframework.com/docs/apis/push-notifications/)によると、Androidはプッシュ通知アイコンを用意しないといけないみたいです。

下の記事を参考にやってみてください。

[Android Push通知のアイコンを作成 ＋ 設定](https://qiita.com/kokogento/items/9e91075855779285e6ac)

#プッシュ通知を送る
FirebaseコンソールからCloud Messagingへ。
<img width="353" alt="スクリーンショット 2020-01-02 19.25.52.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/078d8d39-95ce-947c-2c3f-d0ce888e2288.jpeg">

新しい通知を作成し、アプリを閉じた状態で待ってみる。

**iOS**
![IMG_6423.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/ffff1393-2f0c-0d49-8a3c-9a4bc6bc9941.jpeg)

**Android**
![Screenshot_20200102_192438.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/992c6655-99fe-72e6-723e-512666dddd70.jpeg)

#自分の端末をテストデバイスとして登録
ユーザー全員にプッシュ通知を送れる状態ではテストできないので、自分のデバイスを**テストデバイス**として登録しておきましょう！

```typescript:push.service.ts
// On success, we should be able to receive notifications
    PushNotifications.addListener(
      'registration',
      (token: PushNotificationToken) => {
        // alert('登録に成功, tokenはこちら: ' + token.value);
        console.log('登録に成功, tokenはこちら: ' + token.value);
      }
    );
```

アプリ起動時に`token`がコンソールに表示されるようにしておきましょう。

このコンソールに表示された`token`をコピー。

<img width="458" alt="スクリーンショット 2020-01-05 21.07.41.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/044eb356-ebd2-b4ce-343b-7abdd1bbbe56.jpeg">

テストメッセージを送信ボタンを押す

<img width="542" alt="スクリーンショット 2020-01-05 21.07.53.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/d73be218-4c71-db01-effb-a258a6cf5374.jpeg">

**[FCM 登録トークンを追加]**というラベルの付いたフィールドで、先ほどコピーした登録トークンを入力。

これで特定の端末にだけ、プッシュ通知を送ることができるようになりました〜！


#発生したバグ
ここで1つ、私が遭遇したエラーを紹介。

<img width="563" alt="スクリーンショット 2019-12-30 21.23.13.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/e603bcd0-a1ce-2f4f-6fd0-8591379f90f8.jpeg">

上記の画像のように**`could not build module 'GoogleDataTransport'`**が大量に発生しました。。。

シュミレーターでは発生しなかったのに、実機で行うとこのエラーでビルドが失敗となりました。。

どうやって解決したかと言うと、[こちら](https://dev.classmethod.jp/smartphone/iphone/remove-xcode-derived-data/)の記事で解説されている**DerivedDataを削除してみる**事で、不思議なことにエラーが消えました！！

<img width="371" alt="スクリーンショット 2019-12-30 21.30.12.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/f9d49300-7cac-bb2c-d8bf-c8978b5fe52a.jpeg">


DerivedDataの削除は`上部メニュー > [Product] > [Clean Build Folder]`で実行できます。

>DerivedData は、プロジェクトごとに生成されるキャッシュデータといいますか、インデックスやログや生成物の等の溜め場といいますか、中間データの類です。 ときにこれが悪さをして不穏な動きをさせてしまっていることもあるようです。

と言う事で、消しても問題なし。:wink: