【iOS/Android】プッシュ通知の仕組みをざっくり解説

# はじめに
Firebase Cloud Messagingでプッシュ通知は意外と簡単に実装できちゃったりします。簡単がゆえに、全体像をイメージすることなく実現できている人も多いのではないでしょうか？
私もその一人です:joy_cat: 

**「何となくでプッシュ通知を実装したことがあるがあんまよくわかっていない」**
そんな人のためにかなりざっくりではありますが、アプリでのプッシュ通知の仕組みをまとめたいと思います。
**（Firebase Cloud Messagingを使うことが前提です）**

https://firebase.google.com/docs/cloud-messaging/fcm-architecture?hl=ja

👆の公式ドキュメントを参考にしながら噛み砕いて書いていきます！

# 全体像
![Screen Shot 2022-07-27 at 16.38.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/d5fa8437-8331-cee0-5664-7ec8900f8c4d.png)

## ①メッセージリクエストを作成
要するに「こんなプッシュ通知を作ってほしい」とリクエストを出すための原稿を作ってるような感じかと。

主に２つパターンがあります。
・Firebaseのコンソール画面（公式ドキュメント内で言う所のGUIベースのオプション）で作成
・サーバー環境（Cloud Functions for Firebaseなど）からメッセージのリクエストを作成

## ②メッセージ生成
詳しいことはよく分かりませんが「[ファンアウト](https://firebase.google.com/docs/cloud-messaging/concept-options?hl=ja#fanout_throttling)」とやらを実行して、
①で作られたメッセージリクエストを元に、Firebase Cloud Messaging（FCM）のバックエンドサーバーがメッセージを生成します。

## ③ターゲットデバイスにメッセージを配信
メッセージはFCMから直接送られてるわけではありません。

ではどこからデバイスにメッセージが送られてるのか？というと、👇のように
**・Google Play 開発者サービスを搭載した Android デバイス向けの Android トランスポート レイヤ（ATL）**
**・Apple デバイス向けの Apple Push Notification Service（APNs）**

各プラットフォームにプッシュ通知を送るためのtransporterのようなものが存在します。
**特にAPNsは後ほど証明書やら鍵やら使うのでとっても大事！！！**


## ④プッシュ通知が通知される
実際にデバイスがメッセージを受け取り、プッシュ通知として表示されます。

全体としてはこんな流れっぽいです。
ここからは各プラットフォームでの流れを見てみましょう。


# iOS
iOSの場合は少々面倒な手順があります。。。
## メッセージを送るために
iOS端末にプッシュ通知を送るにはApple Push Notification Service（APNs）を使います。

そして外部サーバー（今回はFirebase Cloud Messaging）からAPNsを使ってプッシュ通知を送るためには、アプリに紐づけられたAPNs証明書、もしくはAPNsキーが必要になります。
そのためAPNs証明書、もしくはAPNsキーを外部サーバーに登録する必要があります。

APNs証明書とAPNsキーについては👇を参考に。

https://qiita.com/kokogento/items/405703f3177ebd2e0320

### p8形式のAPNsキーの場合は👇のような手順で取得できます。（p12形式の証明書の場合は長いので省きます:joy_cat:）

https://developer.apple.com/

Developerアカウントへログイン
![Screen Shot 2022-07-27 at 18.36.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/577e011a-27e4-8958-772c-23928cb27a62.png)

![Screen Shot 2022-07-27 at 18.34.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/56ebcd1b-cb3e-a51f-c11d-4c1a341cd7a3.png)

![Screen Shot 2022-07-27 at 18.34.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/0dd8cbbb-2984-e933-193a-998f3458792f.png)

より詳しい情報は👇が参考になるかと

https://docs.repro.io/ja/dev/sdk/push-notification/setup-ios.html#apns-ios

### Firebaseに登録されてる状態はこんな感じ
![Screen Shot 2022-07-27 at 18.43.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167220/361e2aa9-2a84-3605-5260-f9b678803acb.png)


## 特定のデバイスにメッセージを送るために
特定のデバイスにメッセージを送るには、デバイスを一意に識別する「デバイストークン」が必要です。

アプリ上でプッシュ通知の登録（後述するregisterForRemoteNotifications()）を行うと、デバイストークンを取得することができます。

①アプリ起動
🔽
②ユーザーのデバイスはAPNsからデバイストークン要求
🔽
③取得したデバイストークンをFCMへ送信
🔽
④デバイストークンのコピーをFCMトークンとして作成し、ユーザーのデバイスへ返送

書き方は色々だと思いますが、swiftのコードは以下のようになるかと


```swift
import SwiftUI
import Firebase
import FirebaseMessaging

class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions
                     launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
        FirebaseApp.configure()
        Messaging.messaging().delegate = self
        UNUserNotificationCenter.current().delegate = self

        // 初回起動時、プッシュ通知の許可ダイアログを表示させる
	let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
        UNUserNotificationCenter.current().requestAuthorization(
        options: authOptions,
        completionHandler: { _, _ in }
        )
	
        application.registerForRemoteNotifications() // プッシュ通知の登録

        return true
    }
}
```

## 参考

https://www.youtube.com/watch?v=A1SDBIViRtE




# Android
AndroidはFCMから直接配信されるため、iOSとは違って他のサービスとの繋ぎ合わせは必要ありません:relaxed:
すげ！


# 最後に
色々と間違ったことも書いてるかと思うので、指摘あれば是非お願いします:hugging: