Azure Notification Hubsを使用してiOSとAndroidにPush通知を行う (3) ~配信編~ 

# 配信編

前回の[実装編](http://qiita.com/granoeste/items/74a910ad3b9a84011c0b)では、iOS/AndroidのTokenをAzure Notification Hubsに登録し、コンソールからPush通知を送信/受信できることを確認しました。

今回は、プログラムでAzure Notification HubsnのPush通知の送信や登録の管理等を行って行きます。

Azure Notification Hubsには、[Notification Hubs REST APIs](https://msdn.microsoft.com/library/en-us/Dn223264.aspx)が提供されていおり、JavaとNode.jsのSDKも提供されています。
REST APIを直接使用するには、認証,アクセストークンなどの仕組みを理解しなければならないため、それらをラップしたSDKを使用することで効率的に開発することが出来ます。

- Java ... [Azure/azure\-notificationhubs\-java\-backend](https://github.com/Azure/azure-notificationhubs-java-backend)
- Node.js ... [Notification Hubs](https://github.com/Azure/azure-sdk-for-node)

*※　PHPとPythonはラッパーサンプルが提供されています。*

- PHP ... [azure\-notificationhubs\-samples/notificationhubs\-rest\-php at master · Azure/azure\-notificationhubs\-samples](https://github.com/Azure/azure-notificationhubs-samples/tree/master/notificationhubs-rest-php)
- Python ...[azure\-notificationhubs\-samples/notificationhubs\-rest\-python at master · Azure/azure\-notificationhubs\-samples](https://github.com/Azure/azure-notificationhubs-samples/tree/master/notificationhubs-rest-python)

## JavaからPush通知

### SDKのビルド

1. GitHubから[Azure/azure\-notificationhubs\-java\-backend](https://github.com/Azure/azure-notificationhubs-java-backend) をPCローカルにcloneする。

    ```sh
    $ git clone https://github.com/Azure/azure-notificationhubs-java-backend.git
    ```
2. Maven構成のプロジェクトとなっているので、以下のコードでビルド。

    ```sh
    $ mvn clean package
    ```
3. *TESTが失敗し、"BUILD FAILURE"となる場合は、暫定的に以下の行をコメントアウトして、再ビルド。*

    ```xml
    <!--
    <testSourceDirectory>test</testSourceDirectory>
    <testResources>
        <testResource>
            <directory>test</directory>
            <excludes>
                <exclude>**/*.java</exclude>
            </excludes>
        </testResource>
    </testResources>
    -->
    ```
5. ビルドに成功すると、target ディレクトリにjarファイルが作成される。
  `NotificationHubs-0.0.3.jar`

### アプリの実装

1. Notification Hubのクライアントのインスタンスを作成

    ```java
    String connectionString = "Endpoint=sb://x.servicebus.windows.net/;SharedAccessKeyName=DefaultFullSharedAccessSignature;SharedAccessKey=...";
    String hubPath = "AzureNotificationHubsExample";
    NotificationHub hub = new NotificationHub(connectionString, hubPath);
    ```
    * connectionString に、Notification Hubの[DefaultFullSharedAccessSignature]の文字列を指定
    * hubPath に、Notification Hubの名前を指定

2. Push通知の送信

  1. iOSへ送信する

        ```Java
        String message = "Hello Azure World!";
        String body = String.format("{\"aps\":{\"alert\":\"%s\"}}", message);

        Notification notifiation = Notification.createAppleNotifiation(body);

        hub.sendNotification(notifiation);
        ```
  2. Androidへ送信する

        ```Java
        String message = "Hello Azure World!";
        String body = String.format("{\"data\":{\"message\":\"%s\"}}", message);

        Notification notifiation = Notification.createGcmNotifiation(body);

        hub.sendNotification(notifiation);
        ```

3. タグを使って指定した範囲にPush通知を送信
  iOS/Android アプリからAzure Notification HubsへのPush通知のトークンの登録時にタグを指定することが出来ます。
  タグに、カテゴリーやグループの識別子や、ユーザー、デバイス識別子(UUID等)を設定しておくことで、送信先の範囲を絞り込むことに使用すること出来ます。

    1. iOS/Android アプリからタグを指定してPush通知のトークンを登録
        [Azure Notification Hubsを使用してiOSとAndroidにPush通知を行う \(2\) ~実装編~](http://qiita.com/granoeste/private/74a910ad3b9a84011c0b)で、実装したコードの登録のロジックに、UUIDのタグを追加します。

        1. iOS

            ```swift
            func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
                // NotificationHubへ登録
                let hub = SBNotificationHub(
                    connectionString: "Endpoint=sb://x.servicebus.windows.net/;SharedAccessKeyName=DefaultListenSharedAccessSignature;SharedAccessKey=...",
                    notificationHubPath: "AzureNotificationHubsExample")
                  
                // タグにUUIDを追加
                let uuid = NSUUID().UUIDString;
                let tags: Set<String> = ["UUID:\(uuid)"]

                hub.registerNativeWithDeviceToken(deviceToken, tags: tags) { (error) in
                    if (error != nil) {
                        print("Error registering for notifications.", error)
                    } else {
                        print("Success registering for notifications.", deviceToken)
                    }
                }
            }
            ``` 
      2. Android

            ```Java
            private void sendRegistrationToServer(String refreshedToken) {
                NotificationHub hub = new NotificationHub(
                        "AzureNotificationHubsExample",
                        "Endpoint=sb://x.windows.net/;SharedAccessKeyName=DefaultListenSharedAccessSignature;SharedAccessKey=...",
                        this);
        
                try {
                    // タグにUUIDを追加
                    String uuid = UUID.randomUUID().toString();
                    String[] tags = {"UUID:" + uuid};

                    Registration registration = hub.register(refreshedToken, tags);
                    String registrationId = registration.getRegistrationId();

                    Log.d(TAG, "Registration ID: " + registrationId);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } 
            ```
    2. タグを指定してPush通知を送信
        *タグにUUIDを指定*

        ```Java
        Set<String> tags = new HashSet<String>();
        tags.add("UUID:4151CA6C-FE7B-48C4-83AC-F1FEDD8C37AA");

        hub.sendNotification(notifiation, tags);
        ```
        *UUIDのデバイスにのみPush通知が送信されます。*


4. 登録の編集
  タグに設定したカテゴリーやグループの識別子を編集することが可能です。
    * 登録を取得

    ```java:RegistrationIdで取得
    Registration registration = hub.getRegistration(registrationId);
    ```
    ```java:Tagで検索
    CollectionResult collectionResult = hub.getRegistrationsByTag("SINGLE");
    for (Registration registration:collectionResult.getRegistrations()) {
        // do logic
    }

    ```
    * 登録を更新

    ```java
    // タグを編集
    Set<String> tags = new HashSet<String>();
    tags.add("AGE_GROUP:F2");
    tags.add("FEMALE");
    tags.add("SINGLE");

    registration.setTags(tags);

    hub.updateRegistration(registration);
    ```
    * 登録を削除

    ```java
    hub.deleteRegistration(registrationId);
    ```

5. デバイスの登録
    SDKからもPush通知のトークンの登録が出来ます。

    ```java
        Registration reg = null;
        switch (platform) {
            case IOS:
                reg = new AppleRegistration(ｔoken);
                break;
            case ANDROID:
                reg = new GcmRegistration(token);
                break;
            default:
        }
        reg.getTags().add("UUID:" + uuid);
        reg.getTags().add("PLATFORM:" + platform);
        Registration registration = hub.createRegistration(reg);

        String registrationId = registration.getRegistrationId();
    
    ```

6. 補足とまとめ
    - Azure Notification Hubsは、同時に複数デバイスにPush通知の送信が出来る。
    - 送信対象のデバイスはタグで絞り込むことが出来る。
    - プラットフォーム毎にPush通知を送信しなければならない。
        - プラットフォームを意識しないで送信することも出来れば良かったのだが...
    - APNS/FCMのPushのトークンを使用して、Push通知の送信は出来ない。
    - 登録時に発行されるRegistrationIdを指定して、Push通知の送信は出来ない。
    - デバイス個々にPush通知を送信をしたい場合は、タグにデバイス識別子を設定する工夫が必要。
    - 登録の更新でトークンを更新することは出来ない。Pushのトークンのリフレッシュが発生した場合、登録は削除と作成しなければならない。
    - 登録時に発行されるRegistrationIdが、iOSのSDKでは取得できない...
        - アプリのサービスを想定すると、個人情報やデバイス情報などとPush通知をまとめて管理する必要があると思うので、各プラットフォームが直接Azure Notification Hubsに登録するのではなく、Web App サーバでAPIで登録を管理した方がよいと考える。


*Push通知は、バックエンドのAnalyticsの情報を元にターゲットの絞込やメッセージの作成をすることが想定されるので、mBaaSとしてそのあたりの強化を期待したい*