【SwiftUI】FirebaseMessagingでプッシュ通知を実装する

# はじめに
プッシュ通知の実装は難しいものだと思ってずっと避けてきましたが、めっちゃ簡単だったので記事にしておきます。
古い記事を見るとめっちゃ複雑で難しそうなのですが、多分簡単になってます。
Firebaseにアプリは設定されている前提での記事になります。
まだ、以下のようなページがない場合は設定してください。
![スクリーンショット 2023-02-28 22.01.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/1bf286f8-a661-d9c8-4f58-67337a64ec04.png)

# APNs認証キーの作成
[こちら](https://developer.apple.com/account/resources/authkeys/list)から「Certificates, Identifiers & Profiles」を開きます。
① 「Keys」を選択します
② 「+」を選択します
![スクリーンショット 2023-02-28 22.03.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/1843bce3-b74b-16bc-4258-4637d33b0637.png)

③ 「Key Name」を決めます(ここはなんでもいい)
④ 「Apple Push Notifications service (APNs)」にチェックを入れます
⑤ 「Continue」を選択します
![スクリーンショット 2023-02-28 22.08.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/bbcb23ee-1cf5-e04e-ecc8-6e60f9a0f7a1.png)

⑥ 「Register」を選択します
![スクリーンショット 2023-02-28 22.11.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/950429b5-788b-11c0-a87a-9d5be9dd65da.png)

⑦ 「Download」を選択します
![スクリーンショット 2023-02-28 22.12.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/3e18f522-551c-6f60-e8dc-217f95c5138e.png)

# APNs認証キーのアップロード
① 設定マークを選択します
② 「プロジェクトの設定」を選択します
![スクリーンショット 2023-02-28 22.22.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/f57cb6f1-d901-3461-3e02-e1196527c5a1.png)

③ 「Cloud Messaging」を選択します
![スクリーンショット 2023-02-28 22.25.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/c3c090da-c4fb-b17f-453b-0dfcfa3e8f13.png)

④ 「アップロード」を選択します
![スクリーンショット 2023-02-28 22.26.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/5e06044b-a441-ff27-29f6-044c72f9fd2f.png)

⑤ ダウンロードしたファイルをドラッグします
⑥ キーIDを入力します(キーIDの場所は下で解説します)
⑦ チームIDを入力します(チームIDの場所は下で解説します)
![スクリーンショット 2023-02-28 22.28.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/1c29e0f1-677b-77f4-1629-47425355bba3.png)

:::note info
**キーIDの場所**
[ここ](https://developer.apple.com/account/resources/authkeys/list)から先ほど作成したAPNs認証キーを選択してください
![スクリーンショット 2023-02-28 22.34.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/f8000ad9-29f0-e2e5-6f60-f646a57ea5ec.png)
:::

:::note info
**チームIDの場所**
[ここ](https://developer.apple.com/account)から「メンバーシップの詳細」を確認します
![スクリーンショット 2023-02-28 22.36.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/85da4fc8-5ac1-e221-5184-96ba864c42ab.png)
:::

⑧ 「アップロード」を選択します
![スクリーンショット 2023-02-28 22.39.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/7f67d6db-8c57-55d9-5eac-22ac7de425b8.png)

# 実装
```swift:AppDelegate
import Firebase
import FirebaseCore
import FirebaseMessaging
import UserNotifications

final class AppDelegate: NSObject, UIApplicationDelegate {
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions _: [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()

        Messaging.messaging().token { token, error in
            if let error {
                print("Error fetching FCM registration token: \(error)")
            } else if let token {
                print("FCM registration token: \(token)")
            }
        }

        return true
    }

    func application(_: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        print("Oh no! Failed to register for remote notifications with error \(error)")
    }

    func application(_: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        var readableToken = ""
        for index in 0 ..< deviceToken.count {
            readableToken += String(format: "%02.2hhx", deviceToken[index] as CVarArg)
        }
        print("Received an APNs device token: \(readableToken)")
    }
}

extension AppDelegate: MessagingDelegate {
    @objc func messaging(_: Messaging, didReceiveRegistrationToken fcmToken: String?) {
        print("Firebase token: \(String(describing: fcmToken))")
    }
}

extension AppDelegate: UNUserNotificationCenterDelegate {
    func userNotificationCenter(
        _: UNUserNotificationCenter,
        willPresent _: UNNotification,
        withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
    ) {
        completionHandler([[.banner, .list, .sound]])
    }

    func userNotificationCenter(
        _: UNUserNotificationCenter,
        didReceive response: UNNotificationResponse,
        withCompletionHandler completionHandler: @escaping () -> Void
    ) {
        let userInfo = response.notification.request.content.userInfo
        NotificationCenter.default.post(
            name: Notification.Name("didReceiveRemoteNotification"),
            object: nil,
            userInfo: userInfo
        )
        completionHandler()
    }
}
```

```swift:SampleApp
import SwiftUI

@main
struct SampleApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
```

# 通知を送る
① 「最初のキャンペーンを作成」を選択します
![スクリーンショット 2023-02-28 22.46.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/4a6ea62c-2598-2285-63c7-052c65f8ef40.png)

② 「Firebase Notification メッセージ」を選択します
③ 「作成」を選択します
![スクリーンショット 2023-02-28 22.47.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/daf17795-74f1-4d99-1978-501d030a2ea2.png)

④ 「通知のタイトル」を設定します
⑤ 「通知テキスト」を設定します
⑥ 「次へ」を選択します
![スクリーンショット 2023-02-28 22.51.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/05224602-86f6-6f22-8502-b7df66ea1e1c.png)

⑦ 対象のアプリを設定します
⑧ 「次へ」を選択します
![スクリーンショット 2023-02-28 22.52.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/4ae0c400-cfa5-4d68-8fce-b32774b791e7.png)

⑨ 「次へ」を選択します
![スクリーンショット 2023-02-28 22.54.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/7abf37e0-f9a4-2718-b178-727d75c6ef7f.png)

⑩ 「次へ」を選択します
![スクリーンショット 2023-02-28 22.55.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/7b7ec089-d517-726d-783e-9a739ceaa670.png)

⑪ 「確認」を選択します
![スクリーンショット 2023-02-28 22.55.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/7c3abe3b-2af7-b999-fd4f-25f5ac48a04c.png)

⑫ 「公開」を選択します
![スクリーンショット 2023-02-28 22.56.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/53bfa48e-8eb5-be02-32cc-f931d320f7f6.png)


この画面になっていれば成功だと思います！
6分後に来たり、2分後に来たり結構アバウトな感じなのでちょっと待ってみましょう
アプリに通知が来たら成功です。
![スクリーンショット 2023-02-28 22.57.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/bf569e39-fedf-05f1-5835-93450056a3cd.png)

# おわり
こんなに簡単にプッシュ通知が実装できるなんて感動です
これが無料ってFirebaseやばくないですかね？