- 投稿日:2019-07-28T13:35:06+09:00
AWS Pinpoint とAWS iOS SDKでセグメントを指定したPush配信
AWS Pinpointとは
顧客にパーソナライズされ、タイミング良く、関連性の高いコミュニケーションを複数チャネルを通して送り、顧客理解とエンゲージメントを高めるためのサービスです。
公式ドキュメント https://aws.amazon.com/jp/pinpoint/
モバイルPush配信、Eメール、SMSなどの配信を、属性やイベントなど適切な条件に一致するユーザにのみ配信し、その動向を解析して可視化することができます。
私のこと
- サーバサイドエンジニア
- 普段は主にAWSのサービスを使ってAPIなどのサーバサイドのアプリケーションを開発している
- モバイルPush配信は、Amazon SNSなどを利用して配信側のシステム構築を行ったことがある
- iOSアプリ開発は、プログラミング学習サイトでおみくじアプリを作ってローカルで動かした程度
今回試したこと
- AWS SDKを使って、アプリケーションサーバを介さずにiOSアプリからAWS Pinpointへデータを送る。
- 送られたデータを元にiOS端末にモバイルPush配信を行う。
- カスタム属性を設定を設定してセグメント配信を行う。
環境構築
今回は、AWS Amplifyという便利な物を使ってAWS PinpointとiOSアプリを連携していきます。
用意するもの
- Mac
- Xcode
- Cocoapods
- AWSアカウント
- IAMユーザのアクセスキーとシークレットキー
- AWS Amplify CLI
- Apple Developers programアカウント (登録しても使えるようになるまでに数日かかるので注意
)
- iPhone(実機)
iOSアプリケーションを作る
こちらのサンプルコードをダウンロードして使います。シンプルなノートアプリです。
https://github.com/aws-samples/aws-mobile-ios-notes-tutorial/Xcodeで開いて実行すると以下のようなアプリが起動します。
Amplify CLIインストールと設定
Amplify CLI はこちらを参考にしてインストールします。
https://aws-amplify.github.io/docs/$ npm install -g @aws-amplify/cli $ exec $SHELL -l $ amplify -v 1.8.2その後、以下を実行して、リージョン、ユーザ名、アクセスキー、シークレットキーを設定します。
$ amplify configure
Amplify CLIを使って AWS PinpointプロジェクトとiOSの設定ファイルを作成する
AWS Pinpointプロジェクトは AWSコンソールなどから単体で作成することもできますが、今回は、Amplifyを使って関連リソースを一緒に作成します。
こちらを参考に Pinpointを設定します。
https://aws-amplify.github.io/docs/ios/analytics
(pod install --repo-update まで実施していきます。)$ cd ./YOUR_PROJECT_FOLDER $ amplify add analytics $ amplify status | Category | Resource name | Operation | Provider plugin | | --------- | --------------- | --------- | ----------------- | | Auth | cognitoabcd0123 | Create | awscloudformation | | Analytics | yourprojectname | Create | awscloudformation | $ amplify pushamplify pushを実行すると Cloudformationが実行され、必要なAWSリソースが自動的に構築されます。Pinpointも作成されます。
どんなリソースが作成されているかは、Cloudformationを見るとわかります。
ここまでできたら、以下のようなjsonファイルが生成されています。
awsconfiguration.json{ "UserAgent": "aws-amplify/cli", "Version": "0.1.0", "IdentityManager": { "Default": {} }, "CredentialsProvider": { "CognitoIdentity": { "Default": { "PoolId": "リージョン:xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "Region": "リージョン" } } }, "PinpointAnalytics": { "Default": { "AppId": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "Region": "リージョン" } }, "PinpointTargeting": { "Default": { "Region": "リージョン" } } }このjsonファイルは、Xcodeを開いて、Info.plist があるディレクトリにimportしておきます。
Podfile には AWSPinpoint を追加します。
# Uncomment the next line to define a global platform for your project platform :ios, '11.0' target 'MyNotes' do # Comment the next line if you don't want to use dynamic frameworks use_frameworks! # Pods for MyNotes pod 'AWSPinpoint', '~> 2.10.0' endインストールを実行します。
pod install --repo-updatePush Notificationの配信準備(証明書の作成と登録)
雑に説明すると、
1. iOSのアプリ登録をして証明書作成、テスト端末登録、プロビジョニングファイルを作成する。
2. Push配信の証明書を作成してAWS Pinpointに登録する。
という手順です。注意する点は、iOSアプリケーションの証明書には 開発用(Development)と本番用(Distribution)がありますが、Push配信を確認するためには本番用が必要なので Distribution の方で作成する必要がある、ということです。
基本的にこちらの方法でやればできます。
Setting Up APNS for Push Notifications
、、が、iOSアプリ開発初心者にはわかりにくいかもしれません。iOSアプリ証明書とテスト端末登録、プロビジョニングファイル作成
iOSアプリケーションの証明書作成はこちらの記事[iPhone] iOS App IDs を登録する
がとてもわかりやすく、参考になりました。
(ありがとうございます!)
上記記事と異なる点は、今回は自分のテスト用iPhoneにPush配信するのが目的なので、Provisioning Profile作成時に、「App Store」ではなく、「Ad Hoc」を選択して作成します。Push配信証明書をAWS Pinpointに登録する
Push配信のために必要なp12ファイルを用意します。
Setting Up APNS for Push Notificationsの、 Step 2: Create an APNs SSL Certificate の通りにして作成します。作成されたp12ファイルを AWS Pinpointに登録します。
AWSマネージメントコンソール Pinpoint 左メニューの Push notificationをクリック
右に現れた画面のEditを押すと下記のような設定画面が現れるので、 Apple Push Notification service (APNs) を選択し、 SSL certificate (.p12 file) に、先ほどのp12ファイルを指定し、 Certificate password (optional) に、作成時に指定したパスワードを指定して保存すると完了です。
iOSアプリ修正とAWS Pinpointの配信設定
ここまでがとても大変ですが、ここから先はとても簡単です。
iOSアプリにAWS Pinpointへの登録処理を追加
GitHubのチュートリアルに習って、以下のクラスを新規Swiftファイルとして追加します。
AWSAnalyticsService.swiftimport Foundation import AWSCore import AWSPinpoint class AWSAnalyticsService : AnalyticsService { var pinpoint: AWSPinpoint? init() { let config = AWSPinpointConfiguration.defaultPinpointConfiguration(launchOptions: nil) pinpoint = AWSPinpoint(configuration: config) } func recordEvent(_ eventName: String, parameters: [String : String]?, metrics: [String : Double]?) { let event = pinpoint?.analyticsClient.createEvent(withEventType: eventName) if (parameters != nil) { for (key, value) in parameters! { event?.addAttribute(value, forKey: key) } } if (metrics != nil) { for (key, value) in metrics! { event?.addMetric(NSNumber(value: value), forKey: key) } } pinpoint?.analyticsClient.record(event!) pinpoint?.analyticsClient.submitEvents() } }これを AppDelegate から呼び出します。
チュートリアルのコードでは既に、 LocalAnalyticsService を使う記述があるのでそこを AWSAnalyticsService に変更します。AppDelegate.swiftimport UIKit import AWSPinpoint @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate { var window: UIWindow? var dataService: DataService? var analyticsService: AnalyticsService? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { : (省略) : // Initialize the analytics service //analyticsService = LocalAnalyticsService() analyticsService = AWSAnalyticsService() : (省略) : }こちらのクラスメソッドさんの記事 【2018年版】Amazon Pinpoint で iOS のサンプルアプリ(Swift)にセグメントプッシュを送る の ノートアプリの プッシュ 通知を有効にする に習って
- Push通知許諾
- デバイストークンをAWS Pinpointへ送信
を追加します。
クラスメソッドさんありがとうございます!セグメント配信
セグメント配信とは、ユーザ属性などを元に、一定条件のグループを作ってそのグループ対象に配信することです。
AWS Pinpointでは、事前にフィルター条件を指定してセグメント設定をしておき、Campaign という名称の配信条件設定を行い、Campaign の単位で配信していきます。
セグメント設定
左メニューのSegmentsを選ぶと右側にSegmentの画面が開きます。
属性を指定してセグメント配信
アプリのOSやバージョンなどは標準の属性として登録されるようになっています。
いくつかの端末やシミュレータでアプリを実行したあと、AWS Pinpointのコンソール画面を開き、AnalyticsのDemographicsを見ると、PlatformやAppVersion、Device modelなどがグラフで表示されています。
バージョン番号をアップしながら、いくつかの種類のシミュレータで実行したので複数のバージョンがグラフに表示されているのがわかります。
App version でセグメントを作成する場合のセグメント作成画面は以下のようにします。
このセグメントを以下のようなメッセージを配信することができます。
カスタム属性の登録
独自の属性を追加する場合は、以下のようにiOSアプリ側から属性を登録します。
事前にPinpoint側で設定などは不要です。func updateEndpointProfile(_ key: String, value: String) { let pinpointTargetingClient = pinpoint!.targetingClient pinpointTargetingClient.addAttribute([value], forKey: key) pinpointTargetingClient.updateEndpointProfile() }これを先ほど追加した AWSAnalyticsServiceクラスに追加し、ViewControllerのinsertNewObjectメソッドから呼び出してみます。
class MasterViewController: UITableViewController { : (省略) : @objc func insertNewObject(_ sender: Any) { analyticsService?.recordEvent("AddNewNote", parameters: nil, metrics: nil) // ★ここに追加。 行を追加したひとは AddNewNotes属性 を yes にする。 analyticsService?.updateEndpointProfile("AddNewNote", value: "yes") self.performSegue(withIdentifier: "showDetail", sender: sender) } : (省略) :これを実行し、アプリで+を押して行を追加した後、AWS Pinpointのコンソール画面のDemographicを見ると、Custom attributesとして AddNewNote が追加されています。
この属性を使ってセグメントを作成してみます。
セグメント作成画面で Filter by endpoint 選択します。
Chose an endpoint attribute に先ほどの AddNewNote が追加されています。
これを利用すれば、特定の機能を使ったユーザや、アプリ内の設定値の状況などをアプリケーションサーバを介することなく確認したり、その属性に対してPush通知を送ることができそうです。
イベント
今回のサンプルコードには既にイベントの登録が含まれています。
追加した AWSAnalyticsServiceクラスの recordEvent です。
実際にイベントを送信している場所は、 ViewController の各処理に実装されています。MasterViewController.swift: (省略) : @objc func insertNewObject(_ sender: Any) { // ここでイベントをAWS Pinpointに送信している analyticsService?.recordEvent("AddNewNote", parameters: nil, metrics: nil) self.performSegue(withIdentifier: "showDetail", sender: sender) } : (省略) :AWS Pinpointのコンソール画面の Eventを開くと利用状況がグラフで表示されます。
イベントからも何かできるはずですがまだ勉強不足です・・
感想
AWS Pinpointにはもっと多くの機能があります。
また、Pinpointから配信したデータをディープラーニングのサービスへデータ連携させたりすることも可能です。
今回は、ここまででのアウトプットですが、もっと、使いこなせるように色々ためしていきたいともいます。参考にさせていただいた記事
[iPhone] iOSアプリを登録、申請して公開するまで
【2018年版】Amazon Pinpoint で iOS のサンプルアプリ(Swift)にセグメントプッシュを送る
個人開発だと適当になりがちなiOSの証明書とアカウントを理解する
iOSアプリのプロビジョニング周りを図にしてみる
- 投稿日:2019-07-28T11:47:52+09:00
アプリ画面に流れる文字列(ティッカー、マーキー)を表示する
本記事は「デジタルサイネージアプリ「Sign!」(iOS版)とその実装機能の紹介」の子記事です。
目的
WKWebViewに流れる文字列(ティッカー、マーキーなどと呼ばれます)を表示するための、WKWebViewに読み込ませるHTML文字列を紹介します。
手順としては、まず、流れる文字列を実現するHTML文字列(String)を紹介し、その後、WKWebViewでその文字列を読み込む方法を紹介します。開発・実行環境
- 開発環境:macOS、Xcode(9~10)、Swift(4~5)
- 実行環境:iOS 11以上
コード
まずはHTML文字列を定義するコードを紹介します。ほとんどがCSSの定義です。コード中に、ポイントを解説しています。
ViewController.swiftlet TICKER_HTML = """ <html lang="ja"> <head> <style> /* フォント種類、サイズ、色、背景色などはお好きに設定してください */ * { /* すべてのHTMタグの余白を除きます */ margin: 0; padding: 0; } html { /* Safariには、画面の向き(縦横)が変化したときに、文字サイズを適度に調整する機能がありますが、それを抑制します */ -webkit-text-size-adjust: 100%; } .box { /* 流れる文字列を表示する範囲を画面いっぱいに設定します */ width: 100vw; height: 100vh; /* 流れる文字列を画面の縦方向中央に表示するための設定です */ display: flex; align-items: center; } /* 流れる文字列を収めるpタグです */ p { /* 文字列が画面幅より長くても途中で途切れないように、インラインボックスとして表示します */ display: inline-block; /* 文字列が途中で改行しないようにします */ white-space: nowrap; /* 画面の横幅分の余白を文字列の前に取ります。これにより、画面右端から文字列が流れ始めます */ padding-left: 100vw; /* 実際に文字列を流すためのアニメーション設定です。「%d」には、後ほど、流れる速さ(秒数)が入ります */ animation: marquee %ds linear infinite; } /* 実際に文字列を流すための設定です */ @keyframes marquee { from { /* 文字列の左端が画面の左端に揃っているところから流れ始めますが、先の設定で文字列の左端に100%のpaddingが入っていますので、あたかも画面の右端から文字列が現れるように見えます。なお「%%」は「%」のことです(Stringの中で%を表現するには%%と書きます) */ transform: translate(0%%); } to { /* 文字列の右端が画面の左端に来るまで流します */ transform: translate(-100%%); } } </style> </head> <body> <div class="box"> %@ /* この「%@」に、後ほど、流したい文字列が入ります */ </div> </body> </html> """以上のHTML文字列には、2箇所、値を入れるべき箇所があります。文字列が流れる秒数を指定するところ(%d)と、実際に流す文字列を指定するところ(%@)です。これらの値をString(format:)で指定しながら、WKWebViewで文字列をHTMLとして読み込むメソッド loadHTMLString() で読み込みます。
ViewController.swift@IBOutlet weak var webview: WKWebView! override func viewDidLoad() { super.viewDidLoad() self.webview.loadHTMLString( String(format: TICKER_HTML, 10, "この文字列が流れます") // 任意の秒数(ここでは10)と文字列を指定しています , baseURL: nil) }以上です。
- 投稿日:2019-07-28T11:27:02+09:00
iPadでアクションシートが開かない問題に対処する
本記事は「デジタルサイネージアプリ「Sign!」(iOS版)とその実装機能の紹介」の子記事です。
目的
iPadでアクションシート(.actionSheet)タイプのダイアログ(UIAlertController)が開かない場合の対処方法を説明します。
開発・実行環境
- 開発環境:macOS、Xcode(9~10)、Swift(4~5)
- 実行環境:iOS 11以上
コード
別の記事「ダイアログを表示する」で、ダイアログ(UIAlertController)を生成する際、「preferredStyle」に「.actionSheet」を選択できることを書きましたが、iPadでアクションシート(.actionSheet)を開くには、present()を実行する前に、1行加える必要があります。
ViewController.swiftoverride func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let dialog = UIAlertController(title: "Dialog Title", message: "Message is here", preferredStyle: .actionSheet) dialog.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) dialog.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) dialog.popoverPresentationController?.sourceView = self.view // この行を追加 self.present(dialog, animated: true, completion: nil) }iPadの場合、アクションシートはポップアップメニュー的に扱われますが、ポップアップする対象のビュー(.sourceView)を指定する必要があり、上記「この行」が、それに該当します。この例では、ViewControllerのviewを指定しています。
ちなみに、ダイアログは以下のようになります。
iPad画面の左上に表示されます。ステータスバーに食い込んでいるのはご愛嬌(サンプルプログラムのViewのレイアウト指定をサボりました)。
ちなみに、「Cancel」は表示されません。ダイアログの外をタップすると、「Cancel」が選択されたものとして処理されます。以上です。
- 投稿日:2019-07-28T11:20:53+09:00
ダイアログを表示する
本記事は「デジタルサイネージアプリ「Sign!」(iOS版)とその実装機能の紹介」の子記事です。
目的
複数の選択肢を持つダイアログ(UIAlertController)を開き、特定の選択肢を選んだときに、何らかの処理を行うためのコードを紹介します。
開発・実行環境
- 開発環境:macOS、Xcode(9~10)、Swift(4~5)
- 実行環境:iOS 11以上
コード
まず、コード例と、実際に表示されるダイアログのイメージを紹介します。
便宜上、ViewControllerのviewDidAppeared()でダイアログを生成し、表示させます(アプリを起動するといきなりダイアログが開きます)。ViewController.swiftoverride func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) // ダイアログ(AlertControllerのインスタンス)を生成します // titleには、ダイアログの表題として表示される文字列を指定します // messageには、ダイアログの説明として表示される文字列を指定します let dialog = UIAlertController(title: "Dialog Title", message: "Message is here", preferredStyle: .alert) // 選択肢(ボタン)を2つ(OKとCancel)追加します // titleには、選択肢として表示される文字列を指定します // styleには、通常は「.default」、キャンセルなど操作を無効にするものは「.cancel」、削除など注意して選択すべきものは「.destructive」を指定します dialog.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) dialog.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil)) // 生成したダイアログを実際に表示します self.present(dialog, animated: true, completion: nil) }なお、ダイアログを生成する行の「preferredStyle」に「.actionSheet」を指定すると、iPhoneの場合は、以下のようなメニューが画面下からせり上がるように表示されます。用途に合わせて使い分けてください。
ところで、このコードのままでは、「OK」や「Cancel」を選んでも、単にダイアログが閉じるだけで、何の処理もされません。選択肢をタップすると何らかの処理をする場合は、addAction()のhandlerをnilにせず、コードを書く必要があります。
ここでは、「OK」をタップすると、画面の色がランダムに変わるようにしてみます(実用性はありませんが)。
※「OK」に該当するaddAction()のみ記述しています。ViewController.swiftdialog.addAction(UIAlertAction(title: "OK", style: .default, handler: { action in self.view.backgroundColor = UIColor( red: CGFloat(Float.random(in: 0.0 ... 1.0)), green: CGFloat(Float.random(in: 0.0 ... 1.0)), blue: CGFloat(Float.random(in: 0.0 ... 1.0)), alpha: 1.0) }))以上です。
- 投稿日:2019-07-28T11:11:01+09:00
HTTP Webページを読み込めるようにする
本記事は「デジタルサイネージアプリ「Sign!」(iOS版)とその実装機能の紹介」の子記事です。
目的
WKWebViewで「HTTP」でアクセスするWebページを読み込むための設定を紹介します。
開発・実行環境
- 開発環境:macOS、Xcode(9~10)、Swift(4~5)
- 実行環境:iOS 11以上
設定
Xcodeのナビゲータエリア上部のフォルダアイコン(一番左)をクリックし、表示されるツリーの中のプロジェクトルート(一番上)をクリックします。さらに、エディタエリア上部の「Info」をクリックし、「Custom iOS Target Properties」のうちのいずれかの項目にマウスカーソルを合わせると表示される「+」マークをクリックします。
開いたプルダウンから「App Transport Security Settings」を選びます。
さらに、追加された「App Transport Security Settings」にマウスカーソルを合わせると表示される「+」マークをクリックします。
開いたプルダウンから「Allow Arbitrary Loads in Web Content」を選びます。
以上です。
- 投稿日:2019-07-28T00:15:27+09:00
大手銀行のAPIを使ったiOSアプリを作ってみる
三菱UFJ銀行が提供する銀行APIを使用した簡易iOSアプリを作成してみる。
公式ページが発行するAPIキーを用いて、「個人API 口座」のテスト用データをJSON形式で取得。各種データ項目を配列に格納する。以下、MUFG APIポータルサイト(公式)。
https://innovation.mufg.jp/api/実行環境
【Xcode】Version 10.3
【Swift】Version 5.0.1事前準備
1.MUFG APIポータルサイトから銀行APIページへ移動。アカウントを作成する。
2.「APIキー発行」タブより”APIキー発行登録”ボタンを押下。アプリ情報を登録。
3.発行されるクライアントIDを控えておく。(後ほど使用)実装
自身のクライアントIDやシーケンスNoをheadersとして定義する。
併せて、JSONデータを格納する為の配列を宣言。
※x-btmu-seq-noの例 : "x-btmu-seq-no": "20191231-aseyoshi12345678"ViewController.swiftlet headers = [ "x-ibm-client-id": "<自身のアプリのクライアントID>", "x-btmu-seq-no": "<YYYYMMDD-[16桁の任意の英数字]>", "accept": "application/json" ] var accountIdList: [String] = [] var accountNameList: [String] = [] var accountNameKanaList: [String] = [] var accountNoList: [String] = [] var accountTypeCodeList: [String] = [] var accountTypeDetailCodeList: [String] = [] var accountTypeNameList: [String] = [] var branchNameList: [String] = [] var branchNoList: [String] = [] var currencyCodeList: [String] = [] var primaryAccountFlagList: [String] = []更にviewDidLoad()の中を以下の通り実装。
ViewController.swiftlet request = NSMutableURLRequest(url: NSURL(string: "https://developer.api.bk.mufg.jp/btmu/retail/trial/v1/accounts/")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error) } else { let httpResponse = response as? HTTPURLResponse var lecturerData: Data = data! do { let json = try JSONSerialization.jsonObject(with: lecturerData, options: JSONSerialization.ReadingOptions.allowFragments) as! [String:Any] for (k,v) in json { let arr = v as! NSArray for dic in arr { let d = dic as! NSDictionary self.accountIdList.append(d["accountId"] as? String ?? "") self.accountNameList.append(d["accountName"] as? String ?? "") self.accountNameKanaList.append(d["accountNameKana"] as? String ?? "") self.accountNoList.append(d["accountNo"] as? String ?? "") self.accountTypeCodeList.append(d["accountTypeCode"] as? String ?? "") self.accountTypeDetailCodeList.append(d["accountTypeDetailCode"] as? String ?? "") self.accountTypeNameList.append(d["accountTypeName"] as? String ?? "") self.branchNameList.append(d["branchName"] as? String ?? "") self.branchNoList.append(d["branchNo"] as? String ?? "") self.currencyCodeList.append(d["currencyCode"] as? String ?? "") let Temp = d["primaryAccountFlag"] as? Int ?? 0 self.primaryAccountFlagList.append(String(Temp)) } } } catch { print(error) } } }) dataTask.resume()これで各配列にString型のテスト用データが格納されるようになった。
UICollectionViewに格納すれば以下のように口座情報を一覧化出来る。