チュートリアル:Azure Notification Hubs を使用して特定のユーザーにプッシュ通知を送信するTutorial: Send push notifications to specific users using Azure Notification Hubs

このチュートリアルでは、Azure Notification Hubs を使用して特定のデバイスで特定のアプリケーション ユーザーにプッシュ通知を送信する方法について説明します。This tutorial shows you how to use Azure Notification Hubs to send push notifications to a specific app user on a specific device. アプリ バックエンドからの登録管理に関するガイダンス トピックに示すように、ASP.NET WebAPI バックエンドを使用してクライアントを認証し、通知を生成します。An ASP.NET WebAPI backend is used to authenticate clients and to generate notifications, as shown in the guidance topic Registering from your app backend.

このチュートリアルでは、次の手順を実行します。In this tutorial, you take the following steps:

  • Web API プロジェクトを作成するCreate the WebAPI project
  • WebAPI バックエンドに対してクライアントを認証するAuthenticate clients to the WebAPI backend
  • WebAPI バックエンドを使用して通知に登録するRegister for notifications by using the WebAPI backend
  • WebAPI バックエンドから通知を送信するSend notifications from the WebAPI backend
  • 新しい WebAPI バックエンドを発行するPublish the new WebAPI backend
  • iOS アプリを変更するModify your iOS app
  • アプリケーションをテストするTest the application

前提条件Prerequisites

このチュートリアルでは、「Azure Notification Hubs を使用して iOS アプリにプッシュ通知を送信する」の説明のとおり通知ハブを作成し、構成したと想定しています。This tutorial assumes that you have created and configured your notification hub as described in Send push notifications to iOS apps using Azure Notification Hubs. また、「 安全なプッシュ (iOS) 」チュートリアルの前提条件でもあります。This tutorial is also the prerequisite to the Secure Push (iOS) tutorial. バックエンド サービスとして Mobile Apps を使用する場合は、「 iOS アプリへのプッシュ通知の追加」を参照してください。If you want to use Mobile Apps as your backend service, see the Mobile Apps Get Started with Push.

Web API プロジェクトを作成するCreate the WebAPI project

次のセクションでは、新しい ASP.NET WebAPI バックエンドの作成について説明します。The following sections discuss the creation of a new ASP.NET WebAPI backend. このプロセスには、主に次の 3 つの目的があります。This process has three main purposes:

  • クライアントの認証: クライアント要求を認証し、ユーザーを要求と関連付けるメッセージ ハンドラーを追加します。Authenticate clients: You add a message handler to authenticate client requests and associate the user with the request.
  • WebAPI バックエンドを使用した通知の登録: クライアント デバイスで通知を受信するための新しい登録を処理するコントローラーを追加します。Register for notifications by using the WebAPI backend: You add a controller to handle new registrations for a client device to receive notifications. 認証されたユーザー名はタグとして自動的に登録に追加されます。The authenticated username is automatically added to the registration as a tag.
  • クライアントへの通知の送信: ユーザーがタグに関連するデバイスやクライアントにセキュリティで保護されたプッシュ通知をトリガーできるコントローラーを追加します。Send notifications to clients: You add a controller to provide a way for users to trigger a secure push to devices and clients associated with the tag.

次の操作を実行して、新しい ASP.NET WebAPI バックエンドを作成します。Create the new ASP.NET WebAPI backend by doing the following actions:

重要

Visual Studio 2015 またはそれ以前のバージョンを使用している場合は、このチュートリアルを始める前に、Visual Studio 用の最新の NuGet パッケージ マネージャーがインストールされていることを確認してください。If you are using Visual Studio 2015 or earlier, before starting this tutorial, ensure that you have installed the latest version of NuGet Package Manager for Visual Studio.

確認するには、Visual Studio を起動します。To check, start Visual Studio. [ツール] メニューの [拡張機能と更新プログラム] を選びます。On the Tools menu, select Extensions and Updates. お使いの Visual Studio に対応した NuGet パッケージ マネージャー を探し、バージョンが最新であることを確認します。Search for NuGet Package Manager in your version of Visual Studio, and make sure you have the latest version. 最新バージョンでない場合は、アンインストールして、NuGet パッケージ マネージャーを再インストールしてください。If your version is not the latest version, uninstall it, and then reinstall the NuGet Package Manager.

Visual Studio 用 NuGet パッケージ マネージャー パッケージが強調表示された [拡張機能と更新プログラム] ダイアログ ボックスのスクリーンショット。

注意

Web サイトのデプロイ用に Visual Studio Azure SDK がインストールされていることを確認してください。Make sure you have installed the Visual Studio Azure SDK for website deployment.

  1. Visual Studio または Visual Studio Express を起動します。Start Visual Studio or Visual Studio Express.

  2. [サーバー エクスプローラー] を選択し、Azure アカウントにサインインします。Select Server Explorer, and sign in to your Azure account. アカウントの Web サイト リソースを作成するには、サインインする必要があります。To create the web site resources on your account, you must be signed in.

  3. Visual Studio で Visual Studio ソリューションを右クリックし、 [追加] をポイントして、 [新しいプロジェクト] をクリックします。In Visual Studio, right-click Visual Studio solution, point to Add, and click New Project.

  4. [Visual C#] を展開し、 [Web] を選択して [ASP.NET Web アプリケーション] をクリックします。Expand Visual C#, select Web, and click ASP.NET Web Application.

  5. [名前] ボックスに「AppBackend」と入力し、 [OK] を選択します。In the Name box, type AppBackend, and then select OK.

    [新しいプロジェクト] ウィンドウ

  6. [新しい ASP.NET プロジェクト] ウィンドウで、 [Web API] チェック ボックスをオンにし、 [OK] を選択します。In the New ASP.NET Project window, select the Web API check box, and then select OK.

    [新しい ASP.NET プロジェクト] ウィンドウ

  7. [Microsoft Azure Web App の構成] ウィンドウで、サブスクリプションを選択し、 [App Service プラン] の一覧で次のいずれかの操作を実行します。In the Configure Microsoft Azure Web App window, select a subscription and then, in the App Service plan list, do either of the following actions:

    • 作成済みの App Service プランを選択します。Select an app service plan that you've already created.
    • [新しい App Service プランの作成] を選択し、App Service プランを作成します。Select Create a new app service plan, and then create one.

    このチュートリアルではデータベースは必要ありません。You do not need a database for this tutorial. App Service プランを選択したら、 [OK] を選択して、プロジェクトを作成します。After you have selected your app service plan, select OK to create the project.

    [Microsoft Azure Web App の構成] ウィンドウ

    App Service プランを構成するためのこのページが表示されない場合、チュートリアルを続行してください。If you don't see this page for configure app service plan, continue with the tutorial. これはアプリの発行時に後で構成できます。You can configure it while publishing the app later.

WebAPI バックエンドに対してクライアントを認証するAuthenticate clients to the WebAPI backend

このセクションでは、新しいバックエンドに対して AuthenticationTestHandler という名前の新しいメッセージ ハンドラー クラスを作成します。In this section, you create a new message-handler class named AuthenticationTestHandler for the new backend. このクラスは DelegatingHandler から派生し、メッセージ ハンドラーとして追加されるため、バックエンドに送信されるすべての要求を処理できます。This class is derived from DelegatingHandler and added as a message handler so that it can process all requests that come into the backend.

  1. ソリューション エクスプローラーで、AppBackend プロジェクトを右クリックし、 [追加][クラス] の順に選択します。In Solution Explorer, right-click the AppBackend project, select Add, and then select Class.

  2. 新しいクラスに「AuthenticationTestHandler.cs」という名前を付け、 [追加] を選択して、クラスを生成します。Name the new class AuthenticationTestHandler.cs, and then select Add to generate the class. 説明を簡単にするために、このクラスでは、基本認証 を使用してユーザーを認証します。This class authenticates users by using Basic Authentication for simplicity. 実際のアプリでは、任意の認証スキームを使用できます。Your app can use any authentication scheme.

  3. AuthenticationTestHandler.cs に次の using ステートメントを追加します。In AuthenticationTestHandler.cs, add the following using statements:

    using System.Net.Http;
    using System.Threading;
    using System.Security.Principal;
    using System.Net;
    using System.Text;
    using System.Threading.Tasks;
    
  4. AuthenticationTestHandler.cs で、AuthenticationTestHandler クラス定義を次のコードに置き換えます。In AuthenticationTestHandler.cs, replace the AuthenticationTestHandler class definition with the following code:

    このハンドラーは、次の 3 つの条件を満たす場合に要求を承認します。The handler authorizes the request when the following three conditions are true:

    • 要求に Authorization ヘッダーが含まれている。The request includes an Authorization header.
    • 要求に 基本 認証が使用されている。The request uses basic authentication.
    • ユーザー名の文字列とパスワードの文字列が同じである。The user name string and the password string are the same string.

    それ以外の場合、要求は拒否されます。Otherwise, the request is rejected. この認証は、認証と認可の正規のアプローチではありません。This authentication is not a true authentication and authorization approach. このチュートリアルのための単なる例にすぎません。It is only a simple example for this tutorial.

    要求メッセージが認証され、AuthenticationTestHandler によって承認される場合、基本認証ユーザーは HttpContext の現在の要求に添付されます。If the request message is authenticated and authorized by AuthenticationTestHandler, the basic authentication user is attached to the current request on HttpContext. HttpContext のユーザー情報は、後で別のコントローラー (RegisterController) で使用され、通知登録の要求にタグを追加します。User information in HttpContext will be used by another controller (RegisterController) later to add a tag to the notification registration request.

    public class AuthenticationTestHandler : DelegatingHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
        {
            var authorizationHeader = request.Headers.GetValues("Authorization").First();
    
            if (authorizationHeader != null && authorizationHeader
                .StartsWith("Basic ", StringComparison.InvariantCultureIgnoreCase))
            {
                string authorizationUserAndPwdBase64 =
                    authorizationHeader.Substring("Basic ".Length);
                string authorizationUserAndPwd = Encoding.Default
                    .GetString(Convert.FromBase64String(authorizationUserAndPwdBase64));
                string user = authorizationUserAndPwd.Split(':')[0];
                string password = authorizationUserAndPwd.Split(':')[1];
    
                if (VerifyUserAndPwd(user, password))
                {
                    // Attach the new principal object to the current HttpContext object
                    HttpContext.Current.User =
                        new GenericPrincipal(new GenericIdentity(user), new string[0]);
                    System.Threading.Thread.CurrentPrincipal =
                        System.Web.HttpContext.Current.User;
                }
                else return Unauthorized();
            }
            else return Unauthorized();
    
            return base.SendAsync(request, cancellationToken);
        }
    
        private bool VerifyUserAndPwd(string user, string password)
        {
            // This is not a real authentication scheme.
            return user == password;
        }
    
        private Task<HttpResponseMessage> Unauthorized()
        {
            var response = new HttpResponseMessage(HttpStatusCode.Forbidden);
            var tsc = new TaskCompletionSource<HttpResponseMessage>();
            tsc.SetResult(response);
            return tsc.Task;
        }
    }
    

    注意

    セキュリティに関する注意: AuthenticationTestHandler クラスは、本当の認証を提供するわけではありません。Security note: The AuthenticationTestHandler class does not provide true authentication. 基本認証を模倣するためだけに使用されるため、安全ではありません。It is used only to mimic basic authentication and is not secure. 実稼働のアプリケーションとサービスでは、セキュリティで保護された認証メカニズムを実装する必要があります。You must implement a secure authentication mechanism in your production applications and services.

  5. メッセージ ハンドラーを登録するには、App_Start/WebApiConfig.cs クラスの Register メソッドの末尾に次のコードを追加します。To register the message handler, add the following code at the end of the Register method in the App_Start/WebApiConfig.cs class:

    config.MessageHandlers.Add(new AuthenticationTestHandler());
    
  6. 変更を保存します。Save your changes.

WebAPI バックエンドを使用して通知に登録するRegister for notifications by using the WebAPI backend

このセクションでは、Notification Hubs のクライアント ライブラリを使用して、通知用にユーザーとデバイスを登録する要求を処理する新しいコントローラーを WebAPI バックエンドに追加します。In this section, you add a new controller to the WebAPI backend to handle requests to register a user and a device for notifications by using the client library for notification hubs. コントローラーでは、AuthenticationTestHandler で認証され、HttpContext に添付されたユーザーのユーザー タグを追加します。The controller adds a user tag for the user that was authenticated and attached to HttpContext by AuthenticationTestHandler. タグは、"username:<actual username>" という形式の文字列になります。The tag has the string format, "username:<actual username>".

  1. ソリューション エクスプローラーで AppBackend プロジェクトを右クリックし、 [NuGet パッケージの管理] を選択します。In Solution Explorer, right-click the AppBackend project and then select Manage NuGet Packages.

  2. 左側のウィンドウにある [オンライン] を選択し、 [検索] ボックスに「Microsoft.Azure.NotificationHubs」と入力します。In the left pane, select Online and then, in the Search box, type Microsoft.Azure.NotificationHubs.

  3. 結果の一覧で、 [Microsoft Azure Notification Hubs] を選択してから、 [インストール] を選択します。In the results list, select Microsoft Azure Notification Hubs, and then select Install. インストールが完了したら、NuGet パッケージ マネージャーのウィンドウを閉じます。Complete the installation, and then close the NuGet Package Manager window.

    この操作によって、Microsoft.Azure.Notification Hubs NuGet パッケージを使用して Azure Notification Hubs SDK に参照が追加されます。This action adds a reference to the Azure Notification Hubs SDK by using the Microsoft.Azure.Notification Hubs NuGet package.

  4. 通知の送信に使用する通知ハブとの接続を表す新しいクラス ファイルを作成します。Create a new class file that represents the connection with the notification hub that's used to send notifications. ソリューション エクスプローラーで、Models フォルダーを右クリックし、 [追加][クラス] の順に選択します。In Solution Explorer, right-click the Models folder, select Add, and then select Class. 新しいクラスに「Notifications.cs」という名前を付け、 [追加] を選択して、クラスを生成します。Name the new class Notifications.cs, and then select Add to generate the class.

    [新しい項目の追加] ウィンドウ

  5. Notifications.cs で、ファイルの先頭に次の using ステートメントを追加します。In Notifications.cs, add the following using statement at the top of the file:

    using Microsoft.Azure.NotificationHubs;
    
  6. Notifications クラス定義を以下のコードに置き換えます。2 つのプレースホルダーは、通知ハブに対する (フル アクセス権を持つ) 接続文字列と、ハブ名 (Azure Portalで確認できます) に置き換えます。Replace the Notifications class definition with the following code, and replace the two placeholders with the connection string (with full access) for your notification hub and the hub name (available at Azure portal):

    public class Notifications
    {
        public static Notifications Instance = new Notifications();
    
        public NotificationHubClient Hub { get; set; }
    
        private Notifications() {
            Hub = NotificationHubClient.CreateClientFromConnectionString("<your hub's DefaultFullSharedAccessSignature>",
                                                                            "<hub name>");
        }
    }
    

    重要

    先に進む前に、ハブの 名前DefaultFullSharedAccessSignature を入力します。Enter the name and the DefaultFullSharedAccessSignature of your hub before proceeding further.

  7. 次に、RegisterController という名前の新しいコントローラーを作成します。Next, create a new controller named RegisterController. ソリューション エクスプローラーで、Controllers フォルダーを右クリックし、 [追加][コントローラー] の順に選択します。In Solution Explorer, right-click the Controllers folder, select Add, and then select Controller.

  8. [Web API 2 コントローラー - 空][追加] の順に選択します。Select Web API 2 Controller - Empty, and then select Add.

    [スキャフォールディングの追加] ウィンドウ

  9. [コントローラー名] ボックスに「RegisterController」と入力して、新しいクラスに名前を付け、 [追加] を選択します。In the Controller name box, type RegisterController to name the new class, and then select Add.

    [コントローラーの追加] ウィンドウ

  10. RegisterController.cs に次の using ステートメントを追加します。In RegisterController.cs, add the following using statements:

    using Microsoft.Azure.NotificationHubs;
    using Microsoft.Azure.NotificationHubs.Messaging;
    using AppBackend.Models;
    using System.Threading.Tasks;
    using System.Web;
    
  11. RegisterController クラス定義内で、次のコードを追加します。Add the following code inside the RegisterController class definition. このコードでは、HttpContext に添付されるユーザーのユーザー タグを追加します。In this code, you add a user tag for the user that's attached to HttpContext. ユーザーは認証され、追加したメッセージ フィルター AuthenticationTestHandler で HttpContext に添付されます。The user was authenticated and attached to HttpContext by the message filter that you added, AuthenticationTestHandler. また、要求されたタグを登録する権限をユーザーが持っていることを確認するコードをオプションで追加することもできます。You can also add optional checks to verify that the user has rights to register for the requested tags.

    private NotificationHubClient hub;
    
    public RegisterController()
    {
        hub = Notifications.Instance.Hub;
    }
    
    public class DeviceRegistration
    {
        public string Platform { get; set; }
        public string Handle { get; set; }
        public string[] Tags { get; set; }
    }
    
    // POST api/register
    // This creates a registration id
    public async Task<string> Post(string handle = null)
    {
        string newRegistrationId = null;
    
        // make sure there are no existing registrations for this push handle (used for iOS and Android)
        if (handle != null)
        {
            var registrations = await hub.GetRegistrationsByChannelAsync(handle, 100);
    
            foreach (RegistrationDescription registration in registrations)
            {
                if (newRegistrationId == null)
                {
                    newRegistrationId = registration.RegistrationId;
                }
                else
                {
                    await hub.DeleteRegistrationAsync(registration);
                }
            }
        }
    
        if (newRegistrationId == null) 
            newRegistrationId = await hub.CreateRegistrationIdAsync();
    
        return newRegistrationId;
    }
    
    // PUT api/register/5
    // This creates or updates a registration (with provided channelURI) at the specified id
    public async Task<HttpResponseMessage> Put(string id, DeviceRegistration deviceUpdate)
    {
        RegistrationDescription registration = null;
        switch (deviceUpdate.Platform)
        {
            case "mpns":
                registration = new MpnsRegistrationDescription(deviceUpdate.Handle);
                break;
            case "wns":
                registration = new WindowsRegistrationDescription(deviceUpdate.Handle);
                break;
            case "apns":
                registration = new AppleRegistrationDescription(deviceUpdate.Handle);
                break;
            case "fcm":
                registration = new FcmRegistrationDescription(deviceUpdate.Handle);
                break;
            default:
                throw new HttpResponseException(HttpStatusCode.BadRequest);
        }
    
        registration.RegistrationId = id;
        var username = HttpContext.Current.User.Identity.Name;
    
        // add check if user is allowed to add these tags
        registration.Tags = new HashSet<string>(deviceUpdate.Tags);
        registration.Tags.Add("username:" + username);
    
        try
        {
            await hub.CreateOrUpdateRegistrationAsync(registration);
        }
        catch (MessagingException e)
        {
            ReturnGoneIfHubResponseIsGone(e);
        }
    
        return Request.CreateResponse(HttpStatusCode.OK);
    }
    
    // DELETE api/register/5
    public async Task<HttpResponseMessage> Delete(string id)
    {
        await hub.DeleteRegistrationAsync(id);
        return Request.CreateResponse(HttpStatusCode.OK);
    }
    
    private static void ReturnGoneIfHubResponseIsGone(MessagingException e)
    {
        var webex = e.InnerException as WebException;
        if (webex.Status == WebExceptionStatus.ProtocolError)
        {
            var response = (HttpWebResponse)webex.Response;
            if (response.StatusCode == HttpStatusCode.Gone)
                throw new HttpRequestException(HttpStatusCode.Gone.ToString());
        }
    }
    
  12. 変更を保存します。Save your changes.

WebAPI バックエンドから通知を送信するSend notifications from the WebAPI backend

このセクションでは、クライアント デバイスが通知を送信する方法を公開する新しいコントローラーを追加します。In this section, you add a new controller that exposes a way for client devices to send a notification. この通知は、ASP.NET WebAPI バックエンドの Azure Notification Hubs .NET ライブラリを使用する username タグに基づいています。The notification is based on the username tag that uses Azure Notification Hubs .NET Library in the ASP.NET WebAPI backend.

  1. 前のセクションで RegisterController を作成したときと同じ方法で、NotificationsController という別の新しいコントローラーを作成します。Create another new controller named NotificationsController the same way you created RegisterController in the previous section.

  2. NotificationsController.cs に次の using ステートメントを追加します。In NotificationsController.cs, add the following using statements:

    using AppBackend.Models;
    using System.Threading.Tasks;
    using System.Web;
    
  3. NotificationsController クラスに次のメソッドを追加します。Add the following method to the NotificationsController class:

    このコードでは、プラットフォーム通知サービス (PNS) の pns パラメーターに基づく通知の種類を送信します。This code sends a notification type that's based on the Platform Notification Service (PNS) pns parameter. to_tag の値はメッセージの ユーザー名 タグを設定するために使用します。The value of to_tag is used to set the username tag on the message. このタグは、アクティブな通知ハブ登録のユーザー名のタグと一致する必要があります。This tag must match a username tag of an active notification hub registration. 通知メッセージは、POST 要求の本文からプルされ、ターゲット PNS に合わせた形式に設定されます。The notification message is pulled from the body of the POST request and formatted for the target PNS.

    サポートされているデバイスで通知の受信に使用される PNS に応じて、各種形式で通知がサポートされています。Depending on the PNS that your supported devices use to receive notifications, the notifications are supported by a variety of formats. たとえば Windows デバイスの場合、別の PNS で直接はサポートされていない WNS によるトースト通知を使用することもできます。For example, on Windows devices, you might use a toast notification with WNS that isn't directly supported by another PNS. このような場合、バックエンドが、通知を、サポートを計画しているデバイスの PNS でサポートされている形式に設定する必要があります。In such an instance, your backend needs to format the notification into a supported notification for the PNS of devices you plan to support. その後、NotificationHubClient クラスで適切な送信 API を使用します。Then use the appropriate send API on the NotificationHubClient class.

    public async Task<HttpResponseMessage> Post(string pns, [FromBody]string message, string to_tag)
    {
        var user = HttpContext.Current.User.Identity.Name;
        string[] userTag = new string[2];
        userTag[0] = "username:" + to_tag;
        userTag[1] = "from:" + user;
    
        Microsoft.Azure.NotificationHubs.NotificationOutcome outcome = null;
        HttpStatusCode ret = HttpStatusCode.InternalServerError;
    
        switch (pns.ToLower())
        {
            case "wns":
                // Windows 8.1 / Windows Phone 8.1
                var toast = @"<toast><visual><binding template=""ToastText01""><text id=""1"">" + 
                            "From " + user + ": " + message + "</text></binding></visual></toast>";
                outcome = await Notifications.Instance.Hub.SendWindowsNativeNotificationAsync(toast, userTag);
                break;
            case "apns":
                // iOS
                var alert = "{\"aps\":{\"alert\":\"" + "From " + user + ": " + message + "\"}}";
                outcome = await Notifications.Instance.Hub.SendAppleNativeNotificationAsync(alert, userTag);
                break;
            case "fcm":
                // Android
                var notif = "{ \"data\" : {\"message\":\"" + "From " + user + ": " + message + "\"}}";
                outcome = await Notifications.Instance.Hub.SendFcmNativeNotificationAsync(notif, userTag);
                break;
        }
    
        if (outcome != null)
        {
            if (!((outcome.State == Microsoft.Azure.NotificationHubs.NotificationOutcomeState.Abandoned) ||
                (outcome.State == Microsoft.Azure.NotificationHubs.NotificationOutcomeState.Unknown)))
            {
                ret = HttpStatusCode.OK;
            }
        }
    
        return Request.CreateResponse(ret);
    }
    
  4. アプリケーションを実行し、ここまでの作業に問題がないことを確認するために、F5 キーを選択します。To run the application and ensure the accuracy of your work so far, select the F5 key. アプリにより、Web ブラウザーが開き、ASP.NET ホーム ページに表示されます。The app opens a web browser, and it is displayed on the ASP.NET home page.

新しい WebAPI バックエンドを発行するPublish the new WebAPI backend

次に、このアプリを Azure の Web サイトにデプロイして、すべてのデバイスからアクセスできるようにします。Next, you deploy the app to an Azure website to make it accessible from all devices.

  1. AppBackend プロジェクトを右クリックして [発行] を選択します。Right-click the AppBackend project, and then select Publish.

  2. 発行先として [Microsoft Azure App Service] を選択し、[発行] を選択します。Select Microsoft Azure App Service as your publish target, and then select **Publish. [App Service の作成] ウィンドウが開きます。The Create App Service window opens. このウィンドウでは、Azure で ASP.NET Web アプリを実行するために必要なすべての Azure リソースを作成できます。Here you can create all the necessary Azure resources to run the ASP.NET web app in Azure.

    [Microsoft Azure App Service] タイル

  3. [App Service の作成] ウィンドウで、Azure アカウントを選択します。In the Create App Service window, select your Azure account. [変更の種類] > [Web アプリ] の順に選択します。Select Change Type > Web App. 既定の Web アプリ名 をそのまま保持し、 [サブスクリプション][リソース グループ][App Service プラン] の順に選択します。Keep the default Web App Name, and then select the Subscription, Resource Group, and App Service Plan.

  4. [作成] を選択しますSelect Create.

  5. [概要] セクションの [サイト URL] プロパティをメモします。Make a note of the Site URL property in the Summary section. この URL は、このチュートリアルの後半で使用する "バックエンドエンドポイント" です。This URL is your back-end endpoint later in the tutorial.

  6. [発行] を選択します。Select Publish.

ウィザードの完了後に、Azure に ASP.NET Web アプリを発行してから、既定のブラウザーでアプリを開きます。After you've completed the wizard, it publishes the ASP.NET web app to Azure and then opens the app in the default browser. アプリケーションが Azure App Services に表示されます。Your application is viewable in Azure App Services.

URL では、前に指定した Web アプリ名が http://<アプリ名>.azurewebsites.net という形式で使用されます。The URL uses the web app name that you specified earlier, with the format http://<app_name>.azurewebsites.net.

iOS アプリを変更するModify your iOS app

  1. Azure Notification Hubs を使用して iOS アプリにプッシュ通知を送信する」で作成した、単一ページの表示アプリを開きます。Open the Single Page view app you created in the Send push notifications to iOS apps using Azure Notification Hubs tutorial.

    注意

    このセクションでは、プロジェクトは空の組織名で構成されていると想定しています。This section assumes that your project is configured with an empty organization name. そうでない場合は、すべてのクラス名の先頭に組織名を追加します。If not, prepend your organization name to all class names.

  2. Main.storyboard ファイルで、オブジェクト ライブラリから、スクリーンショットに表示されているコンポーネントを追加します。In the Main.storyboard file, add the components shown in the screenshot from the object library.

    Xcode Interface Builder でストーリーボードを編集する

    • [ユーザー名] : "Enter Username" というプレースホルダー テキストが入っている UI テキスト フィールドです。結果の送信ラベルのすぐ下にあり、左右の余白と、結果の送信ラベルの下の余白による制約が適用されます。Username: A UITextField with placeholder text, Enter Username, immediately beneath the send results label and constrained to the left and right margins and beneath the send results label.

    • パスワード:"Enter Password" というプレースホルダー テキストが入っている UI テキスト フィールドです。ユーザー名のテキスト フィールドのすぐ下にあり、左右の余白と、ユーザー名のテキスト フィールドの下の余白による制約が適用されます。Password: A UITextField with placeholder text, Enter Password, immediately beneath the username text field and constrained to the left and right margins and beneath the username text field. Attributes Inspector で [Return Key] の下にある [Secure Text Entry] オプションをオンにします。Check the Secure Text Entry option in the Attribute Inspector, under Return Key.

    • Log in: パスワードのテキスト フィールドのすぐ下にあるラベル付きの UI ボタンです。Attributes Inspector の [Control-Content] の下にある [Enabled] オプションをオフにしますLog in: A UIButton labeled immediately beneath the password text field and uncheck the Enabled option in the Attributes Inspector, under Control-Content

    • WNS: Windows Notification Service に通知を送信するためのスイッチとそのラベルです。ハブ上でセットアップを済ませておく必要があります。WNS: Label and switch to enable sending the notification Windows Notification Service if it has been set up on the hub. 詳しくは、Windows で通知ハブを使用する方法に関するチュートリアルをご覧ください。See the Windows Getting Started tutorial.

    • GCM: Google Cloud Messaging に通知を送信するためのスイッチとそのラベルです。ハブ上でセットアップを済ませておく必要があります。GCM: Label and switch to enable sending the notification to Google Cloud Messaging if it has been set up on the hub. 詳しくは、Android で通知ハブを使用する方法に関するチュートリアルをご覧ください。See Android Getting Started tutorial.

    • APNS: Apple Platform Notification Service に通知を送信するためのスイッチとそのラベルです。APNS: Label and switch to enable sending the notification to the Apple Platform Notification Service.

    • Recipient Username: "Recipient username tag" というプレースホルダー テキストが入っている UI テキスト フィールドです。GCM ラベルのすぐ下にあり、左右の余白と、GCM ラベルの下にあることの制約が適用されます。Recipient Username:A UITextField with placeholder text, Recipient username tag, immediately beneath the GCM label and constrained to the left and right margins and beneath the GCM label.

      Azure Notification Hubs を使用して iOS アプリにプッシュ通知を送信する」チュートリアルで、いくつかのコンポーネントが追加されました。Some components were added in the Send push notifications to iOS apps using Azure Notification Hubs tutorial.

  3. ビューに表示されているコンポーネントを Ctrl キーを押しながら ViewController.h までドラッグし、次の新しいアウトレットを追加します。Ctrl drag from the components in the view to ViewController.h and add these new outlets:

    @property (weak, nonatomic) IBOutlet UITextField *UsernameField;
    @property (weak, nonatomic) IBOutlet UITextField *PasswordField;
    @property (weak, nonatomic) IBOutlet UITextField *RecipientField;
    @property (weak, nonatomic) IBOutlet UITextField *NotificationField;
    
    // Used to enable the buttons on the UI
    @property (weak, nonatomic) IBOutlet UIButton *LogInButton;
    @property (weak, nonatomic) IBOutlet UIButton *SendNotificationButton;
    
    // Used to enabled sending notifications across platforms
    @property (weak, nonatomic) IBOutlet UISwitch *WNSSwitch;
    @property (weak, nonatomic) IBOutlet UISwitch *GCMSwitch;
    @property (weak, nonatomic) IBOutlet UISwitch *APNSSwitch;
    
    - (IBAction)LogInAction:(id)sender;
    
  4. ViewController.h で、インポート ステートメントの後ろに次の #define を追加します。In ViewController.h, add the following #define after your import statements. <Your backend endpoint> のプレースホルダーは、前のセクションでアプリのバックエンドのデプロイに使用した宛先 URL に置き換えます。Substitute the <Your backend endpoint> placeholder with the Destination URL you used to deploy your app backend in the previous section. http://your_backend.azurewebsites.net の例を次に示します。For example, http://your_backend.azurewebsites.net:

    #define BACKEND_ENDPOINT @"<Your backend endpoint>"
    
  5. プロジェクトで、作成済みの ASP.NET バックエンドとのインターフェイスになる、RegisterClient という名前の新しい Cocoa Touch クラスを作成します。In your project, create a new Cocoa Touch class named RegisterClient to interface with the ASP.NET back-end you created. NSObject から継承するクラスを作成したら、Create the class inheriting from NSObject. RegisterClient.h の中に次のコードを追加します。Then add the following code in the RegisterClient.h:

    @interface RegisterClient : NSObject
    
    @property (strong, nonatomic) NSString* authenticationHeader;
    
    -(void) registerWithDeviceToken:(NSData*)token tags:(NSSet*)tags
        andCompletion:(void(^)(NSError*))completion;
    
    -(instancetype) initWithEndpoint:(NSString*)Endpoint;
    
    @end
    
  6. RegisterClient.m で、@interface セクションを更新します。In the RegisterClient.m, update the @interface section:

    @interface RegisterClient ()
    
    @property (strong, nonatomic) NSURLSession* session;
    @property (strong, nonatomic) NSURLSession* endpoint;
    
    -(void) tryToRegisterWithDeviceToken:(NSData*)token tags:(NSSet*)tags retry:(BOOL)retry
                andCompletion:(void(^)(NSError*))completion;
    -(void) retrieveOrRequestRegistrationIdWithDeviceToken:(NSString*)token
                completion:(void(^)(NSString*, NSError*))completion;
    -(void) upsertRegistrationWithRegistrationId:(NSString*)registrationId deviceToken:(NSString*)token
                tags:(NSSet*)tags andCompletion:(void(^)(NSURLResponse*, NSError*))completion;
    
    @end
    
  7. RegisterClient.m の @implementation セクションを、次のコードで置き換えます。Replace the @implementation section in the RegisterClient.m with the following code:

    @implementation RegisterClient
    
    // Globals used by RegisterClient
    NSString *const RegistrationIdLocalStorageKey = @"RegistrationId";
    
    -(instancetype) initWithEndpoint:(NSString*)Endpoint
    {
        self = [super init];
        if (self) {
            NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration];
            _session = [NSURLSession sessionWithConfiguration:config delegate:nil delegateQueue:nil];
            _endpoint = Endpoint;
        }
        return self;
    }
    
    -(void) registerWithDeviceToken:(NSData*)token tags:(NSSet*)tags
                andCompletion:(void(^)(NSError*))completion
    {
        [self tryToRegisterWithDeviceToken:token tags:tags retry:YES andCompletion:completion];
    }
    
    -(void) tryToRegisterWithDeviceToken:(NSData*)token tags:(NSSet*)tags retry:(BOOL)retry
                andCompletion:(void(^)(NSError*))completion
    {
        NSSet* tagsSet = tags?tags:[[NSSet alloc] init];
    
        NSString *deviceTokenString = [[token description]
            stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];
        deviceTokenString = [[deviceTokenString stringByReplacingOccurrencesOfString:@" " withString:@""]
                                uppercaseString];
    
        [self retrieveOrRequestRegistrationIdWithDeviceToken: deviceTokenString
            completion:^(NSString* registrationId, NSError *error) {
            NSLog(@"regId: %@", registrationId);
            if (error) {
                completion(error);
                return;
            }
    
            [self upsertRegistrationWithRegistrationId:registrationId deviceToken:deviceTokenString
                tags:tagsSet andCompletion:^(NSURLResponse * response, NSError *error) {
                if (error) {
                    completion(error);
                    return;
                }
    
                NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
                if (httpResponse.statusCode == 200) {
                    completion(nil);
                } else if (httpResponse.statusCode == 410 && retry) {
                    [self tryToRegisterWithDeviceToken:token tags:tags retry:NO andCompletion:completion];
                } else {
                    NSLog(@"Registration error with response status: %ld", (long)httpResponse.statusCode);
    
                    completion([NSError errorWithDomain:@"Registration" code:httpResponse.statusCode
                                userInfo:nil]);
                }
    
            }];
        }];
    }
    
    -(void) upsertRegistrationWithRegistrationId:(NSString*)registrationId deviceToken:(NSData*)token
                tags:(NSSet*)tags andCompletion:(void(^)(NSURLResponse*, NSError*))completion
    {
        NSDictionary* deviceRegistration = @{@"Platform" : @"apns", @"Handle": token,
                                                @"Tags": [tags allObjects]};
        NSData* jsonData = [NSJSONSerialization dataWithJSONObject:deviceRegistration
                            options:NSJSONWritingPrettyPrinted error:nil];
    
        NSLog(@"JSON registration: %@", [[NSString alloc] initWithData:jsonData
                                            encoding:NSUTF8StringEncoding]);
    
        NSString* endpoint = [NSString stringWithFormat:@"%@/api/register/%@", _endpoint,
                                registrationId];
        NSURL* requestURL = [NSURL URLWithString:endpoint];
        NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:requestURL];
        [request setHTTPMethod:@"PUT"];
        [request setHTTPBody:jsonData];
        NSString* authorizationHeaderValue = [NSString stringWithFormat:@"Basic %@",
                                                self.authenticationHeader];
        [request setValue:authorizationHeaderValue forHTTPHeaderField:@"Authorization"];
        [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    
        NSURLSessionDataTask* dataTask = [self.session dataTaskWithRequest:request
            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
        {
            if (!error)
            {
                completion(response, error);
            }
            else
            {
                NSLog(@"Error request: %@", error);
                completion(nil, error);
            }
        }];
        [dataTask resume];
    }
    
    -(void) retrieveOrRequestRegistrationIdWithDeviceToken:(NSString*)token
                completion:(void(^)(NSString*, NSError*))completion
    {
        NSString* registrationId = [[NSUserDefaults standardUserDefaults]
                                    objectForKey:RegistrationIdLocalStorageKey];
    
        if (registrationId)
        {
            completion(registrationId, nil);
            return;
        }
    
        // request new one & save
        NSURL* requestURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@/api/register?handle=%@",
                                _endpoint, token]];
        NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:requestURL];
        [request setHTTPMethod:@"POST"];
        NSString* authorizationHeaderValue = [NSString stringWithFormat:@"Basic %@",
                                                self.authenticationHeader];
        [request setValue:authorizationHeaderValue forHTTPHeaderField:@"Authorization"];
    
        NSURLSessionDataTask* dataTask = [self.session dataTaskWithRequest:request
            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
        {
            NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*) response;
            if (!error && httpResponse.statusCode == 200)
            {
                NSString* registrationId = [[NSString alloc] initWithData:data
                    encoding:NSUTF8StringEncoding];
    
                // remove quotes
                registrationId = [registrationId substringWithRange:NSMakeRange(1,
                                    [registrationId length]-2)];
    
                [[NSUserDefaults standardUserDefaults] setObject:registrationId
                    forKey:RegistrationIdLocalStorageKey];
                [[NSUserDefaults standardUserDefaults] synchronize];
    
                completion(registrationId, nil);
            }
            else
            {
                NSLog(@"Error status: %ld, request: %@", (long)httpResponse.statusCode, error);
                if (error)
                    completion(nil, error);
                else {
                    completion(nil, [NSError errorWithDomain:@"Registration" code:httpResponse.statusCode
                                userInfo:nil]);
                }
            }
        }];
        [dataTask resume];
    }
    
    @end
    

    このコードは、アプリケーション バックエンドへの REST 呼び出しを実行するための NSURLSession、および通信ハブによって返される registrationId をローカルに格納するための NSUserDefaults の使用についてのガイダンス記事「アプリ バックエンドからの登録」で説明したロジックを実装します。This code implements the logic explained in the guidance article Registering from your app backend using NSURLSession to perform REST calls to your app backend, and NSUserDefaults to locally store the registrationId returned by the notification hub.

    このクラスが適切に機能するためには、プロパティ authorizationHeader を設定する必要があります。This class requires its property authorizationHeader to be set in order to work properly. このプロパティは、ログイン後に ViewController クラスによって設定されます。This property is set by the ViewController class after the login.

  8. ViewController.h で、RegisterClient.h のための #import ステートメントを追加します。In ViewController.h, add a #import statement for RegisterClient.h. デバイス トークンの宣言と、@interface セクションのRegisterClient インスタンスに対する参照を追加します。Then add a declaration for the device token and reference to a RegisterClient instance in the @interface section:

    #import "RegisterClient.h"
    
    @property (strong, nonatomic) NSData* deviceToken;
    @property (strong, nonatomic) RegisterClient* registerClient;
    
  9. ViewController.m で、以下のように @interface セクションのプライベート メソッドの宣言を追加します。In ViewController.m, add a private method declaration in the @interface section:

    @interface ViewController () <UITextFieldDelegate, NSURLConnectionDataDelegate, NSXMLParserDelegate>
    
    // create the Authorization header to perform Basic authentication with your app back-end
    -(void) createAndSetAuthenticationHeaderWithUsername:(NSString*)username
                    AndPassword:(NSString*)password;
    
    @end
    

    注意

    次のスニペットは安全な認証スキームではないため、createAndSetAuthenticationHeaderWithUsername:AndPassword: の実装を、OAuth や Active Directory などの登録クライアント クラスによって使用される認証トークンを生成する特定の認証メカニズムで置き換える必要があります。The following snippet is not a secure authentication scheme, you should substitute the implementation of the createAndSetAuthenticationHeaderWithUsername:AndPassword: with your specific authentication mechanism that generates an authentication token to be consumed by the register client class, e.g. OAuth, Active Directory.

  10. 次に、ViewController.m@implementation セクションに以下のコードを追加します。これは、デバイス トークンと認証ヘッダーの設定の実装を追加するものです。Then in the @implementation section of ViewController.m, add the following code, which adds the implementation for setting the device token and authentication header.

    -(void) setDeviceToken: (NSData*) deviceToken
    {
        _deviceToken = deviceToken;
        self.LogInButton.enabled = YES;
    }
    
    -(void) createAndSetAuthenticationHeaderWithUsername:(NSString*)username
                    AndPassword:(NSString*)password;
    {
        NSString* headerValue = [NSString stringWithFormat:@"%@:%@", username, password];
    
        NSData* encodedData = [[headerValue dataUsingEncoding:NSUTF8StringEncoding] base64EncodedDataWithOptions:NSDataBase64EncodingEndLineWithCarriageReturn];
    
        self.registerClient.authenticationHeader = [[NSString alloc] initWithData:encodedData
                                                    encoding:NSUTF8StringEncoding];
    }
    
    -(BOOL)textFieldShouldReturn:(UITextField *)textField
    {
        [textField resignFirstResponder];
        return YES;
    }
    

    デバイス トークンの設定によって、どのように [Log in] ボタンが有効になるかに注目してください。Notice how setting the device token enables the Log in button. これは、ログイン アクションの一部として、View Controller がアプリケーション バックエンドでプッシュ通知を登録するためです。It's because as a part of the login action, the view controller registers for push notifications with the app backend. デバイス トークンが適切に設定されるまで、ログイン アクションにアクセスできないようにします。You do not want the Log in action to be accessible until the device token has been properly set up. プッシュ登録の前にログインが発生する場合には、プッシュ登録からログインを切り離す必要があります。You can decouple the login from the push registration as long as the former happens before the latter.

  11. ViewController.m で以下のスニペットを使用して、 [Log in] ボタンのアクション メソッドを実装し、ASP.NET バックエンドを使って通知メッセージを送信するメソッドを実装します。In ViewController.m, use the following snippets to implement the action method for your Log in button and a method to send the notification message using the ASP.NET backend.

    - (IBAction)LogInAction:(id)sender {
        // create authentication header and set it in register client
        NSString* username = self.UsernameField.text;
        NSString* password = self.PasswordField.text;
    
        [self createAndSetAuthenticationHeaderWithUsername:username AndPassword:password];
    
        __weak ViewController* selfie = self;
        [self.registerClient registerWithDeviceToken:self.deviceToken tags:nil
            andCompletion:^(NSError* error) {
            if (!error) {
                dispatch_async(dispatch_get_main_queue(),
                ^{
                    selfie.SendNotificationButton.enabled = YES;
                    [self MessageBox:@"Success" message:@"Registered successfully!"];
                });
            }
        }];
    }
    
    - (void)SendNotificationASPNETBackend:(NSString*)pns UsernameTag:(NSString*)usernameTag
                Message:(NSString*)message
    {
        NSURLSession* session = [NSURLSession
            sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:nil
            delegateQueue:nil];
    
        // Pass the pns and username tag as parameters with the REST URL to the ASP.NET backend
        NSURL* requestURL = [NSURL URLWithString:[NSString
            stringWithFormat:@"%@/api/notifications?pns=%@&to_tag=%@", BACKEND_ENDPOINT, pns,
            usernameTag]];
    
        NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:requestURL];
        [request setHTTPMethod:@"POST"];
    
        // Get the mock authenticationheader from the register client
        NSString* authorizationHeaderValue = [NSString stringWithFormat:@"Basic %@",
            self.registerClient.authenticationHeader];
        [request setValue:authorizationHeaderValue forHTTPHeaderField:@"Authorization"];
    
        //Add the notification message body
        [request setValue:@"application/json;charset=utf-8" forHTTPHeaderField:@"Content-Type"];
        [request setHTTPBody:[message dataUsingEncoding:NSUTF8StringEncoding]];
    
        // Execute the send notification REST API on the ASP.NET Backend
        NSURLSessionDataTask* dataTask = [session dataTaskWithRequest:request
            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
        {
            NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*) response;
            if (error || httpResponse.statusCode != 200)
            {
                NSString* status = [NSString stringWithFormat:@"Error Status for %@: %d\nError: %@\n",
                                    pns, httpResponse.statusCode, error];
                dispatch_async(dispatch_get_main_queue(),
                ^{
                    // Append text because all 3 PNS calls may also have information to view
                    [self.sendResults setText:[self.sendResults.text stringByAppendingString:status]];
                });
                NSLog(status);
            }
    
            if (data != NULL)
            {
                xmlParser = [[NSXMLParser alloc] initWithData:data];
                [xmlParser setDelegate:self];
                [xmlParser parse];
            }
        }];
        [dataTask resume];
    }
    
  12. [Send Notification] ボタンのアクションを更新し、ASP.NET バックエンドを使用すると共に、スイッチで有効になっている PNS があればそれに通知を送信するようにします。Update the action for the Send Notification button to use the ASP.NET backend and send to any PNS enabled by a switch.

    - (IBAction)SendNotificationMessage:(id)sender
    {
        //[self SendNotificationRESTAPI];
        [self SendToEnabledPlatforms];
    }
    
    -(void)SendToEnabledPlatforms
    {
        NSString* json = [NSString stringWithFormat:@"\"%@\"",self.notificationMessage.text];
    
        [self.sendResults setText:@""];
    
        if ([self.WNSSwitch isOn])
            [self SendNotificationASPNETBackend:@"wns" UsernameTag:self.RecipientField.text Message:json];
    
        if ([self.GCMSwitch isOn])
            [self SendNotificationASPNETBackend:@"gcm" UsernameTag:self.RecipientField.text Message:json];
    
        if ([self.APNSSwitch isOn])
            [self SendNotificationASPNETBackend:@"apns" UsernameTag:self.RecipientField.text Message:json];
    }
    
  13. ViewDidLoad 関数で、以下を追加して RegisterClient インスタンスをインスタンス化し、テキスト フィールドに対するデリゲートを設定します。In the ViewDidLoad function, add the following to instantiate the RegisterClient instance and set the delegate for your text fields.

    self.UsernameField.delegate = self;
    self.PasswordField.delegate = self;
    self.RecipientField.delegate = self;
    self.registerClient = [[RegisterClient alloc] initWithEndpoint:BACKEND_ENDPOINT];
    
  14. ここで AppDelegate.m 内で、application:didRegisterForPushNotificationWithDeviceToken: メソッドの内容をすべて削除し、以下に置き換えます (ビュー コントローラーに APNs から取得した最新のデバイス トークンが含まれることを確認するため)。Now in AppDelegate.m, remove all the content of the method application:didRegisterForPushNotificationWithDeviceToken: and replace it with the following (to make sure that the view controller contains the latest device token retrieved from APNs):

    // Add import to the top of the file
    #import "ViewController.h"
    
    - (void)application:(UIApplication *)application
                didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
    {
        ViewController* rvc = (ViewController*) self.window.rootViewController;
        rvc.deviceToken = deviceToken;
    }
    
  15. 最後に AppDelegate.m で、次のメソッドが含まれていることを確認します。Finally in AppDelegate.m, make sure you have the following method:

    - (void)application:(UIApplication *)application didReceiveRemoteNotification: (NSDictionary *)userInfo {
        NSLog(@"%@", userInfo);
        [self MessageBox:@"Notification" message:[[userInfo objectForKey:@"aps"] valueForKey:@"alert"]];
    }
    

アプリケーションをテストするTest the application

  1. XCode を使用して、物理 iOS デバイスでアプリケーションを実行します (プッシュ通知はシミュレーターでは機能しません)。In XCode, run the app on a physical iOS device (push notifications do not work in the simulator).

  2. iOS アプリケーションの UI で、ユーザー名とパスワードの両方に同じ値を入力します。In the iOS app UI, enter same value for both username and password. 次に、 [Log in] をクリックします。Then click Log in.

    iOS テスト アプリケーション

  3. 登録の成功を通知するポップアップが表示されます。You should see a pop-up informing you of registration success. [OK] をクリックします。Click OK.

    iOS テスト通知の表示

  4. *Recipient username tag というテキストが表示されているフィールドに、別のデバイスから登録するときに使用したユーザー名のタグを入力します。In the *Recipient username tag text field, enter the user name tag used with the registration from another device.

  5. 通知メッセージを入力して Send Notification をクリックします。Enter a notification message and click Send Notification. 入力したタグが登録されているデバイスのみ、通知メッセージを受信します。Only the devices that have a registration with the recipient user name tag receive the notification message. 通知は、該当するユーザーにのみ送信されます。It is only sent to those users.

    iOS テストのタグ付けされた通知

次のステップNext steps

このチュートリアルでは、タグが登録に関連付けられている特定のユーザーにプッシュ通知を送信する方法を学習しました。In this tutorial, you learned how to push notifications to specific users that have tags associated with their registrations. 場所に基づいたプッシュ通知を送信する方法を学習するには、次のチュートリアルに進んでください。To learn how to push location-based notifications, advance to the following tutorial: