使い方ガイド: Graph 通知との統合 (iOS)

Graph 通知を使用すると、アプリはユーザーをターゲットにした通知を複数のデバイスにまたがって送信および管理できます。

iOS 上の Project Rome クライアント側 SDK によって、iOS アプリは、ログイン中のユーザーをターゲットにしてアプリ サーバーから発行された通知を受信するための登録ができます。 アプリ クライアントは SDK を使用して、新しい着信通知ペイロードを受信したり、既存の通知の状態を管理したり、通知履歴を取得したりできます。 通知の詳細と、それによって人間が主体の通知配信が実現するしくみについては、Microsoft Graph 通知の概要に関するページを参照してください

Graph 通知などを含む Project Rome SDK のすべての機能は、Connected Devices Platform と呼ばれる基盤プラットフォーム上に構築されています。 このガイドの目的は、Connected Devices Platform を初めて使用するために必要な手順、および、Graph 通知に固有の機能を実装するために SDK で API を使用する方法について説明することです。

この手順では、GitHub から入手できる Project Rome iOS サンプル アプリのコードを参照します。

通知のシナリオに関連したリファレンス ドキュメントへのリンクは、API リファレンスのページを参照してください。

Connected Devices Platform と通知の設定

アプリの登録

Microsoft アカウント (MSA) または Azure Active Directory (AAD) 認証は、Project Rome SDK のほとんどすべての機能に必要です (例外は近距離共有 API です)。 MSA をまだ所有しておらず、使用したい場合は、account.microsoft.com で登録してください。

注意

Azure Active Directory (AAD) アカウントは、Device Relay API ではサポートされていません。

選択した認証方法を使用して、またアプリケーション登録ポータルの指示に従ってアプリを Microsoft に登録する必要があります。 Microsoft 開発者アカウントがない場合、作成する必要があります。

MSA を使用してアプリを登録したら、クライアント ID 文字列が届くはずです。 これは後で使用しますので、保存しておいてください。 これにより、アプリが Microsoft の Connected Devices Platform リソースにアクセスできるようになります。 AAD を使用している場合は、クライアント ID 文字列を取得する手順について「Azure Active Directory 認証ライブラリ」を参照してください。

SDK の追加

Connected Devices Platform を iOS アプリに追加する最も簡単な方法は、CocoaPods 依存関係マネージャーを使用することです。 iOS プロジェクトの Podfile に移動し、次のエントリを挿入します。

platform :ios, "10.0"
workspace 'iOSSample'

target 'iOSSample' do
  # Uncomment the next line if you're using Swift or would like to use dynamic frameworks
  # use_frameworks!

	pod 'ProjectRomeSdk'

  # Pods for iOSSample

注意

CocoaPod を利用するためには、プロジェクト内の .xcworkspace ファイルを使用する必要があります。

認証とアカウント管理の設定

Connected Devices Platform では、有効な OAuth トークンを登録プロセスで使用する必要があります。 OAuth トークンの生成および管理には任意の方法を使用できます。 ただし、プラットフォームを初めて使用する開発者を支援するために、アプリで更新トークンの生成と管理に使用できる認証プロバイダーを iOS サンプルアプリの一部として含めています。

提供されたコードを使用しない場合、MCDConnectedDevicesAccountManager インターフェイスを自身で実装する必要があります。

MSA を使用している場合、サインイン要求に次のスコープを含めます: "wl.offline_access""ccs.ReadWrite""dds.read""dds.register""wns.connect""asimovrome.telemetry"、および "https://activity.windows.com/UserActivity.ReadWrite.CreatedByApp"

注意

Azure Active Directory (AAD) アカウントは、Device Relay API ではサポートされていません。

AAD アカウントを使用している場合、次の対象ユーザーを要求する必要があります: "https://cdpcs.access.microsoft.com""https://cs.dds.microsoft.com""https://wns.windows.com/"、および "https://activity.microsoft.com"

提供されている MCDConnectedDevicesAccountManager の実装を使用するかどうかにかかわらず、AAD を使用している場合は、Azure portal のアプリの登録 (portal.azure.com > [Azure Active Directory] > [アプリの登録]) で次のアクセス許可を指定する必要があります。

  • Microsoft アクティビティ フィード サービス
    • このアプリのユーザー通知を配信および変更する
    • アプリのアクティビティを読み取ってユーザーのアクティビティ フィードに書き込む
  • Windows 通知サービス
    • デバイスを Windows 通知サービスに接続する
  • Microsoft デバイス ディレクトリ サービス
    • デバイスの一覧を参照する
    • デバイスとアプリの一覧に追加される
  • Microsoft コマンド サービス
    • ユーザー デバイスと通信する
    • ユーザー デバイスを読み取る

アプリケーションをプッシュ通知に登録する

Apple Push Notification のサポートのためにアプリケーションを Apple に登録します。 受け取った送信者 ID とサーバー キーは、後で必要になるので必ずメモしておいてください。

登録したら、アプリ内で、プッシュ通知機能を Connected Devices Platform に関連付ける必要があります。

self.notificationRegistration = [[MCDConnectedDevicesNotificationRegistration alloc] init];
    if ([[UIApplication sharedApplication] isRegisteredForRemoteNotifications])
    {
        self.notificationRegistration.type = MCDNotificationTypeAPN;
    }
    else
    {
        self.notificationRegistration.type = MCDNotificationTypePolling;
    }
    self.notificationRegistration.appId = [[NSBundle mainBundle] bundleIdentifier];
    self.notificationRegistration.appDisplayName = (NSString*)[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"];
    self.notificationRegistration.token = deviceToken;
    self.isRegisteredWithToken = YES;

Microsoft Windows デベロッパー センターでクロスデバイス エクスペリエンスのためにアプリを登録する

警告

この手順は、Project Rome の機能を使用して非 Windows デバイスからデータにアクセスする、またはそのデバイスの要求を実行する場合にのみ必要です。 Windows デバイスのみをターゲットにする場合、この手順を完了する必要はありません。

Microsoft デベロッパー ダッシュボードのクロスデバイス エクスペリエンス機能用にアプリを登録します。 これは、上記の MSA および AAD アプリ登録とは異なる手順です。 このプロセスの主な目的は、プラットフォーム固有のアプリ ID を、Connected Devices Platform によって認識されるクロスプラットフォームのアプリ ID にマップすることです。 この手順により、アプリで利用するモバイル プラットフォームに対応するネイティブのプッシュ通知サービスを使用した通知の送信も有効化されます。 iOS の場合、それによって、APNS (Apple Push Notification Service) 経由で iOS アプリのエンドポイントに通知を送信できるようになります。

デベロッパー センターのダッシュボードに移動し、左側のナビゲーション ウィンドウから [Cross-Device Experiences] (クロスデバイス エクスペリエンス) に移動して、[configuring a new cross-device app] (新しいクロスデバイス アプリの構成) を選択します。 デベロッパー センター ダッシュボード – クロスデバイス エクスペリエンス

デベロッパー センターのオンボーディング プロセスでは、次の手順が必要です。

  • サポートされているプラットフォームの選択 – アプリがプレゼンスを持ち、クロスデバイス エクスペリエンスが有効化されるプラットフォームを選択します。 Graph 通知との統合の場合、使用しているプラットフォームに応じて Windows、Android、iOS から選択できます。 クロスデバイス エクスペリエンス – サポートされているプラットフォーム

  • アプリ ID の指定 – 使用しているプラットフォームごとにアプリ ID を指定します。 iOS アプリの場合、これはプロジェクト作成時にアプリに割り当てたパッケージ名です。 プラットフォームごとに (最大 10 個の) 異なる ID を追加できることに注意してください。これは、同じアプリの複数のバージョン、または複数の異なるアプリがあり、同じユーザーをターゲットにしてアプリ サーバーから送信された同じ通知をこれらのアプリで受信できるようにしたい場合です。 クロスデバイス エクスペリエンス – アプリ ID

  • 前述した以前の MSA/AAD アプリ登録手順で取得した、MSA/AAD アプリ登録のアプリ ID を指定または選択します。 クロスデバイス エクスペリエンス – MSA および AAD アプリ登録

  • ユーザーをターゲットにした通知を発行したときにアプリ サーバーから通知が配信されるよう、アプリで使用するネイティブ通知プラットフォーム (Windows の WNS、Android の FCM、iOS の APNS) の資格情報を入力します。 クロスデバイス エクスペリエンス – プッシュ資格情報

  • 最後に、クロスデバイス アプリ ドメインを検証して、アプリがドメインの所有権を持っていること、また、それをアプリのクロスデバイス ID として使用できることを確認します。 クロスデバイス エクスペリエンス – ドメインの確認

プラットフォームの使用

プラットフォームのインスタンスの作成

開始するには、単純にプラットフォームをインスタンス化します。

MCDConnectedDevicesPlatform* platform = [MCDConnectedDevicesPlatform new];

MCDConnectedDevicesAccountManager への登録

プラットフォームは、認証済みのユーザーがプラットフォームにアクセスすることを要求します。 有効なアカウントが使用されていることを保証するために、MCDConnectedDevicesAccountManager イベントに登録する必要があります。

[MCDConnectedDevicesPlatform* platform.accountManager.accessTokenRequested
     subscribe:^(MCDConnectedDevicesAccountManager* _Nonnull manager __unused,
                 MCDConnectedDevicesAccessTokenRequestedEventArgs* _Nonnull request __unused) {

                    // Get access token

                 }
[MCDConnectedDevicesPlatform* platform.platform.accountManager.accessTokenInvalidated
     subscribe:^(MCDConnectedDevicesAccountManager* _Nonnull manager __unused,
                 MCDConnectedDevicesAccessTokenInvalidatedEventArgs* _Nonnull request) {

                      // Refresh and renew existing access token

                 }

MCDConnectedDevicesNotificationRegistrationManager への登録

同様に、プラットフォームは通知を使用してデバイス間でコマンドを配信します。 したがって、使用されているアカウントに対してクラウドの登録状態が有効であることを保証するために、MCDConnectedDevicesNotificationRegistrationManager イベントに登録する必要があります。 MCDConnectedDevicesNotificationRegistrationState を使用して状態を確認する

[MCDConnectedDevicesPlatform* platform.notificationRegistrationManager.notificationRegistrationStateChanged
     subscribe:^(MCDConnectedDevicesNotificationRegistrationManager* manager __unused,
                 MCDConnectedDevicesNotificationRegistrationStateChangedEventArgs* args __unused) {

                     // Check state using MCDConnectedDevicesNotificationRegistrationState enum

                 }

プラットフォームの開始

プラットフォームが初期化され、イベント ハンドラーが配置されたので、リモート システム デバイスの検出を開始する準備ができました。

[MCDConnectedDevicesPlatform* platform start];

アプリが認識しているユーザー アカウントの取得

アプリが認識しているユーザー アカウントの一覧を、確実に MCDConnectedDevicesAccountManager と正しく同期することが重要です。

MCDConnectedDevicesAccountManager.addAccountAsync を使用して新しいユーザー アカウントを追加します。

[MCDConnectedDevicesPlatform* platform.accountManager
     addAccountAsync:self.mcdAccount
     callback:^(MCDConnectedDevicesAddAccountResult* _Nonnull result, NSError* _Nullable error) {

     // Check state using **MCDConnectedDevicesAccountAddedStatus** enum

     }

無効なアカウントを削除するには、MCDConnectedDevicesAccountManager.removeAccountAsync を使用できます。

 [MCDConnectedDevicesPlatform* platform.accountManager
     removeAccountAsync:existingAccount
     callback:^(MCDConnectedDevicesRemoveAccountResult* _Nonnull result __unused, NSError* _Nullable error) {

                    // Remove invalid user account

     }

Graph 通知チャネルの初期化

Graph 通知、ユーザー アクティビティなど、さまざまな種類のユーザー データを受信および管理するために、アプリで Project Rome SDK を使用してさまざまなチャネルに登録することができます。 これらはすべて MCDUserDataFeed に格納され、同期されます。 MCDUserNotification は、Graph 通知を介して送信される、ユーザーをターゲットにした通知に対応するクラスおよびデータ型です。 Graph 通知と統合して、アプリ サーバーによって発行された MCDUserNotification の受信を開始するには、まず MCDUserNotificationChannel を作成してユーザー データ フィードを初期化する必要があります。 これは、前述したプラットフォーム初期化手順のように扱う必要があります。(プラットフォーム初期化の前ではないタイミングで) アプリがフォアグラウンドになるたびにチェックし、場合によってはやり直す必要があります。

次のメソッドは MCDUserNotificationChannel を初期化します。

// You must be logged in to use UserNotifications
NSArray<MCDUserAccount*>* accounts = [[AppDataSource sharedInstance].accountProvider getUserAccounts];
if (accounts.count > 0)
{
    // Get a UserNotification channel, getting the default channel
    NSLog(@"Creating UserNotificationChannel");
    NSArray<MCDUserAccount*>* accounts = [[AppDataSource sharedInstance].accountProvider getUserAccounts];
    MCDUserDataFeed* userDataFeed = [MCDUserDataFeed userDataFeedForAccount:accounts[0]
        platform:[AppDataSource sharedInstance].platform
        activitySourceHost:CROSS_PLATFORM_APP_ID];
    NSArray<MCDSyncScope*>* syncScopes = @[ [MCDUserNotificationChannel syncScope] ];
    [userDataFeed addSyncScopes:syncScopes];
    self.channel = [MCDUserNotificationChannel userNotificationChannelWithUserDataFeed:userDataFeed];
}
else
{
    NSLog(@"Must log in to receive notifications for the logged in user!");
    self.createNotificationStatusField.text = @"Need to be logged in!";
}

この時点で、MCDUserNotificationChannel の参照が channel にあるはずです。

MCDUserNotificationReader を作成して着信 MCDUserNotification を受信し MCDUserNotification の履歴にアクセスする

前に説明したように、アプリ クライアントに到着する初期の APNS サイレント メッセージにはショルダー タップしか含まれておらず、そのショルダー タップ ペイロードを Connected Devices Platform に渡して、Connected Device サーバーとの完全同期を SDK に実行させる必要があります。このサーバーには、アプリ サーバーによって発行されたすべての MCDUserNotification が格納されます。 これにより、アプリ サーバーによって発行され、このショルダー タップに対応している完全な通知ペイロードが取得されます (また、発行されたがデバイス接続性またはその他の問題のためこのアプリ クライアントで受信されなかった以前の通知がある場合、それらも同時に取得されます)。 これらのリアルタイム同期が継続的に SDK によって実行されるので、アプリ クライアントはこのログイン中ユーザーの MCDUserNotification データ フィードのローカル キャッシュにアクセスできます。 この場合、アプリ クライアントは MCDUserNotificationReader を使用してこのデータ フィードにアクセスし、イベント リスナーを介して最新の通知ペイロードを受信したり、ユーザーの通知履歴のビュー モデルとして使用できる完全な MCDUserNotification コレクションにアクセスしたりできます。

MCDUserNotification の受信

実現しようとしているエクスペリエンスのためにその情報を利用することに関心がある場合は、まず MCDUserNotificationReader をインスタンス化し、既にリーダーにある既存の MCDUserNotification をすべて取得する必要があります。 この特定のデバイス エンドポイントが、ユーザーがアプリをインストールした唯一または最初のエンドポイントではない可能性を考えると、アプリ サーバーはこのログイン中ユーザーに通知を発行済みであると常に想定するのが安全です。 次に、Connected Device プラットフォームで同期が完了し、通知する新しい変更がある時点でトリガーされるイベント リスナーを追加します。 Graph 通知の場合、新しい変更として可能性があるのは、アプリ サーバーによって発行された MCDUserNotification の新規着信、または、サーバーから発生したか同じユーザーがログインした他の登録済みエンドポイントから発生した MCDUserNotifcation の更新、削除、期限切れです。

ヒント

このイベント リスナーは、メインのビジネス ロジックを処理し、シナリオに基づいて通知ペイロードの内容を "消費" する場所です。 現在 APNS サイレント通知を使用して OS レベルの通知センターで視覚的な通知を作成している場合、またはサイレント通知の内容を使用して何らかのアプリ内 UI を更新している場合、ここがそれらの処理を実行する場所です。

// Instantiate the reader from a MCDUserNotificationChannel
// Add a data change listener to subscribe to new changes when new notifications or notification updates are received
- (void)setupWithAccount:(MCDUserAccount*)account {
    dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
        @synchronized (self) {
            MCDUserDataFeed* dataFeed = [MCDUserDataFeed userDataFeedForAccount:account platform:_platform activitySourceHost:@"graphnotifications.sample.windows.com"];
            [dataFeed addSyncScopes:@[[MCDUserNotificationChannel syncScope]]];
            self.channel = [MCDUserNotificationChannel userNotificationChannelWithUserDataFeed:dataFeed];
            self.reader = [self.channel createReader];
            
            __weak typeof(self) weakSelf = self;
            _readerRegistrationToken = [self.reader addDataChangedListener:^(__unused MCDUserNotificationReader* source) {
                NSLog(@"ME123 Got a change!");
                if (weakSelf) {
                    [weakSelf forceRead];
                } else {
                    NSLog(@"ME123 WEAKSELF FOR CHANGES IS NULL!!!");
                }
            }];
            
            [self forceRead];
        }
    });
}

// this is your own business logic when the event listener is fired
// In this case, the app reads the existing batch of notifications in the store and handle any new incoming notifications or notification updates after that
- (void)forceRead {
    NSLog(@"ME123 Forced to read!");
    [self.reader readBatchAsyncWithMaxSize:NSUIntegerMax completion:^(NSArray<MCDUserNotification *> * _Nullable notifications, NSError * _Nullable error) {
        if (error) {
            NSLog(@"ME123 Failed to read batch with error %@", error);
        } else {
            [self _handleNotifications:notifications];
            NSLog(@"ME123 Have %ld listeners", self.listenerMap.count);
            for (void (^listener)(void) in self.listenerMap.allValues) {
                NSLog(@"ME123 Calling a listener about an update!");
                listener();
            }
        }
    }];
}

既存の MCDUserNotification の状態を更新する

前のセクションで説明しましたが、時として、リーダー経由で受信した MCDUserNotification の変更は、既存の MCDUserNotification の状態更新 (それが無視とマークされているか既読とマークされているかは問わない) である可能性があります。 この場合アプリ クライアントは、この特定のデバイス上で対応する視覚的通知を削除することによって全体無視を有効化するなど、実行する処理を選択できます。 少し話を戻すと、多くの場合、この MCDUserNotification の変更更新を最初に別のデバイスから開始したのはアプリ クライアントです。 MCDUserNotification の状態を更新するタイミングは選択できますが、それは通常、対応する視覚的通知がそのデバイス上でユーザーによって処理された時点、または、有効化する何らかのアプリ内エクスペリエンスでその通知がユーザーによってさらに処理された時点です。 フローがどのようになるかの例を次に示します: アプリ サーバーはユーザー A をターゲットに通知を発行します。ユーザー A は、アプリ クライアントがインストールされている PC と電話の両方でこの通知を受信します。 ユーザーは PC で通知をクリックし、対応するタスクを処理するためにアプリを起動します。 その後、この PC 上のアプリ クライアントが Connected Devices Platform SDK を呼び出して、対応するユーザー通知の状態を更新し、このユーザーのすべてのデバイス間でこの更新を同期します。 他のアプリ クライアントは、この状態更新をリアルタイムで受信すると、対応する視覚的アラート/メッセージ/トースト通知をデバイスの通知センター/通知トレイ/アクション センターから削除します。 これは、ユーザーのデバイス間で通知が全体無視されるしくみです。

ヒント

MCDUserNotification クラスは現在、2 種類の状態更新を提供します。MCDUserNotificationReadState または MCDUserNotificationUserActionState を変更し、通知が更新されたときに実行する処理について独自のロジックを定義できます。 たとえば、アクションの状態を Activated (アクティブ化) または Dismissed (無視) とマークし、その値を基準にして全体無視を実装できます。 その代わりに、または同時に、読み取り状態を Read (既読) または Unread (未読) とマークし、それに基づいて、どの通知をアプリ内通知の履歴ビューに表示するかを決定できます。

- (void)dismissNotification:(MCDUserNotification*)notification {
    @synchronized (self) {
        notification.userActionState = MCDUserNotificationUserActionStateDismissed;
        [notification saveAsync:^(__unused MCDUserNotificationUpdateResult * _Nullable result, __unused NSError * _Nullable err) {
            NSLog(@"ME123 Dismiss notification with result %d error %@", result.succeeded, err);
        }];
    }
}