April 2016

Volume 31 Number 4

Cutting Edge - モバイル アプリへのプッシュ通知

Dino Esposito | April 2016

Dino Espositoここ数回のコラムでは、主に、ソフトウェアの設計とアーキテクチャについて取り上げてきました。ソフトウェア システムをビルドする際にアーキテクチャが果たす役割を軽視することも、否定することもできませんが、すべてのアプリケーションは結局のところ個別の機能を積み上げたものに他なりません。これは、UX 主導型設計 (UXDD) の理念も同じです。ソフトウェア アプリケーションの成功は、ユーザーがそのアプリケーションを操作するときの全体的なエクスペリエンスで評価されるとするのが UXDD の理念です。ソフトウェア システムにモバイル フロント エンドを組み込むつもりならば、プッシュ通知のような機能を無視するわけにはいきません。

今回は、モバイル OS 自体とは無関係に、通知レイヤーをモバイル アプリ上に追加することについてまとめます。その過程で、Microsoft Azure Notification Hub プラットフォームのサービスも確認します。

プッシュ通知の概要

プッシュ通知とは、最初にアプリから明示的な要求を送ることなく、バックエンドから送られるメッセージをモバイル デバイスで受信することです。アプリのクライアント コンポーネントとサーバー コンポーネントの間のやり取りの大半は、フィードバックを求める明示的なアクション (通常はユーザーによる操作) によって発生します。ある意味、プッシュ通知は頼んでもいないフィードバックのようなもので、何かしら重要な情報が利用可能になった時点でバックエンドから送られるメッセージです。厳密に言えば、「頼んでもいない」というのは正確な表現ではありません。プッシュ通知を受け取るすべてのモバイル アプリは、利用可能なフィードをサブスクライブしなければなりません。ただし、一度サービスをサブスクライブしたら、「頼んでもいないのに」通知が到着します。

ユーザーに対するプッシュ通知の実質的効果は、ユーザーがアプリをアクティブに使用しているかどうかにかかわらず、関連する更新が配信されることにあります。たとえば、旅客機アプリをインストールすると、おそらく到着スケジュールや到着ゲートの変更についての更新を、すばやくタイムリーに受信できるようになります。ある時点になると、スマートフォンのビープ音が鳴ったり、本体が振動して、インターフェイス上のどこかに視覚的フィードバックが表示されます。ただし、それが正確にどこに表示されるかは、デバイスの基盤 OS のバージョンや、アプリの構成とユーザー設定によって細かく変わります。OS にもよりますが、プッシュ通知はトップ バーのアイコン、トースト メッセージ、バッジ更新などの形式をとります。

プッシュ通知に関して非常に重要な点は、その技術的側面がすべて厳密にプラットフォーム固有であることです。たとえば、アプリでプッシュ通知サービスをサブスクライブする方法は、iOS、Android、Windows Phone、ユニバーサル Windows プラットフォーム (UWP) の各アプリでそれぞれ異なります。同様に、各プラットフォームは接続されているデバイスにメッセージを配信するために独自のペイロードを使用しており、プラットフォームごとに異なるディスパッチ エンジンを構成する必要があります。実際の実装は大きく異なりますが、プッシュ通知システム (PNS) の全体アーキテクチャには非常に多くの共通点があります (図 1 参照)。

プラットフォーム固有のプッシュ通知システムの全体アーキテクチャ
図 1 プラットフォーム固有のプッシュ通知システムの全体アーキテクチャ

複数のプッシュ通知システムの操作

世の中にはさまざまなモバイル プラットフォームが存在し、それがプッシュ通知プラットフォームの多様性に反映されていても驚くことではありません。複数のプラットフォーム向けにモバイル アプリを開発する開発者は、複数の異なるプッシュ通知エンジンを理解し、それぞれに適切なサーバー環境をセットアップしなければなりません。開発中のアプリが 1 つのプラットフォームを対象にし、なおかつそのアプリを将来他のプラットフォームに移植する予定がない場合を除き、クロスプラットフォーム PNS を検討します (図 2 参照)。図 1 と比較すると、新しいアーキテクチャにはレイヤーが 1 つ多く追加され、プログラミングに 1 つのエントリ ポイントが提供されます。

クロスプラットフォーム プッシュ通知システムの全体アーキテクチャ
図 2 クロスプラットフォーム プッシュ通知システムの全体アーキテクチャ

モバイル アプリは汎用のプッシュ通知ハブに登録し、このアプリのバックエンドがメッセージをハブのキューに追加します。次に、ハブは、適切なプロトコルとペイロードを使用して、実際のメッセージを固有のモバイル プラットフォームに配信します。このようなクロスプラットフォーム モバイル通知システムを使用することで、1 つの API を使用して、メッセージを Android、iOS、Windows Phone の各デバイスにいっせいにプッシュできます。

Azure Notification Hub は、こうしたクロスプラットフォーム通知ハブの 1 つにすぎません。ここからは、そのしくみを見ていくことにします。

Azure Notification Hub

Azure Notification Hub を使用する最初の手順は、Azure プランのセットアップです。サービスは 3 つのレベルが用意され、最も低い無料レベルでは、通知を配信可能なデバイス数の上限 (500 台) と、送信可能な通知数の上限 (100 万件) が設定されます。詳細については、bit.ly/1Tbimew を参照してください。まずは、内部で通知ハブと名前空間を作成するための無料アカウントがあれば十分です。この情報は、アプリのバックエンドからデバイスのキュー メッセージに接続を確立するために使用します。

プッシュ通知を使用するうえで最も煩わしい作業は、クラスプラットフォーム ハブを扱うことではなく、通知を届ける各モバイル プラットフォームの要件を満たすことです。たとえば、iOS または Android に更新を配信するには、まずそれぞれのネイティブ PNS を使用してアプリを完全に構成しなくてはなりません。詳細については、bit.ly/1o15uv2 を参照してください。ご想像どおり、登録済みの iOS アプリしかプッシュ通知を受け取ることができせん。ネイティブ Apple Push Notification Service にアプリを登録するには、まず、アプリから送る通知を一意に識別する証明書を Apple から取得しなければなりません。Android の場合、証明書ではなく、Android Studio のインターフェイスから取得する API キーが必要です。UWP アプリでは、最初に Windows ストアへの登録が必要です。サポート対象のプラットフォームごとに、プラットフォーム固有の手順をすべて完了しておかなければなりません。入手した登録情報は Azure Notification Hub に入力する必要があります。この Azure Notification Hub がそれぞれのネイティブ PNS に対して開発者の代わりを務めます。

ハブへのアプリの登録

たとえば、Apple のシステムから通知を受け取る iOS アプリ用に、.p12 証明書ファイルと更新済みのプロビジョニング プロファイルが手元にあるとします。Azure Notification Hub を使用しないのであれば、ほぼ完了したようなものです。本当に中間ハブ システムを使用する必要があるのでしょうか。

問題は、どのプラットフォーム固有 PNS でも、開発者が一般的に要求されるタスクを実行するにはまだ多く作業が残されていることです。たとえば、接続するすべてのデバイスに対して、または特定のロケールでデバイスを使用しているユーザー グループに対して、単純にブロードキャストするようなタスクなどが通常必要になります。特にブロードキャストは、デバイスの数が増えるとスケーラビリティに大きな問題が生じる可能性があるため、簡単ではありません。そのため、中核となるプラットフォーム固有のサービスと、作成するコードを切り離す、スケーラブルな中間インフラストラクチャがあれば、大きなメリットを得られます。ただし、Azure Notification Hub を使用するには、プログラムから Apple .p12 証明書をアップロードし、アプリを Azure Notification Hub に登録する手順が必要になります。Xamarin.iOS を使用して iOS アプリを開発している場合は、ステップ バイ ステップの優れたチュートリアルがあります (bit.ly/1KaySJ3 参照)。

モバイル アプリでは主に 2 つのことを行います。まず、Azure 通知フィードをサブスクライブするコードを組み込みます。次に、受信通知を処理するコードを用意します。iOS アプリは、実際のプッシュ通知を Apple サービスから受け取るので、Apple サービスへのサブスクライブが必要です。これは、UIApplication.SharedApplication.RegisterForRemoteNotifications メソッドを使って行います。終了時に、このメソッドはアプリの RegisteredForRemoteNotifications というデリゲート クラスのオーバーライド メソッドを呼び出します。以下のコードで、Azure Notification Hub をサブスクライブします。このメソッドはデバイス トークンを OS から受け取ります。コードではこのトークンをハブに送るだけです。以下のように、Azure Notification Hub の識別はパスと接続文字列で行われます。

var hub = new SBNotificationHub(connectionString, hubPath);
hub.RegisterNativeAsync(deviceToken, null);

次に、実際に通知を受け取る際に、アプリのデリゲート クラスの別のオーバーライド メソッド ReceivedRemoteNotification が呼び出されます。このメソッドは、プッシュされたメッセージの実際のコンテンツを、(おそらく入れ子になった) 文字列の辞書の形式で OS から受け取ります。このメソッド オーバーライドの役割は、実際のメッセージを抽出して、バッジ、サウンド、アラートなど、アプリに役立つ機能を利用してそのメッセージを表示することです。

Azure Notification Hub キューへのメッセージの登録

ここまでに完了した作業は全体の半分にすぎません。残る課題は、Azure Notification Hub にメッセージを送り、そこから接続しているデバイスにメッセージを配信する方法です。つまり、ハブ接続の詳細を把握してユーザーにメッセージを渡すアプリ バックエンドが必要です。このようなアプリ バックエンドには、ASP.NET アプリケーションなど、任意の .NET アプリケーションを使用できます。ビジネス上の理由からモバイル アプリでプッシュ通知を受信する場合は、ビジネス ドメインで関連メッセージを生成することになります。メッセージの送信はソフトウェア トリガーによるものもあれば、図 3 に示すように管理ユーザーの操作によるものもあります。

ロケール固有のメッセージを Azure Notification Hub 経由で iOS アプリと Android アプリに送信する ASP.NET バックエンド
図 3 ロケール固有のメッセージを Azure Notification Hub 経由で iOS アプリと Android アプリに送信する ASP.NET バックエンド

プッシュ通知を ASP.NET バックエンドに組み込むのに必要なのは、Microsoft Azure NotificationHubs Nuget パッケージのみです。適切な接続文字列を作成する役割はコードが担います。接続文字列には、関連する Azure Service Bus URL エンドポイントと暗号化されたトークンに関する情報を格納します。Service Bus エンドポイントは、Azure サービスをセットアップするときに作成した名前空間の名前を保持します。これは、sb://your-ns.servicebus.windows.net のようになります。暗号化されたトークンは、ラベル "接続文字列" 下の名前空間の構成ページから読み取ります。有効なハブ インスタンスの作成に必要なコードは以下のようになります。

var hub = NotificationHubClient.CreateClientFromConnectionString(
  connString, hubName);

次に、ターゲットにするプラットフォームごとに適切なペイロードを作成します。このペイロードは固定パターンの後に続く JSON 文字列です。JSON 文字列は自由に作成できます。次の例の $ は実際に送信されるメッセージのプレースホルダーを表しています。

const string iosFormat = "{\"aps\":{\"alert\":\"$\"}}";
const string androidFormat = "{\"data\":{\"message\":\"$\"}}";
var iosAlert = iosFormat.Replace("$", actualMessage);
var androidAlert = androidFormat.Replace("$", actualMessage);

ペイロードを作成したら、それをハブに送信するのは簡単です。

var task1 = hub.SendAppleNativeNotificationAsync(iosAlert);
var outcome1 = task1.Result;
var task2 = hub.SendGcmNativeNotificationAsync(androidAlert);
var outcome2 = task2.Result;

このコードの結果として得られる変数は、NotificationOutcome 型のインスタンスで、処理結果について詳しい情報を返します。

テンプレートベース メッセージの送信

前のコード サンプルでは、プッシュ通知を送信する最も簡単な方法を示しました。つまり、単純なテキスト文字列を接続しているデバイスにそのままブロードキャストする方法です。それに加えて、目的のモバイル プラットフォームごとにメッセージを書式設定する必要があります。さらに一般的なシナリオに、テンプレートベースでのメッセージの送信があります。テンプレートベースのメッセージは文字列の辞書として Azure に送られ、Azure Notification Hub はアカウントが構成されたすべてのモバイル プラットフォームに対してそのメッセージが送信されるようにします。テンプレートベース メッセージを支える重要な考え方は、アプリから既定よりも充実した書式設定を利用できるようにすることです。たとえば、以下のようにさまざまなロケールに従ってユーザーに異なるメッセージを送信できます。

var locale = "EN";
var template = String.Format("{{\"aps\":{{\"alert\":\"$(News_{0})\"}}}}", locale);

上記の例は、News_XX というテンプレートベースの受信メッセージを Apple PNS に登録するテンプレートを示しています。ここで、XX はロケールの先頭 2 文字です。この例のテンプレートが優れている点は、アプリ バックエンドで 1 つの辞書の複数のエントリが送信されても、各デバイスが受信するのはそのデバイスで受信登録している 1 つのメッセージだけであることです。これは、Azure Notification Hub のような中間ハブを使用することでもたらされる追加サービスの 1 つにすぎません。ロケール固有のメッセージを送信するには、以下のようなコードが必要です。

var messages = new Dictionary<string, string>
{
  {"News_EN", "..."},
  {"News_ES", "..."},  {"News_PT", "..."},
  {"News_IT", "..."}
};var task = hub.SendTemplateNotificationAsync(messages);
var outcome = task.Result;

1 回のプッシュで、あらゆるプラットフォームのデバイスに通知が届き、しかも各ユーザーはそのユーザーがスマートフォン上で選択したロケールに合った通知のみを見ることが保証されます。ただし、この機能は iOS デバイスなどで利用可能なメッセージの自動ローカライズとは若干異なります。実は、iOS デバイスでは、プレースホルダーを使用したメッセージを送信でき、このプレースホルダーはローカライズ対象の文字列辞書のエントリにマップされます。その後、iOS は魔法のようにそのメッセージを自動で翻訳してから、アラートを呼び出します。どちらかと言えば、テンプレートメッセージは Azure の機能で、区分けしたグループに異なるメッセージを送ることが可能になります。さらにそのユーザーのグループの分け方は開発者が決めることができます。

定期メッセージ

Azure Notification Hub で利用できるもう 1 つの興味深い機能に、定期メッセージがあります。定期メッセージは Azure に配信される通知ですが、接続しているデバイスにそのメッセージが送信されるのは特定の時間に限られます。以下のように若干異なる API を使用するだけで、定期通知を送ることができます。

var notification = new TemplateNotification(messages);
var task = hub.ScheduleNotificationAsync(notification, new DateTime(...));
var outcome = task.Result;

定期通知には Standard レベルのサブスクリプションが必要で、無料テスト サブスクリプションでは利用できません。

デバイスに合ったアプリ

どのように Azure Notification Hub を経由でプッシュ通知を登録し送信するかという単なる技術的側面以外に、プッシュ通知が実際に難しい点は、ユーザーとの意思疎通を上手く行いながらプッシュ通知を使用することです。

昼も夜も単純な情報を通知することでユーザーを悩ませることは本意ではないでしょう。「プッシュ」通知であることを考えると、プッシュされるユーザーから本当の興味を引き出せることが望まれます。この点において、ユーザーのグループを分けることができるのは頼りになる優れた機能です。メッセージのサイズも重要です。どのようなメッセージでも、ツイート程度のサイズにしておくことをお勧めします。たとえば、iOS 8 まではプッシュ通知の最大サイズは 256 バイトでしたが、それ以降のシステムでは 2 KB まで引き上げられました。また、Android では 4 KB まで通知できます。最後に大切なこととして、ターゲットの OS ではこのような機能全体を簡単に拒否できるようになっています。最近のほとんどの OS では拒否できるようになっていますが、慎重に確認することをお勧めします。


Dino Esposito は『Microsoft .NET: Architecting Applications for the Enterprise』(Microsoft Press、2014年) および『Modern Web Applications』(Microsoft Press、2016年) の著者です。JetBrains の .NET および Android プラットフォームのテクニカル エバンジェリストでもあります。世界各国で開催される業界のイベントで頻繁に講演しており、software2cents@wordpress.com (英語) や Twitter (@despos、英語) でソフトウェアに関するビジョンを紹介しています。

この記事のレビューに協力してくれたマイクロソフト技術スタッフの Jon Arne Saeteras に心より感謝いたします。