自分を攻略していく記録

自分がやりたいことを達成するには何をすればいいのか、その攻略していく過程をつらつらと

Firebaseでサーバレスアプリをサクッと作ってみる① ~Cloud MessagingでPush通知実装~

Firebaseを使ってアプリを作る

イメージはこんな感じ。二時間もあれば画像アップロードもこれに加えて画像のアップロードも実装できる。

f:id:ngo275:20180711001107g:plain:w300

Firebaseがサクッとアプリを作るのにめちゃくちゃ便利なので、その手順を書いていきたい。ここで実装する機能としては、匿名ログイン(ログイン・サインアップをさせずに認証情報を与える)、DB(Firestore)に書き込み・読み取り、DBの書き込みに応じてプッシュ通知を飛ばす、のを想定する。こういった機能を実装しようとすると一日で1人でやるにはキツイが、Firebaseを利用するとサクッと数時間もあればできてしまう。たしかに悪意のあるユーザからの書き込みをどうケアするのか(基本的にクライアントからの情報は信用してはいけない)といったことは別途考える必要はあるが、プロトタイピングするにはモッテコイなサービスだと思う。

以下、利用するサービスや構成である。

長くなりそうなので、本記事では、プロジェクトを新規作成してプッシュ通知を実機に送るところまで実装していく。Firebaseを利用したプッシュ通知の実装の参考にもなればいいなという気持ちもある。

1. プロジェクトのセットアップ

Xcodeで新規プロジェクトの作成をする。

f:id:ngo275:20180709085757p:plain:w500

次にここでFirebaseのプロジェクトを作成する。

f:id:ngo275:20180709090223p:plain:w400

続いて、XcodeにFirebaseをセットアップしていく。

f:id:ngo275:20180709090431p:plain:w500

BundleIDやアプリのニックネームを入力して次に進むと、 GoogleService-Info.plist が発行されるので、それをXcodeドラッグアンドドロップして突っ込む。

f:id:ngo275:20180709090957p:plain:w400

最後に、CocoaPodsを使ってFirebaseをプロジェクトにインストールする。

$ cd /project // 自分のproject rootに行く
$ pod init
$ vim Podfile // # Pods for FirebaseSampleの下の行に pod 'Firebase/Core' を付け足す
$ pod install

pod install が完了すると FirebaseSample.xcworkspace ができているのでそれをダブルクリックしてXcodeを開く。 AppDelegate.swift を開いて以下のように import FirebaseFirebaseApp.configure() の2行を追加する。

import UIKit
import Firebase

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        FirebaseApp.configure()
        return true
    }
}

Xcodeで実行すると以下の画面になり、Firebaseのセットアップが完了する。

f:id:ngo275:20180709092225p:plain:w500

2. プッシュ通知の設定をしていく

プッシュ通知の証明書には.p12ではなく.p8を使う。 .p8はHTTP2に対応しており、 .p12ではあった有効期限もなく、.p12ファイルの作成よりもずっと簡単 だ。これからは.p12ではなく.p8を利用する方が良さそうでFirebaseも.p8を推奨している。ただし、AWSAmazon SNSやツールによってはまだ.p8に対応していないようなので注意が必要そう。

Xcodeに戻って、Capabilitiesで Push NotificationsBackground Modes をONにして Remote Notifications にチェックを入れておく。

f:id:ngo275:20180709092608p:plain:w500

続いて、AppleDev CenterでApp IDsを開くと、先ほど作成したプロジェクトが XC hoge fuga FirebaseSample という名前で追加されているので詳細を開く。すると、Push Notificationsの状態がConfigurableになっているのでEditを選択する。DevelopmentとDistribution両方の証明書を作成していくが、そのためにはCSRファイルが必要になるので、まずそれの作成をする(と言ってもすごく簡単)。一旦、以下の画面のまま、キーチェーンアクセスを開く。

f:id:ngo275:20180709093913p:plain:w500

キーチェーンアクセスを開き、 キーチェーンアクセス証明書アシスタント認証局に証明書を要求 をクリックする(画像は英語の設定ですが同じです)。

f:id:ngo275:20180709111723p:plain:w500

ユーザーのメールアドレス を入力し、 通称 はそのまま、 CAのメールアドレス は空欄のままで良い。 要求の処理ディスクに保存 を選択し 鍵ペア情報を設定 にチェックを入れ、 続ける をクリックする。

f:id:ngo275:20180709111856p:plain:w500

鍵の情報が以下のように表示されるが特にいじらずそのまま進めるとCSRファイルがダウンロードできる。

f:id:ngo275:20180709111958p:plain:w500

再び、Dev Centerに戻って、 Create Certificate をクリックして、作成したばかりのCSRファイルをアップロードする。

f:id:ngo275:20180709112412p:plain:w500

すると、.cerファイルが生成されるのでダウンロードし、クリックする。クリックしても何も起こらないように見えるがキーチェーンアクセスにセットされるので問題ない。DevelopmentとDistribution両方とも同じCSRファイルで設定する。Dev CenterでApp IDsの当該プロジェクトを確認して以下のように、Push Notificationsが緑になっていればOK。

f:id:ngo275:20180709113108p:plain:w500

Dev CenterのKeysという欄から.p8ファイルを作成する。Keysを開いて右上の+をクリックする。画像の方に入力して次へ進む。

f:id:ngo275:20180709115742p:plain:w500

下の画像のようになれば成功で、.p8ファイルをダウンロードし、Key IDも記録しておく。.p8ファイルはここでしかダウンロードできないので紛失しないように注意しなければならない。

f:id:ngo275:20180709115927p:plain:w500

次に、Firebaseのコンソールを開いてプッシュの証明書をセットしていく。まず、設定のクラウドメッセージングを開く。

f:id:ngo275:20180709120746p:plain:w600

以下の画像にあるAPNs認証キーが.p8ファイルのことである。.p8ファイルを生成した時に発行されたKey IDとMembershipのページのTeamIDを入力する。

f:id:ngo275:20180709141840p:plain:w500

これでプッシュ通知まわりの設定は完了したので、あとはコードを書いていく。といっても書くのは主に AppDelegate.swift くらいある。

iOSプッシュ通知の仕組みについては以下が参考になる。

qiita.com

3. プッシュ通知の実装をしていく

Podfilepod 'Firebase/Messaging' を追加して、 pod install を実行する。 AppDelegate.swift を以下のように実装する。この中で各デバイスのFCMトークンを取得できる。このトークンに向かってFirebaseでプッシュ通知を送ることができるので、後々、Firestoreの書き込みに応じて任意のユーザにプッシュ通知を送る場合はこのFCMトークンが必要になってくる。ここでは一旦printするだけにしている。

import UIKit
import Firebase
import FirebaseMessaging
import UserNotifications

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        
        FirebaseApp.configure()
        
        // 以下を追加する
        application.registerForRemoteNotifications()
        Messaging.messaging().delegate = self
        UNUserNotificationCenter.current().delegate = self
        
        return true
    }
}

// MARK: - UNUserNotificationCenterDelegate
extension AppDelegate : UNUserNotificationCenterDelegate {
    
    // 通知を受け取った時に(開く前に)呼ばれるメソッド
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        let userInfo = notification.request.content.userInfo
        
        // for analytics
        // Messaging.messaging().appDidReceiveMessage(userInfo)
        
        if let messageID = userInfo["gcm.message_id"] {
            print("Message ID: \(messageID)")
        }
        
        completionHandler([.alert])
    }
    
    // 通知を開いた時に呼ばれるメソッド
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                didReceive response: UNNotificationResponse,
                                withCompletionHandler completionHandler: @escaping () -> Void) {
        let userInfo = response.notification.request.content.userInfo
        
        if let messageID = userInfo["gcm.message_id"] {
            print("Message ID: \(messageID)")
        }
        
        completionHandler()
    }
}

// MARK: - MessagingDelegate
extension AppDelegate: MessagingDelegate {
    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
        print("Firebase registration token: \(fcmToken)")
    }
    
    func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) {
        print("Received data message: \(remoteMessage.appData)")
    }
}

続いて、 ViewController.swift にプッシュ通知の許可ダイアログを表示するように実装する。コードは以下のようになる。

import UIKit
import UserNotifications

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // プッシュ通知の許諾ダイアログを出したいタイミングで呼んであげる. 必ずしもここじゃなくても良い
        let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
        UNUserNotificationCenter.current().requestAuthorization(options: authOptions) { _, _ in
            print("push permission finished")
        }
    }
}

実行すると画像のようにプッシュ通知許可のダイアログが表示される。

f:id:ngo275:20180709154902p:plain:w300

ここまで来たので試しに取得したFCMトークンに対してプッシュ通知を送ってみる。Xcodeデバッグで吐かれたFCMトークンをコピーして、以下の画像のようにFirebaseのコンソールのCloud Messagingの項目からプッシュ通知を送信してみる。

f:id:ngo275:20180709155622p:plain:w500

ちゃんと届いたことが確認できた。

f:id:ngo275:20180709213441j:plain:w300

ソースコードはここにあります。

github.com

つづく。次は、Firestoreにデータを書き込んでそれをトリガーにしてプッシュ通知が届くように実装していく。

diary.shuichi.tech