Xamarin での watchOS のプロアクティブな提案

この記事では、watchOS 3 アプリでプロアクティブな提案を使用して、システムがユーザーに役立つ情報を自動的に事前に提示できるようにすることで、エンゲージメントを促進する方法について説明します。

watchOS 3 で初めて導入されたプロアクティブな提案は、役立つ情報を自動的に適切なタイミングでユーザーに事前に提示して、ユーザーが Xamarin.iOS アプリを利用するための新しい方法を提供します。

プロアクティブな提案について

watchOS 3 の新機能である NSUserActivity には、他のコンテキストで使用できる位置情報をアプリが提供できる MapItem プロパティが含まれています。 たとえば、アプリにホテルのレビューが表示され、MapItem の場所が提供されている場合、ユーザーが Maps アプリに切り替えたると、表示していたホテルの場所が使用可能になります。

アプリは、NSUserActivity、MapKit、Media Player、UIKit などのテクノロジのコレクションを使用して、この機能をシステムに公開します。 さらに、アプリにプロアクティブな提案のサポートを提供することで、より深い Siri との統合が無料で実現します。

位置情報に基づく提案

watchOS 3 の新機能である NSUserActivity のクラスには、他のコンテキストで使用できる位置情報を開発者が提供できる MapItem プロパティが含まれています。 たとえば、アプリにレストラン レビューが表示される場合、開発者は、ユーザーがアプリで表示しているレストランの場所に MapItem プロパティを設定できます。 ユーザーが Maps アプリに切り替えると、レストランの位置情報が自動的に利用できるようになります。

アプリが App Search をサポートしている場合は、CSSearchableItemAttributesSet クラスの新しい住所コンポーネントを使用して、ユーザーがアクセスする可能性がある場所を指定できます。 MapItem プロパティを設定すると、他のプロパティが自動的に入力されます。

住所コンポーネント プロパティの LatitudeLongitude を設定するだけでなく、Siri がその位置情報への呼び出しを開始できるようにアプリで NamedLocationPhoneNumbers プロパティも指定することをお勧めします。

コンテキスト Siri リマインダー

ユーザーが Siri を使用して、後日アプリで現在表示しているコンテンツを表示するためのアラームをすばやく作成できるようにします。 たとえば、アプリでレストランのレビューを見ていた場合、Siri を呼び出して、「家に着いたらこの事について思い出させて」と言う可能性があります。Siri は、アプリのレビューへのリンクを含むリマインダーを生成します。

プロアクティブな提案の実装

Xamarin.iOS アプリへのプロアクティブな提案のサポートの追加は、通常、いくつかの API を実装したり、アプリが既に実装している可能性のあるいくつかの API を拡張したりするのと同じくらい簡単です。

プロアクティブな提案は、次の 3 つの主な方法でアプリと連携します:

  • NSUserActivity - ユーザーが現在画面上で操作している情報をシステムが理解するのに役立ちます。
  • 場所の提案 - アプリが位置情報に基づく情報を提供または使用する場合、これらの API 拡張機能は、アプリ間でこの情報を共有する新しい方法を提供します。

また、次の機能を実装することで、アプリでサポートされます:

  • コンテキスト Siri アラーム - iOS 10 では、ユーザーがアプリで現在表示しているコンテンツを後日表示するように Siri がすぐにリマインダーを作成できるように NSUserActivity が拡張されています。
  • 場所の提案 - iOS 10 では、アプリ内で表示される場所をキャプチャし、システム全体の多くの場所でそれらを昇格するように NSUserActivity が強化されます。
  • コンテキスト Siri 要求 - NSUserActivity は、ユーザーが方向を取得したり、アプリ内から Siri を起動する呼び出しを行うことができるように、アプリ内に表示される情報にコンテキストを提供します。

これらの機能には共通するものがあります。これらはすべて、機能を提供するために 1 つの形式または別の形式で NSUserActivity を使用します。

NSUserActivity

前述のように、NSUserActivity は、ユーザーが画面上で現在どのような情報を扱っているかをシステムが理解するのに役立ちます。 NSUserActivity は、ユーザーがアプリ内を移動するときのユーザーのアクティビティをキャプチャするための軽量な状態キャッシュ メカニズムです。 レストラン アプリを例にします。

レストラン アプリ

次のような処理が行われます。

  1. ユーザーがアプリを操作すると、後でアプリの状態を再作成するために NSUserActivity が作成されます。
  2. ユーザーがレストランを検索した場合、同じパターンのアクティビティの作成に従います。
  3. 再び、ユーザーが結果を表示します。 この最後のケースでは、ユーザーは場所を表示しています。iOS 10 では、システムは特定の概念 (場所や通信のインタラクションなど) への認識を深めています。

最後の画面をより詳しく見てみましょう。

NSUserActivity ペイロード

ここでは、アプリは NSUserActivity を作成しており、これには、後で状態を再作成するための情報が設定されています。 アプリには、場所の名前や住所などのいくつかのメタデータも含まれています。 このアクティビティが作成されると、これがユーザーの現在の状態を表していることをアプリが iOS に通知します。

次に、アプリは、アクティビティが Handoff のために無線でアドバタイズされるか、場所を提案するための一時的な値として保存されるか、検索結果に表示するためにデバイス上の Spotlight インデックスに追加されるかを決定します。

Handoff と Spotlight 検索の詳細については、ハンドオフの概要に関するガイドと iOS 9 の新しい検索 API のガイドを参照してください。

アクティビティの作成

アクティビティを作成する前に、アクティビティを識別するためにアクティビティの種類識別子を作成する必要があります。 アクティビティの種類識別子は、特定のユーザー アクティビティの種類を一意に識別するために使用される、アプリの Info.plist ファイルの NSUserActivityTypes 配列に追加される短い文字列です。 配列には、アプリがサポートし、アプリ検索に公開するアクティビティごとに 1 つのエントリが存在します。 詳細については、アクティビティの種類識別子の作成に関するリファレンスを参照してください。

アクティビティの例を見てみましょう。

// Create App Activity
var activity = new NSUserActivity ("com.xamarin.platform");

// Define details
var info = new NSMutableDictionary ();
info.Add(new NSString("link"),new NSString("http://xamarin.com/platform"));

// Populate Activity
activity.Title = "The Xamarin Platform";
activity.UserInfo = info;

// Enable capabilities
activity.EligibleForSearch = true;
activity.EligibleForHandoff = true;
activity.EligibleForPublicIndexing = true;

// Inform system of Activity
activity.BecomeCurrent();

アクティビティの種類識別子を使用して、新しいアクティビティが作成されます。 次に、この状態を後で復元できるように、アクティビティを定義するいくつかのメタデータが作成されます。 その後、アクティビティにわかりやすいタイトルが与えられ、ユーザー情報に添付されます。 最後に、一部の機能が有効になり、アクティビティがシステムに送信されます。

上記のコードは、次の変更を行って、アクティビティにコンテキストを提供するメタデータを含むようにさらに拡張できます:

...

// Provide context
var attributes = new CSSearchableItemAttributeSet ("com.xamarin.location");
attributes.ThumbnailUrl = myThumbnailURL;
attributes.Keywords = new string [] { "software", "mobile", "language" };
activity.ContentAttributeSet = attributes;

// Inform system of Activity
activity.BecomeCurrent();

開発者がアプリと同じ情報を表示できる Web サイトがある場合、そのアプリには URL を含めることができます。また、アプリがインストールされていない他のデバイス (Handoff 経由) でコンテンツを表示できます:

// Restore on the web
activity.WebPageUrl = new NSUrl("http://xamarin.com/platform");

アクティビティの復元

アプリの検索結果 (NSUserActivity) をタップしてユーザーに応答するには、AppDelegate.cs ファイルを編集し、ContinueUserActivity メソッドをオーバーライドします。 次に例を示します。

public override bool ContinueUserActivity (UIApplication application, NSUserActivity userActivity, UIApplicationRestorationHandler completionHandler)
{

    // Take action based on the activity type
    switch (userActivity.ActivityType) {
    case "com.xamarin.platform":
        // Restore the state of the app here...
        break;
    }

    return true;
}

これが上記で作成したアクティビティと同じアクティビティの種類識別子 (com.xamarin.platform) であることを確認します。 アプリは、ユーザーが中断した場所に状態を復元するために、NSUserActivity に格納されている情報を使用します。

アクティビティを作成する利点

上記の最小限のコードで、アプリは 3 つの新しい iOS 10 機能を利用できるようになりました:

  • Handoff
  • Spotlight 検索
  • コンテキスト Siri リマインダー

次のセクションでは、他の 2 つの新しい iOS 10 機能を有効にする方法について説明します:

  • 場所の提案
  • コンテキスト Siri 要求

位置情報に基づく提案

上記のレストラン検索アプリの例を見てみましょう。 NSUserActivity が実装されており、すべてのメタデータと属性が正しく設定された場合、ユーザーは次のことを実行できます:

  1. 友達と待ち合わせるレストランをアプリで探します。
  2. ユーザーが Maps アプリに切り替えると、レストランの住所が宛先として自動的に提案されます。
  3. これは (NSUserActivity をサポートしている) サード パーティ製アプリでも機能するため、ユーザーはライドシェア アプリに切り替えることができ、そこでも、レストランの住所が目的地として自動的に提案されます。
  4. また、Siri にコンテキストを提供するため、ユーザーはレストラン アプリ内で Siri を呼び出し、"道順の取得..." を求めることができ、Siri は、ユーザーが表示しているレストランへの道順を提供します。

上記のすべての機能には共通点が 1 つあり、それらはすべて、提案の元の場所を示します。 上記の例の場合は、架空のレストラン レビュー アプリです。

watchOS 3 では、既存のフレームワークにいくつかの小さな修正と追加を加えることで、アプリでこの機能を利用できるように強化されています:

  • NSUserActivity には、アプリ内で表示される位置情報をキャプチャするための追加のフィールドがあります。
  • 位置情報をキャプチャするために、MapKit と CoreSpotlight にいくつかの追加が行われています。
  • 位置情報に対応する機能が、システム内の Siri、Maps、マルチタスキング、その他のアプリに追加されました。

位置情報に基づく提案を実装するには、上記と同じアクティビティ コードから始めます:

// Create App Activity
var activity = new NSUserActivity ("com.xamarin.platform");

// Define details
var info = new NSMutableDictionary ();
info.Add(new NSString("link"),new NSString("http://xamarin.com/platform"));

// Populate Activity
activity.Title = "The Xamarin Platform";
activity.UserInfo = info;

// Enable capabilities
activity.EligibleForSearch = true;
activity.EligibleForHandoff = true;
activity.EligibleForPublicIndexing = true;

// Provide context
var attributes = new CSSearchableItemAttributeSet ("com.xamarin.location");
attributes.ThumbnailUrl = myThumbnailURL;
attributes.Keywords = new string [] { "software", "mobile", "language" };
activity.ContentAttributeSet = attributes;

// Restore on the web
activity.WebPageUrl = new NSUrl("http://xamarin.com/platform");

// Inform system of Activity
activity.BecomeCurrent();

アプリで MapKit を使用している場合は、現在のマップ MKMapItem をアクティビティに追加するのと同じくらい簡単です:

// Save MKMapItem location
activity.MapItem = myMapItem;

アプリが MapKit を使用していない場合は、アプリ検索を採用し、以下の新しい位置情報の属性を指定できます:

// Provide context
var attributes = new CSSearchableItemAttributeSet ("com.xamarin.location");
...

attributes.NamedLocation = "Apple Inc.";
attributes.SubThoroughfare = "1";
attributes.Thoroughfare = "Infinite Loop";
attributes.City = "Cupertino";
attributes.StateOrProvince = "CA";
attributes.Country = "United States";
attributes.Latitude = 37.33072;
attributes.Longitude = 122.029674;
attributes.PhoneNumbers = new string[]{"(800) 275-2273"};
attributes.SupportsPhoneCalls = true;
attributes.SupportsNavigation = true;

上記のコードを詳しく見てみましょう。 まず、すべてのインスタンスで場所の名前が必要です:

attributes.NamedLocation = "Apple Inc.";

次に、テキスト ベースのインスタンス (QuickType キーボードなど) には、テキスト ベースの説明が必要です:

attributes.SubThoroughfare = "1";
attributes.Thoroughfare = "Infinite Loop";
attributes.City = "Cupertino";
attributes.StateOrProvince = "CA";
attributes.Country = "United States";

緯度と経度は省略可能ですが、アプリが誘導する正確な場所にユーザーがルーティングされるようにします:

attributes.Latitude = 37.33072;
attributes.Longitude = 122.029674;

電話番号を設定することで、アプリは Siri にアクセスできるため、ユーザーは "この場所に電話して" などのメッセージをアプリから Siri を呼び出すことができます:

attributes.PhoneNumbers = new string[]{"(800) 275-2273"};

最後に、アプリは、インスタンスがナビゲーションと電話での通話に適しているかどうかを示すことができます:

attributes.SupportsPhoneCalls = true;
attributes.SupportsNavigation = true;

アクティビティのベスト プラクティス

Apple は、アクティビティの操作について次のベスト プラクティスを提案します:

  • 遅延ペイロードの更新に NeedsSave を使用します。
  • 現在のアクティビティへの厳密な参照を保持してください。
  • 状態を復元するのに十分な情報のみを含む小さなペイロードのみを転送します。
  • 逆引き DNS 表記を使用して指定することで、アクティビティの種類識別子が一意でわかりやすいものになっていることを確認します。

場所の提案の使用

この次のセクションでは、システムの他の部分 (Maps アプリなど) や他のサード パーティ製アプリから取得した場所の提案について説明します。

ルーティング アプリと場所の提案

このセクションでは、ルーティング アプリ内から直接場所の提案を使用する方法について説明します。 ルーティング アプリでこの機能を追加するために、開発者は次のように既存の MKDirectionsRequest フレームワークを利用します:

  • マルチタスキングでアプリを昇格する。
  • アプリをルーティング アプリとして登録する。
  • MapKit MKDirectionsRequest オブジェクトを使用してアプリの起動を処理する。
  • watchOS に、ユーザー エンゲージメントに基づいてアプリを提案する機能を提供する。

MapKit MKDirectionsRequest オブジェクトを使用してアプリを起動すると、要求された場所への道案内をユーザーに自動的に提供し始めるか、ユーザーが道案内を簡単に開始できる UI を表示する必要があります。 次に例を示します。

using System;
using Foundation;
using UIKit;
using MapKit;
using CoreLocation;

namespace MonkeyChat
{
    [Register ("AppDelegate")]
    public class AppDelegate : UIApplicationDelegate, IUISplitViewControllerDelegate
    {
        ...

        public override bool OpenUrl (UIApplication app, NSUrl url, NSDictionary options)
        {
            if (MKDirectionsRequest.IsDirectionsRequestUrl (url)) {
                var request = new MKDirectionsRequest (url);
                var coordinate = request.Destination?.Placemark.Location?.Coordinate;
                var address = request.Destination.Placemark.AddressDictionary;
                if (coordinate.IsValid()) {
                    var geocoder = new CLGeocoder ();
                    geocoder.GeocodeAddress (address, (place, err) => {
                        // Handle the display of the address

                    });
                }
            }

            return true;
        }
    }
}

このコードを詳しく見てみましょう。 有効な目的地要求であるかどうかをテストします:

if (MKDirectionsRequest.IsDirectionsRequestUrl(url)) {

有効である場合は、URL から MKDirectionsRequest が作成されます:

var request = new MKDirectionsRequest(url);

watchOS 3 の新機能で、アプリに geo 座標を持たない住所を送信できます。そのため、開発者はアドレスをエンコードする必要があります:

var geocoder = new CLGeocoder();
geocoder.GeocodeAddress(address, (place, err)=> {
    // Handle the display of the address

});

まとめ

この記事では、プロアクティブな提案について説明し、開発者がそれらを使用して watchOS 用 Xamarin.iOS アプリにトラフィックを誘導する方法について説明しました。 プロアクティブな提案を実装する手順について説明し、使用ガイドラインを示しました。