次の方法で共有


iOS 用デバイス リレーの実装

Project Rome の Device Relay 機能は、リモート起動 (コマンド実行とも呼ばれます) とアプリ サービスの 2 つの主な機能をサポートする API のセットで構成されています。

リモート起動 API は、リモート デバイス上のプロセスがローカル デバイスから開始されるシナリオをサポートします。 たとえば、ユーザーが車の中でスマートフォンのラジオを聞いているとしますが、家に帰ると、スマートフォンを使用して Xbox に再生を転送し、ホーム ステレオに接続します。

App Services API を使用すると、アプリケーションは、任意のコンテンツを含むメッセージを送信できる 2 つのデバイス間に永続的なパイプラインを確立できます。 たとえば、モバイル デバイスで実行されている写真共有アプリは、写真を取得するためにユーザーの PC との接続を確立できます。

Project Rome の機能は、Connected Devices Platform と呼ばれる基になるプラットフォームでサポートされています。 このガイドでは、Connected Devices Platform の使用を開始するために必要な手順と、プラットフォームを使用してデバイス リレー関連の機能を実装する方法について説明します。

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

接続デバイス プラットフォームと通知の設定

アプリを登録する

Project Rome SDK のほぼすべての機能 (近くの共有 API の例外) には、Microsoft アカウント (MSA) または Azure Active Directory (AAD) 認証が必要です。 MSA をまだお持ちでなく、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 を追加する

iOS アプリに Connected Devices Platform を追加する最も簡単な方法は、 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 ファイルを使用する必要があります。

認証とアカウント管理を設定する

接続デバイス プラットフォームでは、登録プロセスで有効な 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 でのアプリの登録で次のアクセス許可を指定する必要があります (Azure Active Directory > アプリの登録 portal.azure.com >)。

  • Microsoft アクティビティ フィード サービス
    • このアプリのユーザー通知を配信および変更する
    • ユーザーのアクティビティ フィードへのアプリのアクティビティの読み取りと書き込み
  • Windows 通知サービス
    • デバイスを Windows Notification Service に接続する
  • Microsoft Device Directory Service
    • デバイスの一覧を表示する
    • デバイスとアプリの一覧に追加する
  • Microsoft Command Service
    • ユーザーのデバイスとの通信
    • ユーザー デバイスの読み取り

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

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

登録したら、プッシュ通知機能をアプリの接続済みデバイス プラットフォームに関連付ける必要があります。

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 デベロッパー センターにアプリを登録する

Warnung

この手順は、Project Rome 機能を使用して Windows 以外のデバイスからデータにアクセスしたり、要求したりする場合にのみ必要です。 Windows デバイスのみを対象とする場合は、この手順を完了する必要はありません。

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

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

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

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

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

     }

リモート デバイスとアプリを検出する

MCDRemoteSystemWatcher インスタンスは、このセクションのコア機能を処理します。 リモート システムを検出するクラスで宣言します。

MCDRemoteSystemWatcher* _watcher;

ウォッチャーを作成してデバイスの検出を開始する前に、検出フィルターを追加して、アプリが対象とするデバイスの種類を決定できます。 これらは、ユース ケースに応じて、ユーザー入力またはアプリへのハードコーディングによって決定できます。

サンプル アプリの次のコードは、検出されたデバイスをアプリが解析して操作できるようにするウォッチャー インスタンスを作成して開始する方法を示しています。

// Start watcher with filter for transport types, form factors
- (void)startWatcherWithFilter:(NSMutableArray<NSObject<MCDRemoteSystemFilter>*>*)remoteSystemFilter
{
    _discoveredSystems = [[NSMutableArray alloc] init];
    _devicesAdded = 0;
    _devicesUpdated = 0;
    _devicesRemoved = 0;

    // add filters (not defined here)
    _watcher = (remoteSystemFilter.count > 0) ? [[MCDRemoteSystemWatcher alloc] initWithFilters:remoteSystemFilter] :
        [[MCDRemoteSystemWatcher alloc] init];

    // add event handlers
    RemoteSystemViewController* __weak weakSelf = self;
    [_watcher addRemoteSystemAddedListener:^(
        __unused MCDRemoteSystemWatcher* watcher, MCDRemoteSystem* system) { [weakSelf _onRemoteSystemAdded:system]; }];

    [_watcher addRemoteSystemUpdatedListener:^(
        __unused MCDRemoteSystemWatcher* watcher, MCDRemoteSystem* system) { [weakSelf _onRemoteSystemUpdated:system]; }];

    [_watcher addRemoteSystemRemovedListener:^(
        __unused MCDRemoteSystemWatcher* watcher, MCDRemoteSystem* system) { [weakSelf _onRemoteSystemRemoved:system]; }];

    // start watcher
    [_watcher start];
}

イベント ハンドラー メソッドはここで定義されています。

// Handle when RemoteSystems are added
- (void)_onRemoteSystemAdded:(MCDRemoteSystem*)system
{
    @synchronized(self)
    {
        _devicesAdded++;
        [_discoveredSystems addObject:system];
        [_delegate remoteSystemsDidUpdate];
    }
}

// Handle when RemoteSystems are updated
- (void)_onRemoteSystemUpdated:(MCDRemoteSystem*)system
{
    @synchronized(self)
    {
        _devicesUpdated++;

        for (unsigned i = 0; i < _discoveredSystems.count; i++)
        {
            MCDRemoteSystem* cachedSystem = [_discoveredSystems objectAtIndex:i];
            if ([cachedSystem.displayName isEqualToString:system.displayName])
            {
                [_discoveredSystems replaceObjectAtIndex:i withObject:system];
                break;
            }
        }
    }
}

// Handle when RemoteSystems are removed
- (void)_onRemoteSystemRemoved:(MCDRemoteSystem*)system
{
    @synchronized(self)
    {
        _devicesRemoved++;

        for (unsigned i = 0; i < _discoveredSystems.count; i++)
        {
            MCDRemoteSystem* cachedSystem = [_discoveredSystems objectAtIndex:i];
            if ([cachedSystem.displayName isEqualToString:system.displayName])
            {
                [_discoveredSystems removeObjectAtIndex:i];
                break;
            }
        }
    }
}

アプリでは、検出されたデバイスのセット ( MCDRemoteSystem インスタンスで表されます) を維持し、使用可能なデバイスとそのアプリ (表示名やデバイスの種類など) に関する情報を UI に表示することをお勧めします。

[_watcher start]呼び出されると、リモート システム アクティビティの監視が開始され、接続されているデバイスが検出、更新、または検出されたデバイスのセットから削除されたときにイベントが発生します。 バックグラウンドで継続的にスキャンされるため、不要なネットワーク通信やバッテリのドレインを回避するために、必要なくなったときにウォッチャー ( [_watcher stop]) を停止することをお勧めします。

ユース ケースの例: リモート起動とリモート アプリ サービスの実装

コードのこの時点で、使用可能なデバイスを参照する MCDRemoteSystem オブジェクトの作業リストが必要です。 これらのデバイスで何を行うかは、アプリの機能によって異なります。 主な操作の種類は、リモート起動とリモート アプリ サービスです。 これらは、次のセクションで説明します。

A) リモート起動

次のコードは、 MCDRemoteSystem オブジェクトの 1 つ (理想的には UI コントロールを使用して行う) を選択し、 MCDRemoteLauncher を使用してアプリ互換 URI を渡してアプリを起動する方法を示しています。

リモート起動では、リモート デバイス (その URI スキームの既定のアプリで指定された URI がホスト デバイスによって起動されます) またはその デバイス上の特定のリモート アプリケーションをターゲットにできることに注意してください。

前のセクションで示したように、検出は最初にデバイス レベルで行われます (MCDRemoteSystem はデバイスを表します)、MCDRemoteSystem インスタンスで getApplications メソッドを呼び出して、接続デバイス プラットフォームを使用するように登録されているリモート デバイス上のアプリを表す MCDRemoteSystemApp オブジェクトの配列を取得できます (上記の準備手順で独自のアプリを登録した場合と同様)。 MCDRemoteSystemMCDRemoteSystemApp の両方を使用して、URI を起動するために必要な MCDRemoteSystemConnectionRequest を構築できます。

サンプルの次のコードは、接続要求を介した URI のリモート起動を示しています。

// Send a remote launch of a uri to RemoteSystemApplication
- (IBAction)launchUriButton:(id)sender
{
    NSString* uri = self.uriField.text;
    MCDRemoteLauncher* remoteLauncher = [[MCDRemoteLauncher alloc] init];
    MCDRemoteSystemConnectionRequest* connectionRequest =
        [MCDRemoteSystemConnectionRequest requestWithRemoteSystemApplication:self.selectedApplication];
    [remoteLauncher launchUriAsync:uri
        withConnectionRequest:connectionRequest
            completion:^(MCDRemoteLaunchUriStatus result, NSError* _Nullable error) {
                if (error)
                {
                    NSLog(@"LaunchURI [%@]: ERROR: %@", uri, error);
                    return;
                }

                if (result == MCDRemoteLaunchUriStatusSuccess)
                {
                    NSLog(@"LaunchURI [%@]: Success!", uri);
                }
                else
                {
                    NSLog(@"LaunchURI [%@]: Failed with code %d", uri, (int)result);
                }
            }];
}

送信される URI に応じて、リモート デバイスで特定の状態または構成でアプリを起動できます。 これにより、中断することなく、別のデバイスで映画の視聴などのユーザー タスクを続行できます。

使用によっては、対象システム上のアプリが URI を処理できない場合や、複数のアプリで URI を処理できない場合に対応することが必要になる場合があります。 MCDRemoteLauncher クラスと MCDRemoteLauncherOptions クラスでは、これを行う方法について説明します。

B) リモート アプリ サービス

iOS アプリでは、接続済みデバイス ポータルを使用して、他のデバイス上のアプリ サービスと対話できます。 これにより、他のデバイスと通信する多くの方法が提供されます。すべて、アプリをホスト デバイスの前面に移動する必要はありません。

ターゲット デバイスでアプリ サービスを設定する

このガイドでは、対象のアプリ サービスとして Windows 用 Roman Test App を使用します。 したがって、次のコードでは、iOS アプリが特定のリモート システムでその特定のアプリ サービスを検索します。 このシナリオをテストする場合は、Windows デバイスで Roman Test App をダウンロードし、上記の準備手順で使用したものと同じ MSA でサインインしていることを確認します。

独自の UWP アプリ サービスを作成する方法については、「アプリ サービス (UWP) を作成して使用する」を参照してください。 サービスを接続済みデバイスと互換性のあるものにするには、いくつかの変更を行う必要があります。 これを行う方法については、 リモート アプリ サービスの UWP ガイド を参照してください。

クライアント デバイスで App Service 接続を開く

iOS アプリは、リモート デバイスまたはアプリケーションへの参照を取得する必要があります。 起動セクションと同様に、このシナリオでは 、MCDRemoteSystemConnectionRequest を使用する必要があります。 MCDRemoteSystem またはシステムで使用可能なアプリを表す MCDRemoteSystemApp から構築できます。

さらに、アプリは、アプリ サービス名とパッケージ識別子という 2 つの文字列によって、対象となるアプリ サービスを識別する必要があります。 これらは、アプリ サービス プロバイダーのソース コードにあります (詳細については、 アプリ サービス (UWP) の作成と使用 に関するページを参照してください)。 これらの文字列を組み合わせることで 、MCDAppServiceDescription が構築され、 MCDAppServiceConnection インスタンスにフィードされます。

// Step #1:  Establish an app service connection
- (IBAction)connectAppServiceButton:(id)sender
{
    MCDAppServiceConnection* connection = nil;
    @synchronized(self)
    {
        connection = _appServiceConnection;
        if (!connection)
        {
            connection = _appServiceConnection = [MCDAppServiceConnection new];
            connection.appServiceDescription =
                [MCDAppServiceDescription descriptionWithName:g_appServiceName packageId:g_packageIdentifier];
            _serviceClosedRegistration = [connection addServiceClosedListener:^(__unused MCDAppServiceConnection* connection,
                MCDAppServiceClosedStatus status) { [self appServiceConnection:connection closedWithStatus:status]; }];
        }
    }

    @try
    {
        MCDRemoteSystemConnectionRequest* connectionRequest =
            [MCDRemoteSystemConnectionRequest requestWithRemoteSystemApplication:self.selectedApplication];
        [connection openRemoteAsync:connectionRequest
            completion:^(MCDAppServiceConnectionStatus status, NSError* error) {
                if (error)
                {
                    NSLog(@"ConnectAppService: ERROR: %@", error);
                    return;
                }
                if (status != MCDAppServiceConnectionStatusSuccess)
                {
                    NSLog(@"ConnectAppService: Failed with code %d", (int)status);
                    return;
                }
                NSLog(@"Successfully connected!");
                dispatch_async(
                    dispatch_get_main_queue(), ^{ self.appServiceStatusLabel.text = @"App service connected! no ping sent"; });
            }];
    }
    @catch (NSException* ex)
    {
        NSLog(@"ConnectAppService: EXCEPTION! %@", ex);
    }
}

アプリ サービスに送信するメッセージを作成する

送信するメッセージを格納する変数を宣言します。 iOS では、リモート アプリ サービスに送信するメッセージは NSDictionary 型になります。

アプリが他のプラットフォーム上のアプリ サービスと通信すると、Connected Devices Platform は NSDictionary を受信側プラットフォーム上の同等のコンストラクトに変換します。 たとえば、このアプリから Windows アプリ サービスに送信された NSDictionary は、(.NET Framework の) ValueSet オブジェクトに変換され、アプリ サービスによって解釈されます。 他の方向に渡された情報は、逆の翻訳を受けます。

次のメソッドは、Windows 用 Roman Test App のアプリ サービスで解釈できるメッセージを作成します。

// Create a message to send
- (NSDictionary*)_createPingMessage
{
    return @{
        @"Type" : @"ping",
        @"CreationDate" : [_dateFormatter stringFromDate:[NSDate date]],
        @"TargetId" : _selectedApplication.applicationId
    };
}

Von Bedeutung

リモート アプリ サービス シナリオでアプリとサービスの間で渡される NSDictionary オブジェクトは、次の形式に従う必要があります。キーは NSStrings である必要があり、値は NSString、ボックス化された数値型 (整数または浮動小数点)、ボックス化されたブール値、NSDate、NSUUID、これらの型のいずれかの同種配列、またはこの仕様を満たすその他の NSDictionary オブジェクトである必要があります。

アプリ サービスにメッセージを送信する

App Service 接続が確立され、メッセージが作成されると、アプリ サービスへの送信は簡単で、接続インスタンスとメッセージへの参照を持つアプリ内のどこからでも実行できます。

サンプルの次のコードは、アプリ サービスへのメッセージの送信と応答の処理を示しています。

//  Send a message using the app service connection
- (IBAction)sendAppServiceButton:(id)sender
{
    if (!_appServiceConnection)
    {
        return;
    }

    // Send the message and get a response
    @try
    {
        [_appServiceConnection sendMessageAsync:[self _createPingMessage]
            completion:^(MCDAppServiceResponse* response, NSError* error) {
                if (error)
                {
                    NSLog(@"SendPing: ERROR: %@", error);
                    return;
                }

                if (response.status != MCDAppServiceResponseStatusSuccess)
                {
                    NSLog(@"SendPing: Response received with bad status code %d", (int)response.status);
                    return;
                }

                NSString* creationDateString = response.message[@"CreationDate"];
                if (creationDateString)
                {
                    NSDate* date = [_dateFormatter dateFromString:creationDateString];
                    if (date)
                    {
                        NSTimeInterval diff = [[NSDate date] timeIntervalSinceDate:date];
                        dispatch_async(dispatch_get_main_queue(),
                            ^{ self.appServiceStatusLabel.text = [NSString stringWithFormat:@"%g", diff]; });
                    }
                }
            }];
    }
    @catch (NSException* ex)
    {
        NSLog(@"SendPing: EXCEPTION! %@", ex);
    }
}

Roman App の場合、応答には作成された日付が含まれているため、この非常に単純なユース ケースでは、日付を比較してメッセージ応答の合計転送時間を取得できます。

これで、リモート アプリ サービスとの 1 つのメッセージ交換が終了します。

App Service の通信を完了する

アプリがターゲット デバイスのアプリ サービスとの対話を完了したら、2 つのデバイス間の接続を閉じます。

- (void)appServiceConnection:(__unused MCDAppServiceConnection*)connection closedWithStatus:(MCDAppServiceClosedStatus)status
{
    NSLog(@"AppService closed with status %d", (int)status);
    dispatch_async(
        dispatch_get_main_queue(), ^{ self.appServiceStatusLabel.text = [NSString stringWithFormat:@"disconnected (%d)", (int)status]; });
}