Amazon Pinpoint でプッシュ通知を打ってみよう !

2020-10-01
デベロッパーのためのクラウド活用方法

Author : 内山 陽介

皆さま、お待たせしました ! Amazon Pinpoint が Tokyo Region にやってきました !
ということで、今回は Amazon Pinpoint でプッシュ通知を打つ方法をご紹介していきたいと思います。

プッシュ通知はモバイルアプリを開発する上で多くの場合で必要になってきますが、自前で実装しようとするとプッシュ通知を送るためのサーバーを構築/運用したり、デバイストークンを管理したり、セグメントプッシュを送るためにユーザーの抽出を行なったり、と考えることが多いかと思います。そんなプッシュ通知ですが、Amazon Pinpoint を使うことで簡単に配信基盤を構築することができます。  


Amazon Pinpoint について

Amazon Pinpoint は AWS によるカスタマーエンゲージメントのプラットフォームです。Amazon Pinpoint の分析ツールは、ユーザーのエンゲージメントのレベル、購入や行動に関するアクティビティ、エンゲージメントの設定その他についてのトレンドを見ることができます。また、ニーズに合った対象者セグメントに対し Email や SMS、プッシュ通知、Voice メールを使ったキャンペーンメッセージを届けることができます。Amazon Pinpoint は後述する AWS Amplify との親和性が高く、Amplify CLI を用いることで、マネジメントコンソールからインフラを一つ一つ構築することなく、CLI 操作だけでバックエンドの構築を行うことができます。

※ 執筆時点 (2020/09/15) では、Tokyo Region では Voice は非対応となっています。  


AWS Amplify について

Amplify は以下の 3 つのサービスで構成されています。  

Amplify Libraries

Amplify Libraries は、AWS のバックエンドと連携する処理を極めて少ない行数で記述できる Libraries です。 iOS、Android、Web (React / Vue / Angular)、React Native にそれぞれ最適化されたインターフェースを提供します。先ほど紹介した Amplify CLI と統合することで、認証やファイルストレージへのアクセス、Websocket の利用、AppSync や API Gateway + Lambda とのやりとりを極めて簡単に実装することができます。

Amplify CLI

Amplify CLI はコマンドラインで AWS のバックエンドを構築できるインターフェースです。AWS のバックエンドを構築するにはマネジメントコンソールから開発を行う方法や、Cloudformation など Infrastracture as a Code の仕組みを用いる必要がありました。 Amplify CLI を用いれば コマンドラインから対話形式にバックエンドを構築できます。Amplify CLI はバックエンドの構築だけでなく、バックエンドとの連携に必要な設定ファイルやソースコードを自動で 生成 してくれます。 Amplify Libraries と合わせて使用することで、極めて少ない工数で認証、ストレージ、API、プッシュ通知といった基盤を構築することができ、開発者は本質的な課題に集中することができます。  

Amplify Console

Amplify Console は SPA (シングルページアプリケーション) や静的サイトのホスティング、CI/CD の運用を自動化するマネージドサービスです。 Github や AWS CodeCommit のようなソースリポジトリと連携し、CI/CD の仕組みを数クリックで構築することができます。また、ブランチごとにテスト環境を自動で構築したり、特定の環境のみ Basic 認証を付与するといった、柔軟な開発フローの設計が可能なサービスです。 Amplify Console と銘打っていますが、静的サイトであれば Amplify を用いなくても使用できるサービスです。


今日やること

Pinpoint ではユーザーの行動分析や属性分析を行なったり、プッシュ通知以外にも Email や SMS などにも対応していますが、今回はプッシュ通知にフォーカスしてご紹介していきます。

最終的には、iOS アプリ, Android アプリに Pinpoint を組み込んで、各端末にプッシュ通知を送りたいと思います。  

このクラウドレシピ (ハンズオン記事) を無料でお試しいただけます »

毎月提供されるクラウドレシピのアップデート情報とともに、クレジットコードを受け取ることができます。 

1. 開発環境の前提条件

以下の環境が整っていることを確認してください。

  • Administrator 権限でアクセスできる AWS アカウントがあること
  • npm が利用可能であること
  • Android Studio が利用可能であること
  • Apple Developer Program のメンバーであること (iOS アプリを作成する場合)
  • MacOS を利用していること (iOS アプリを作成する場合)
  • XCode が利用可能であること (iOS アプリを作成する場合)
  • iOS デバイスを所持していること (iOS アプリを作成する場合)

2. Amplify CLI のセットアップ

ここから開発の手順に入っていきます。まずは、Amplify CLI をインストールします。バージョン差異を無くすため、今回は version 4.29.1 を使用します。
任意のディレクトリで下記のコマンドを実行します。  

$ npm install -g @aws-amplify/cli@4.29.1 

バージョン情報が表示されれば Amplify CLI のインストールは完了です。  

$ amplify --version
4.29.1

3. IAM ユーザーを作成する

次に、プロジェクトに Amplify を導入します。Amplify CLI が AWS のバックエンドを構築できるようにするために IAM ユーザーの作成と認証情報の設定を行います。  

$ amplify configure

コマンドを実行すると AWS コンソールのログイン画面に飛ぶのでログインします。すでにログイン済みの場合はマネージメントコンソールが開きます。

ログインしたらターミナルに戻り、Enter キーを押すと使用するリージョンや IAM ユーザー名を訊かれます。今回はリージョンは us-east-1 を選択します。他は全てデフォルトを指定します。
※ Amplify CLI 4.29.1 では ap-northeast-1 (Tokyo Region) を選択していても、analytics, notifications カテゴリを追加した際に us-west-2 に Pinpoint のプロジェクトが追加されるのでご注意ください。

https://console.aws.amazon.com/iam/~~ から始まるページが開かれます。
この時、すでに AWS のクレデンシャルをお持ちの方は、マネジメントコンソールで IAM ユーザーを作成する手順をスキップしてください。ターミナルで Enter キーを押下し、既にお持ちのクレデンシャルを入力してください。  

画面に沿って IAM ユーザーを作成します。特に選択肢や設定を変更する必要はなく、次に進めていきます。  

クリックすると拡大します

ユーザー作成完了後、アクセスキー ID とシークレットアクセスキーが発行されるので、これをメモします。シークレットアクセスキーは一度画面を閉じると、二度と確認できなくなってしまうので、忘れずにメモしてください。  

クリックすると拡大します

ターミナルに戻り、Enter キーを押します。アクセスキー ID、シークレットアクセスキー を訊かれるので、ブラウザの IAM ユーザー作成完了画面に表示されているものをコピーしてください。

Profile Name はデフォルト (default) のままでも OK です。もし、既に Profile の設定を持っている場合は、「pinpointapp」のようなわかりやすい名前をつけてください。

コンソール上に「Successfully set up the new user.」と表示されることを確認してください。これで、作成した IAM ユーザーの権限で Amplify CLI からコマンドを発行できるようになりました。  


4. Android プロジェクトの作成

次に Android プロジェクトを作成します。Android Studio を開き Start a new Android Studio project を選択します。 

クリックすると拡大します

Empty Activity を選択して次に進んでください。  

クリックすると拡大します

最後にプロジェクトに任意の名前をつけて、今回はそれぞれ下記を指定して、Finish で完了します。

  • Language
    • Kotlin
  • Minimum SDK
    • API 15

クリックすると拡大します


5. Android プロジェクトに Amplify を導入する

作成した Android プロジェクトのトップディレクトリに移動して amplify init コマンドを実行します。

$ amplify init

途中でいくつか質問をされます。基本的にはデフォルトの設定で OK です。「Enter a name for the environment」には任意の環境名 (dev や test など) を入力してください。

Amplify は、各 AWS サービスをそれぞれ個別に設定するのでなく、amplify init コマンドによって生成された src/aws-exports.js で設定を集中的、自動的に管理することをサポートしています。このファイルには、アプリケーションで利用する全てのリージョン情報やサービスのエンドポイント情報が含まれますが、amplify add コマンド等によって追加・更新された情報は amplify push コマンド実行時に自動的に反映されるため、開発者はファイルの中身を強く意識する必要はありません。

しばらくすると、init 処理が完了します。src/aws-exportsjs が存在することと、amplify status コマンドを発行し、以下のような表示になることを確認してください。  

$ amplify status

Current Environment: dev

| Category | Resource name | Operation | Provider plugin |
| -------- | ------------- | --------- | --------------- |

6. FCM API キーの発行

Android デバイスにプッシュ通知を送るために利用する FCM の API キーを取得します。

firebase にアクセスして、ログインします。
プロジェクトを追加」を選択して、名前を入力し、Google Anlytics アカウントを選択してプロジェクトを作成します。  

クリックすると拡大します

プロジェクトが作成できたら、左側のメニューから 歯車 > プロジェクトを設定 を選択します。  

クリックすると拡大します

全般タブの マイアプリ から Android アプリ を追加します。  

クリックすると拡大します

次に Android パッケージ名を入力します。
Android パッケージ名は app/src/main/AndroidManifest.xml から確認できます。

Android プロジェクトのトップディレクトリで下記を実行します。  

$ cat app/src/main/AndroidManifest.xml | grep 'package' | sed -E s/.+=\"\(.+\)\".+/\\1/ 

取得したパッケージ名を入力して、アプリを登録を選択します。 

クリックすると拡大します

google-services.json をダウンロードして、app 配下に移動します。  

クリックすると拡大します

最後に、上のタブの Cloud Messaging を選択して表示されるページのサーバーキーをメモしておいてください。  

クリックすると拡大します


7. Amplify CLI で Pinpoint を構築する

Amplify CLI を使って Analytics カテゴリを追加します。

Select an Analytics provider で Amazon Pinpoint を選択して、それ以外はデフォルトを選択していきます。

$ amplify add analytics
? Select an Analytics provider Amazon Pinpoint
? Provide your pinpoint resource name: test01
Adding analytics would add the Auth category to the project if not already added.
? Apps need authorization to send analytics events. Do you want to allow guests and unauthenticated users to send analytics events? (we recommend you allow this when getting started) Yes

Analytics カテゴリを追加したら amplify status で状況を確認すると、Auth と Analytics が反映されていないことがわかります。

この Auth カテゴリは Analytics カテゴリによって作成されたもので、 Pinpoint をアプリから呼び出す際に認証を行うために作られたものです。

AWS のサービスを利用するには IAM 認証を行う必要があるのですが、Web アプリやモバイルアプリの実装にクレデンシャルを埋め込むとアプリからクレデンシャルを簡単に取得できてしまうため、Amazon Cognito の Cognito Identity Pool の UnAuth Role の一時クレデンシャルを取得するために利用されます。

$ amplify status

Current Environment: dev

| Category      | Resource name           | Operation | Provider plugin   |
| ------------- | ----------------------- | --------- | ----------------- |
| Auth          | cognitoXXXXXXXX         | Create    | awscloudformation |
| Analytics     | bfpinpointsegments      | Create    | awscloudformation |

amplify push でまだバックエンドに反映されていない項目を反映します。選択肢はデフォルトで大丈夫です。

※ここで amplify push をしておかないと、Analytics と 次に追加する Notifications の Pinpoint のプロジェクトが異なってしまうのでご注意ください。  

$ amplify push

次に、Notifications カテゴリを追加します。
Choose the push notification channel to enable では FCM を選択して、ApiKey には先ほどメモしたサーバーキーを入力します。  

$ amplify add notifications
? Choose the push notification channel to enable. FCM
? ApiKey xxxxxxxxxxxx:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
✔ The FCM channel has been successfully enabled.

もう一度 amplify push で更新した項目をバックエンドに反映します。選択肢はデフォルトで大丈夫です。  

$ amplify push

コマンド完了後、マネージメントコンソールを確認します。

amplify notification console コマンドを実行するとマネージメントコンソールを開いてくれます。  

$ amplify notification console

マネージメントコンソールから Push Notification の項目を確認すると、FCM が Enabled になっていることが確認できます。  

クリックすると拡大します


8. Android アプリの Pinpoint への繋ぎこみ

執筆時点 (2020/09/15) では、Amplify Android は Notifications カテゴリに対応していないため、今回は Mobile SDK を利用していきます。

なお、Mobile SDK と Amplify Libraries の違いは、Mobile SDK はサービスベース、Amplify Libraries はユースケース (カテゴリ) ベースで利用できるという違いがあります。また、Amplify Libraries には Amplify DataStore というローカルデータストア機能があり、開発者はデータへの読み書きを Amplify DataStore を通して行うことで、Amplify DataStore が透過的に AWS AppSync API と同期するため、オンライン、オフラインを意識することなくデータへの読み書きを実装することができます。

まずは、プロジェクトのトップディレクトリの build.gradle に buildscript.dependencies の下記を追加します。  

gradle

buildscript {
    dependencies {
        classpath 'com.google.gms:google-services:4.3.3'
    }
}

app/build.gradle に dependencies と plugin に下記を追加します。  

gradle

dependencies {
    implementation 'com.google.android.gms:play-services-auth:15.0.1'
    implementation 'com.google.firebase:firebase-messaging:17.3.0'
    implementation 'com.amazonaws:aws-android-sdk-pinpoint:2.15.+'
    implementation ('com.amazonaws:aws-android-sdk-mobile-client:2.15.+@aar') { transitive = true }
}

apply plugin: 'com.google.gms.google-services'

app/src/main/AndroidManifest.xml の application の中に下記を追加します。  

<service
    android:name=".PushListenerService">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT"/>
    </intent-filter>
</service>

MainActivity.kt を下記のように実装します。  

kotlin

import android.content.Context
import android.os.Bundle
import android.support.design.widget.Snackbar
import android.support.v7.app.AppCompatActivity
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import com.amazonaws.mobile.client.AWSMobileClient
import com.amazonaws.mobile.client.Callback
import com.amazonaws.mobile.client.UserStateDetails
import com.amazonaws.mobile.config.AWSConfiguration
import com.amazonaws.mobileconnectors.pinpoint.PinpointConfiguration
import com.amazonaws.mobileconnectors.pinpoint.PinpointManager
import com.google.android.gms.tasks.OnCompleteListener
import com.google.firebase.iid.FirebaseInstanceId
import kotlinx.android.synthetic.main.activity_main.*


class MainActivity : AppCompatActivity() {

    companion object {
        private val TAG: String = this::class.java.simpleName
        var pinpointManager: PinpointManager? = null

        fun getPinpointManager(applicationContext: Context): PinpointManager? {
            if (pinpointManager == null) {
                val awsConfig = AWSConfiguration(applicationContext)
                AWSMobileClient.getInstance()
                    .initialize(applicationContext, awsConfig, object: Callback<UserStateDetails> {
                        override fun onResult(userStateDetails: UserStateDetails) {
                            Log.i("INIT", userStateDetails.userState.toString())
                        }

                        override fun onError(e: Exception?) {
                            Log.e("INIT", "Initialization error.", e)
                        }
                    })
                val pinpointConfig = PinpointConfiguration(applicationContext,
                    AWSMobileClient.getInstance(), awsConfig)
                pinpointManager = PinpointManager(pinpointConfig)
                FirebaseInstanceId.getInstance().instanceId
                    .addOnCompleteListener(OnCompleteListener { task ->
                        if (!task.isSuccessful) {
                            Log.w(TAG, "getInstanceId failed", task.exception)
                            return@OnCompleteListener
                        }
                        val token = task.result?.token
                        Log.d(TAG,
                            "Registering push notifications token: $token"
                        )
                        pinpointManager?.notificationClient?.registerDeviceToken(token)
                    })
            }
            return pinpointManager
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setSupportActionBar(toolbar)

        fab.setOnClickListener { view ->
            Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                .setAction("Action", null).show()
        }

        getPinpointManager(applicationContext)
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        // Inflate the menu; this adds items to the action bar if it is present.
        menuInflater.inflate(R.menu.menu_main, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        return when (item.itemId) {
            R.id.action_settings -> true
            else -> super.onOptionsItemSelected(item)
        }
    }
}

PushListenerService.kt を実装します。
MainActivity のあるパッケージで Kotlin File/Class を作成します。

クリックすると拡大します

PushListenerService Class を作成します。  

クリックすると拡大します

PushListenerService.kt を下記の様に実装します。  

kotlin

import android.content.Intent
import android.os.Bundle
import android.support.v4.content.LocalBroadcastManager
import android.util.Log
import com.amazonaws.mobileconnectors.pinpoint.targeting.notification.NotificationClient
import com.amazonaws.mobileconnectors.pinpoint.targeting.notification.NotificationClient.CampaignPushResult
import com.amazonaws.mobileconnectors.pinpoint.targeting.notification.NotificationClient.FCM_INTENT_ACTION
import com.amazonaws.mobileconnectors.pinpoint.targeting.notification.NotificationDetails
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage

class PushListenerService : FirebaseMessagingService() {
    companion object {
        private val TAG: String = this::class.java.simpleName
        private const val ACTION_PUSH_NOTIFICATION = "push-notification"
        private const val INTENT_SNS_NOTIFICATION_FROM = "from"
        private const val INTENT_SNS_NOTIFICATION_DATA = "data"
    }

    override fun onNewToken(token: String) {
        super.onNewToken(token)
        Log.d(TAG, "Registering push notifications token: $token")
        MainActivity.getPinpointManager(applicationContext)!!.notificationClient.registerDeviceToken(
            token
        )
    }

    override fun onMessageReceived(remoteMessage: RemoteMessage) {
        super.onMessageReceived(remoteMessage)
        Log.d(TAG, "Message: " + remoteMessage.data)
        val notificationClient = MainActivity.getPinpointManager(
            applicationContext
        )!!
            .notificationClient
        val notificationDetails = NotificationDetails.builder()
            .from(remoteMessage.from)
            .mapData(remoteMessage.data)
            .intentAction(FCM_INTENT_ACTION)
            .build()
        val pushResult = notificationClient.handleCampaignPush(notificationDetails)
        if (CampaignPushResult.NOT_HANDLED != pushResult) {
            /**
             * The push message was due to a Pinpoint campaign.
             * If the app was in the background, a local notification was added
             * in the notification center. If the app was in the foreground, an
             * event was recorded indicating the app was in the foreground,
             * for the demo, we will broadcast the notification to let the main
             * activity display it in a dialog.
             */
            if (CampaignPushResult.APP_IN_FOREGROUND == pushResult) {
                /* Create a message that will display the raw data of the campaign push in a dialog. */
                val dataMap = HashMap(remoteMessage.data)
                broadcast(remoteMessage.from, dataMap)
            }
            return
        }
    }

    private fun broadcast(from: String?, dataMap: HashMap<String, String>) {
        val intent = Intent(ACTION_PUSH_NOTIFICATION)
        intent.putExtra(NotificationClient.INTENT_SNS_NOTIFICATION_FROM, from)
        intent.putExtra(NotificationClient.INTENT_SNS_NOTIFICATION_DATA, dataMap)
        LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
    }

    /**
     * Helper method to extract push message from bundle.
     *
     * @param data bundle
     * @return message string from push notification
     */
    fun getMessage(data: Bundle): String? {
        return (data["data"] as HashMap<*, *>?).toString()
    }
}

9. Virtual Device の作成

それでは実際にプッシュ通知を端末に送ってみます。今回は Virtual Device を使いたいと思います。

AVD Manager を選択します。  

クリックすると拡大します

+ Create Virtual Device を選択します。  

クリックすると拡大します

Play Store に対応しているデバイスを選択します。  

クリックすると拡大します

API Version を選択します。特に指定はないので、ダウンロードできているバージョンを選択してください。

あとはデフォルトのまま進んで、Finish してください。  

クリックすると拡大します


10. Android アプリにプッシュの送信

Virtual Device を作成できたので、アプリを実行してみます。
先ほど作成した Virtual Device を選択して、アプリを実行します。  

クリックすると拡大します

こちらの様にアプリが開いたら成功です。
なお、今回の実装ではアプリがフォアグラウンドにいる時には通知を表示しない実装になっているので、ホームボタンを押してアプリをバックグラウンドに移動しておきます。  

クリックすると拡大します

では、 Pinpoint からプッシュ通知を送ってみたいと思います。
下記のコマンドでマネージドコンソールを開きます。  

amplify notifications console

Pinpoint では、ユーザーへの通知は「キャンペーン」という単位で管理します。
キャンペーンではいつ、誰に、どんなメッセージを送るかを設定して、キャンペーンの単位で効果を確認することができます。 

早速キャンペーンを作っていきたいと思います。Campaigns の画面に移動して、 Create a campaign を選択します。  

クリックすると拡大します

Pinpoint では、通知の A/B テストを行うこともできますが、今回は通常の Standard Campaign を選択します。

通知の対象はプッシュ通知なので、 Channel は Push Notifications を選択します。  

クリックすると拡大します

次に、誰に送るかを設定していきます。
今回は、プッシュ通知が送れる全デバイスを対象としたセグメントを作成します。

Create a Segment を選択して、Name を All Push Segments とします。
Add a filter から、Filter by channel を選択して、Choose a channel から PUSH を選択します。

セグメントを設定していくと右側の Segment estimate の項目が変化します。
現状だと、Android デバイス1台を登録しているので、1 endpoints と表示されるはずです。対象デバイスが 1 件以上あることを確認したら、次に行きます。  

クリックすると拡大します

次に、どんなメッセージを送るか を設定していきます。
Create a new push notification を選択して Title と Body に任意のメッセージを入力します。  

クリックすると拡大します

最後に、いつ送るかを設定していきます。
今回はすぐに確認したいので、Immediately を選択します。  

クリックすると拡大します

ここまでで通知の設定は完了です。最後に確認画面がでるので、内容が問題ないことを確認して Launch Campaign を選択します。 成功すると、Virtual Device に次の様に通知が届きます。  

クリックすると拡大します


11. iOS アプリの作成

ここからは、iOS アプリを作成していきます。

※ iOS アプリの作成には下記の環境が必要です。下記の環境がない場合は 後片付け までスキップしてください。

  • Apple Developer Program のメンバーであること
  • macOS を利用していること
  • XCode が利用可能であること
  • iOS デバイスを所持していること

ここまでで通知の設定は完了です。最後に確認画面がでるので、内容が問題ないことを確認して Launch Campaign を選択します。 成功すると、Virtual Device に次の様に通知が届きます。  

クリックすると拡大します

今回は Single View App を選択します。  

クリックすると拡大します

任意の Priduct Name を設定して、Apple Developer Program の Team を選択してください。
今回は、Langauge は Swift , User Interface は SwiftUI を使っていきます。  

クリックすると拡大します

プロジェクトを作成できたら、アプリの Target を選択していきます。
今回は User Interface に SwiftUI を選択したため、iOS 13.0 を Target にします。 

クリックすると拡大します

続いて、証明書の設定を行なっていきます。 プロジェクトを選択して、Signing & Capabilities を選択してください。

次に iPhone を Mac に接続します。
Automatically manage signing が有効になっていること、Provisioning Profile にエラーがないことを確認して、+ Capability を選択します。  

クリックすると拡大します

+ Capability から、Push Notification と Background Modes を選択してください。  

クリックすると拡大します

クリックすると拡大します

最後に、 Background Modes の Remote Notificatios を選択して完了です。  

クリックすると拡大します


12. iOS アプリに Amplify の設定の取り込み

次に iOS アプリに Amplify の設定を取り込んでいきます。

Amplify のバックエンドはすでに Android アプリの方で作成しているので、その設定を取り込んでいきます。

Amplify には amplify pull という機能があり、複数のクライアントアプリから同一のバックエンドを使いたい時に、 すでに作成済みのバックエンドの情報を取ってくることができます。

Which app are you working on? では、Android アプリで作成した Amplify プロジェクトを指定して、それ以外はデフォルトのままで問題ないです。  

$ amplify pull

For more information on AWS Profiles, see:
https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html

? Do you want to use an AWS profile? Yes
? Please choose the profile you want to use default
? Which app are you working on? xxxxxxxxxxxxx
Backend environment 'dev' found. Initializing...
? Choose your default editor: Visual Studio Code
? Choose the type of app that you're building ios

? Do you plan on modifying this backend? Yes
✔ Successfully pulled backend environment dev from the cloud.
✔ Channel is not setup for APNS 
✔ Channel information retrieved for FCM
✔ Channel is not setup for Email 
✔ Channel is not setup for SMS 


Successfully pulled backend environment dev from the cloud.
Run 'amplify pull' to sync upstream changes.

13. iOS アプリの Pinpoint への繋ぎこみ

執筆時点 (2020/09/15) では、Amplify iOS は Notifications カテゴリに対応していないため、今回は Mobile SDK を利用していきます。

まずは、iOS アプリのトップディレクトリで Podfile を作成します。  

TARGET_NAME=$(xcodebuild -showBuildSettings | grep 'TARGETNAME' | sed -E s/.+=\ +\(.+\)$/\\1/)
cat << EOF > Podfile
target :'${TARGET_NAME}' do
   use_frameworks!
   pod  'AWSPinpoint'
   pod  'AWSMobileClient'
end
EOF

作成した PodFile でインストールを実行します。  

pod install --repo-update

インストールが完了したら、XCode のプロジェクトをいったん閉じて、CocoaPods が作成した .xcworkspace の方で開き直します。

ワークスペースを開いたら、Amplify が作成した awsconfiguration.json を copy as needed にチェックをつけてプロジェクトに追加します。  

クリックすると拡大します

次に AppDelegate を実装します。 AppDelegate.swift を開き、 下記の import 文を追加します。  

import UserNotifications
import AWSPinpoint

メンバ−変数に AWSPinpoint インスタンスを定義します。  

var pinpoint: AWSPinpoint?

didFinishLaunchingWithOptions に下記を追加します。  

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        
        /* ここから */
        // Instantiate Pinpoint
        let pinpointConfiguration = AWSPinpointConfiguration
            .defaultPinpointConfiguration(launchOptions: launchOptions)
        // Set debug mode to use APNS sandbox, make sure to toggle for your production app
        pinpointConfiguration.debug = true
        pinpoint = AWSPinpoint(configuration: pinpointConfiguration)

        // Present the user with a request to authorize push notifications
        registerForPushNotifications()
        /* ここまで */
        
        return true
    }

registerForPushNotifications, getNotificationSettings メソッドを定義します。  

    // MARK: Push Notification methods

    func registerForPushNotifications() {
        UNUserNotificationCenter.current()
            .requestAuthorization(options: [.alert, .sound, .badge]) { [weak self] granted, _ in
                print("Permission granted: \(granted)")
                guard granted else { return }

                // Only get the notification settings if user has granted permissions
                self?.getNotificationSettings()
            }
    }

    func getNotificationSettings() {
        UNUserNotificationCenter.current().getNotificationSettings { settings in
            print("Notification settings: \(settings)")
            guard settings.authorizationStatus == .authorized else { return }

            DispatchQueue.main.async {
                // Register with Apple Push Notification service
                UIApplication.shared.registerForRemoteNotifications()
            }
        }
    }

プッシュ通知ライフサイクルのデリゲートメソッドを実装します。 

    // MARK: Remote Notifications Lifecycle
    func application(_: UIApplication,
                    didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
        let token = tokenParts.joined()
        print("Device Token: \(token)")

        // Register the device token with Pinpoint as the endpoint for this user
        pinpoint!.notificationManager
            .interceptDidRegisterForRemoteNotifications(withDeviceToken: deviceToken)
    }

    func application(_: UIApplication,
                    didFailToRegisterForRemoteNotificationsWithError error: Error) {
        print("Failed to register: \(error)")
    }

    func application(_ application: UIApplication,
                    didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                    fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult)
                        -> Void) {
        // Pass this remote notification event to pinpoint SDK to keep track of notifications produced by AWS Pinpoint campaigns.
        pinpoint!.notificationManager.interceptDidReceiveRemoteNotification(
            userInfo, fetchCompletionHandler: completionHandler
        )
    }

14. プッシュ証明書の作成

ここまでで iOS アプリの実装が完了しました。
次に、iOS でプッシュ通知を送るためのキーを作成していきます。

Apple Developer に移動します。  

Certificates, Identifiers & Profiles を選択します。  

クリックすると拡大します

左側のメニューから Keys を選択して、Keys + を選択して、キーを作成します。  

クリックすると拡大します

Key Name を入力して、 Apple Push Notifications service (APNs) を選択します。 

クリックすると拡大します

次に進んでキー (.p8) をダウンロードします。

ターミナルに戻り、iOS アプリのトップディレクトリに移動します。
ここでキー設定に必要な情報を一通り取得しておきます。  

Bundle Id

$ xcodebuild -showBuildSettings | grep 'TARGETNAME' | sed -E s/.+=\ +\(.+\)$/\\1/
bf-pinpoint-segments-ios

Team Id

$ xcodebuild -showBuildSettings | grep 'DEVELOPMENT_TEAM' | sed -E s/.+=\ +\(.+\)$/\\1/
xxxxxxxxxx

キー名

$ ls -l ~/Downloads/AuthKey_xxxxxxxxxx.p8 | sed -E s/.+AuthKey_\(.+\)\.p8$/\\1/
xxxxxxxxxx

キーのパス

$ ls -l ~/Downloads/AuthKey_xxxxxxxxxx.p8 | sed -E s/.+\ \(.+\)$/\\1/
/Users/uchiyo/Downloads/AuthKey_xxxxxxxxxx.p8

必要な情報を集めたら Amplify CLI から Pinpoint にキーを登録していきます。  

$ amplify add notifications                                                 
? Choose the push notification channel to enable. APNS
? Choose authentication method used for APNs Key
? The bundle id used for APNs Tokens:  bf-pinpoint-segments-ios
? The team id used for APNs Tokens:  xxxxxxxxxx
? The key id used for APNs Tokens:  xxxxxxxxxx
? The key file path (.p8):  /Users/uchiyo/Downloads/AuthKey_xxxxxxxxxx.p8
✔ The APNS channel has been successfully enabled.

15. iOS アプリにプッシュ通知の送信

ここまででアプリにプッシュ通知を送る準備ができました。

プッシュ通知を送るために、実機に作成したアプリをインストールしていきます。
Mac に接続している iPhone を選択して実行します。

クリックすると拡大します

この様にアプリが開いたら成功です。

Android の実装同様に、アプリがフォアグラウンドにいる時には通知を表示しない実装になっているので、ホームボタン、またはスワイプでアプリをバックグラウンドに移動しておきます。  

クリックすると拡大します

ターミナルに移動して、次のコマンドで Pinpoint のマネージメントコンソールに移動します。 

$ amplify console notifications

Campaigns の画面に移動して、Android に対して送信したキャンペーンを選択して、Actions から Duplicate を選択して、キャンペーンをコピーします。

Name を変更して、それ以外は変更せずにキャンペーンを作成してください。

クリックすると拡大します

端末にプッシュ通知が届いたら成功です ! 

クリックすると拡大します


16. 後片付け

バックエンドが残っている状態では、ごく僅かではありますが費用が発生します。アプリケーションの検証が終わったら、以下のコマンドを発行し、バックエンドを全て削除します。  

amplify delete

いくつか質問をされますが、全てデフォルトを選択します。


まとめ

いかがでしたか ? Amazon Pinpoint を使うことで簡単にプッシュ通知を送信することができました。

その他にも Pinpoint では、Email や SMS、Voice メール、Lmbada を使ったカスタムメッセージの配信を行なったり、ユーザーの行動情報や属性情報を収集して、ダッシュボードでのユーザー分析、属性情報を使ったセグメント配信など様々な機能があります。
ぜひ、 Pinpoint を試してみてください !


builders.flash メールメンバーへ登録することで
AWS のベストプラクティスを毎月無料でお試しいただけます

筆者プロフィール

内山 陽介 (うちやま ようすけ)
アマゾン ウェブ サービス ジャパン合同会社
ソリューションアーキテクト

普段はインターネットメディア系のお客様にアーキテクティングなどの技術的なご支援をしています。
前職ではプッシュ通知を送る人でしたが、AWS でもプッシュ通知を送っています。
Pinpoint の人。  

AWS のベストプラクティスを毎月無料でお試しいただけます

さらに最新記事・デベロッパー向けイベントを検索

下記の項目で絞り込む
1

AWS を無料でお試しいただけます

AWS 無料利用枠の詳細はこちら ≫
5 ステップでアカウント作成できます
無料サインアップ ≫
ご不明な点がおありですか?
日本担当チームへ相談する