Share via


Xamarin.iOS の Siri ショートカット

iOS 10 では、Apple が SiriKit を導入したことで、Siri と対話するメッセージング、VoIP 通話、支払い、トレーニング、乗車予約、写真検索アプリを構築できるようになりました。

iOS 11 では、SiriKit はより多くの種類のアプリのサポートを受け、UI カスタマイズの柔軟性が向上しました。

iOS 12 では Siri ショートカットが追加され、すべての種類のアプリで機能を Siri に公開できます。 Siri は、特定のアプリ ベースのタスクがユーザーに最も関連するタイミングを学習し、この知識を使用して、ショートカットを使用して潜在的なアクションを提案します。 ショートカットをタップするか、音声コマンドで呼び出すと、アプリが開くか、バックグラウンド タスクが実行されます。

ショートカットは、ユーザーが共通のタスクを実行する機能を促進する目的で使用されるべきです。多くの場合、対象のアプリを開く必要すらなく実行できます。

サンプル アプリ: Soup Chef

Siri ショートカットについて理解を深めるために、Soup Chef サンプル アプリを見てみましょう。 Soup Chef を使用すると、ユーザーは架空のスープ レストランからの注文を行い、注文履歴を表示し、Siri と対話してスープを注文するときに使用するフレーズを定義できます。

ヒント

iOS 12 シミュレーターまたはデバイスで Soup Chef をテストする前に、次の 2 つの設定を有効にします。これは、ショートカットをデバッグするときに役立ちます。

  • [設定] アプリで、[開発] >[最近使ったショートカットを表示する] を有効にします。
  • [設定] アプリで、[開発者] > [ロック画面に寄付を表示する] を有効にします。

これらのデバッグ設定を使用すると、iOS のロック画面と検索画面で、最近作成された (予測される代わりに) ショートカットを簡単に見つけることができます。

サンプル アプリを使用するには:

  • iOS 12 シミュレーターまたはデバイスに、Soup Chef サンプル アプリをインストールして実行します。
  • 右上隅にある + ボタンをクリックして、新しい注文を作成します。
  • スープの種類を選択し、数量とオプションを指定して、[注文する] をタップします。
  • [注文履歴] 画面で、新しく作成した注文をタップして詳細を表示します。
  • 注文の詳細画面の下部にある [Siri に追加] をタップします。
  • 音声フレーズを録音して注文に関連付け、[完了] をタップします。
  • Soup Chef を最小化し、Siri を呼び出し、録音した音声フレーズを使用して注文を再度行います。
  • Siri が注文を完了したら、Soup Chef をもう一度開き、[注文履歴] 画面に新しい注文が表示されていることを確認します。

このサンプル アプリでは、次を示します。

Info.plist と Entitlements.plist

Soup Chef コードをさらに詳しく調べる前に、Info.plist ファイルと Entitlements.plist ファイルを見てみましょう。

Info.plist

SoupChef プロジェクトの Info.plist ファイルは、バンドル識別子com.xamarin.SoupChef として定義します。 このバンドル識別子は、このドキュメントで後述する Intents と Intents UI の拡張機能のバンドル識別子のプレフィックスとして使用されます。

Info.plist ファイルには、次のエントリも含まれています。

<key>NSUserActivityTypes</key>
<array>
    <string>OrderSoupIntent</string>
    <string>com.xamarin.SoupChef.viewMenu</string>
</array>

この NSUserActivityTypes キーと値のペアは、Soup Chef が、OrderSoupIntent と "com.xamarin.SoupChef.viewMenu" の ActivityType を持つ NSUserActivity の処理方法を認識していることを示します。

アプリ自体に渡されるアクティビティとカスタム意図は、拡張機能とは対照的に、AppDelegate (ContinueUserActivity メソッドによる UIApplicationDelegate) で処理されます。

Entitlements.plist

SoupChef プロジェクトの Entitlements.plist ファイルには、次のエントリが含まれています。

<key>com.apple.security.application-groups</key>
<array>
    <string>group.com.xamarin.SoupChef</string>
</array>
<key>com.apple.developer.siri</key>
<true/>

この構成は、アプリが "group.com.xamarin.SoupChef" アプリ グループを使用していることを示します。 SoupChefIntents アプリ拡張機能では、この同じアプリ グループを使用します。これにより、2 つのプロジェクトを共有できます。NSUserDefaults データ。

com.apple.developer.siri キーは、アプリが Siri と対話することを示します。

Note

SoupChef プロジェクトのビルド構成では、カスタム エンタイトルメントEntitlements.plist に設定されます。

NSUserActivity ショートカットを使用してアプリを開く

アプリを開いて特定のコンテンツを表示するショートカットを作成するには、NSUserActivity を作成し、ショートカットを開く画面のビュー コントローラーにアタッチします。

NSUserActivity の設定

メニュー画面で、SoupMenuViewControllerNSUserActivity を作成し、ビュー コントローラーの UserActivity プロパティに割り当てます。

public override void ViewDidLoad()
{
    base.ViewDidLoad();
    UserActivity = NSUserActivityHelper.ViewMenuActivity;
}

UserActivity プロパティを設定すると、アクティビティが Siri に寄付されます。 この寄付から、Siri は、このアクティビティがユーザーに関連するタイミングと場所に関する情報を取得し、将来より適切に提案することを学習します。

NSUserActivityHelper は、SoupKit クラス ライブラリ内の SoupChef ソリューションに含まれるユーティリティ クラスです。 NSUserActivity を作成し、Siri に関連するさまざまなプロパティを設定して検索します。

public static string ViewMenuActivityType = "com.xamarin.SoupChef.viewMenu";

public static NSUserActivity ViewMenuActivity {
    get
    {
        var userActivity = new NSUserActivity(ViewMenuActivityType)
        {
            Title = NSBundleHelper.SoupKitBundle.GetLocalizedString("ORDER_LUNCH_TITLE", "View menu activity title"),
            EligibleForSearch = true,
            EligibleForPrediction = true
        };

        var attributes = new CSSearchableItemAttributeSet(NSUserActivityHelper.SearchableItemContentType)
        {
            ThumbnailData = UIImage.FromBundle("tomato").AsPNG(),
            Keywords = ViewMenuSearchableKeywords,
            DisplayName = NSBundleHelper.SoupKitBundle.GetLocalizedString("ORDER_LUNCH_TITLE", "View menu activity title"),
            ContentDescription = NSBundleHelper.SoupKitBundle.GetLocalizedString("VIEW_MENU_CONTENT_DESCRIPTION", "View menu content description")
        };
        userActivity.ContentAttributeSet = attributes;

        var phrase = NSBundleHelper.SoupKitBundle.GetLocalizedString("ORDER_LUNCH_SUGGESTED_PHRASE", "Voice shortcut suggested phrase");
        userActivity.SuggestedInvocationPhrase = phrase;
        return userActivity;
    }
}

特に、次の機能に注意してください。

  • EligibleForPredictiontrue に設定すると、Siri がこのアクティビティを予測し、ショートカットとして表示できることを示します。
  • ContentAttributeSet 配列は、iOS の検索結果に NSUserActivity を含めるために使用される標準的な CSSearchableItemAttributeSet です。
  • SuggestedInvocationPhrase は、ショートカットにフレーズを割り当てるときに、Siri が潜在的な選択肢としてユーザーに提案するフレーズです。

NSUserActivity ショートカットの処理

ユーザーによって呼び出された NSUserActivity ショートカットを処理するため、iOS アプリケーションは、渡された NSUserActivity オブジェクトの ActivityType フィールドに基づいて応答して、AppDelegate クラスの ContinueUserActivity メソッドをオーバーライドする必要があります。

public override bool ContinueUserActivity(UIApplication application, NSUserActivity userActivity, UIApplicationRestorationHandler completionHandler)
{
    // ...
    else if (userActivity.ActivityType == NSUserActivityHelper.ViewMenuActivityType)
    {
        HandleUserActivity();
        return true;
    }
    // ...
}

このメソッドは HandleUserActivity を呼び出し、ここでメニュー画面にセグエを検索して呼び出します。

void HandleUserActivity()
{
    var rootViewController = Window?.RootViewController as UINavigationController;
    var orderHistoryViewController = rootViewController?.ViewControllers?.FirstOrDefault() as OrderHistoryTableViewController;
    if (orderHistoryViewController is null)
    {
        Console.WriteLine("Failed to access OrderHistoryTableViewController.");
        return;
    }
    var segue = OrderHistoryTableViewController.SegueIdentifiers.SoupMenu;
    orderHistoryViewController.PerformSegue(segue, null);
}

NSUserActivity へのフレーズの割り当て

NSUserActivity にフレーズを割り当てるには、iOS の [設定] アプリを開き、[Siri/Search] > [ショートカット] を選択します。 次に、ショートカット (この場合は "Order Lunch") を選択し、フレーズを記録します。

Siri を呼び出してこのフレーズを使用すると、メニュー画面に Soup Chef が開きます。

カスタム意図ショートカットを使用したタスクの実行

カスタム意図の定義

ユーザーがアプリに関連する特定のタスクをすばやく完了できるようにするショートカットを提供するには、カスタム意図を作成します。 カスタム意図は、ユーザーが完了したいタスク、そのタスクに関連するパラメーター、およびタスクの実行に起因する潜在的な応答を表します。 カスタム意図の定義方法に応じて、それを呼び出すと、アプリを開くか、バックグラウンド タスクを実行できます。

Xcode 10 を使用してカスタム意図を作成します。 SoupChef リポジトリでは、OrderSoupIntentCodeGen (Objective-C プロジェクト) でカスタム意図が定義されます。 このプロジェクトを開き、プロジェクト ナビゲーターIntents.intentdefinition ファイルを選択して、OrderSoup 意図を表示します。

次の機能に注意してください。

  • この意図には、OrderCategory が含まれています。 カスタム意図に使用できる定義済みのカテゴリはさまざまです。自分のカスタム意図が有効にするタスクに最もよく一致するものを選びます。 このソリューションはスープ注文アプリであるため、OrderSoupIntentOrder を使用します。
  • [確認] チェックボックスは、タスクを実行する前に、Siri が確認を要求する必要があるかどうかを示します。 Soup Chef の Order Soup 意図では、ユーザーが購入を行うため、このオプションが有効になります。
  • .intentdefinition ファイルの Parameters セクションで、ショートカットに関連するパラメータを定義します。 スープを注文するには、スープの種類、量、および関連するオプションを Soup Chef が認識できる必要があります。 各パラメータには型があります。定義済みの型で表すことができないパラメータは Custom として設定されます。
  • ショートカットの種類インターフェイスでは、ショートカットを提案するときに Siri が使用できるさまざまなパラメータの組み合わせについて説明します。 関連する Title セクションと Subtitle セクションを使用すると、ユーザーに推奨されるショートカットを提示するときに Siri が使用するメッセージを定義できます。
  • [バックグラウンド実行をサポートします] チェック ボックスは、ユーザーの操作を強化するために、アプリを開かずに実行できるショートカットに対して選択する必要があります。

カスタム意図の応答の定義

OrderSoup 意図の下に入れ子になった Response 項目は、スープ注文の結果として生じる可能性のある応答を表します。

OrderSoup 意図の応答定義で、次の機能に注目してください。

  • 応答の Properties を使用して、ユーザーに返すメッセージをカスタマイズできます。 OrderSoup 意図の応答には、soup プロパティと waitTime プロパティがあります。
  • 応答テンプレートは、意図のタスクが完了した後の状態を示すために使用できるさまざまな成功メッセージと失敗メッセージを指定します。
  • 成功を示す応答の [成功] チェックボックスをオンにする必要があります。
  • OrderSoupIntent 成功応答では、soup プロパティと waitTime プロパティを使用して、スープの注文の準備が整うタイミングを説明するわかりやすい便利なメッセージを提供します。

カスタム意図のコードの生成

このカスタム意図定義を含む Xcode プロジェクトをビルドすると、Xcode は、カスタム意図とその応答とプログラムで対話するために使用できるコードを生成します。

この生成されたコードを表示するには、次の手順を実行します。

  • AppDelegate.m を開きます。
  • カスタム意図のヘッダー ファイルにインポートを追加します: #import "OrderSoupIntent.h"
  • クラス内の任意のメソッドで、OrderSoupIntent への参照を追加します。
  • OrderSoupIntent を右クリックし、[定義へ移動] を選択します。
  • 新しく開いたファイル OrderSoupIntent.h を右クリックし、[Show in Finder] を選択します。
  • この操作により、.h を含む Finder ウィンドウと、生成されたコードを含む .m ファイルが開きます。

この生成されたコードには、次のものが含まれます。

  • OrderSoupIntent - カスタム意図を表すクラス。
  • OrderSoupIntentHandling - 意図を実行する必要があることを確認するために使用されるメソッドを定義するプロトコルと、それを実際に実行するメソッド。
  • OrderSoupIntentResponseCode - さまざまな応答状態を定義する列挙型。
  • OrderSoupIntentResponse - 意図の実行に対する応答を表すクラス。

カスタム意図へのバインドの作成

Xamarin.iOS アプリで Xcode によって生成されたコードを使用するには、その C# バインドを作成します。

スタティック ライブラリと C# バインド定義の作成

SoupChef リポジトリで、OrderSoupIntentStaticLib フォルダーを確認し、OrderSoupIntentStaticLib.xcodeproj Xcode プロジェクトを開きます。

この Cocoa Touch スタティック ライブラリ プロジェクトには、Xcode によって生成された OrderSoupIntent.h ファイルと OrderSoupIntent.m ファイルが含まれています。

スタティック ライブラリ プロジェクトのビルド設定の構成

Xcode プロジェクト ナビゲーターで、最上位のプロジェクト OrderSoupIntentStaticLib を選択し、[ビルド フェーズ] > [コンパイル ソース] に移動します。 OrderSoupIntent.m (OrderSoupIntent.h をインポート) がここに一覧表示されていることに注意してください。 [バイナリとライブラリをリンク] に、Intents.frameworkFoundation.framework が含まれていることに注意してください。 これらの設定を適切に設定すると、フレームワークが正しくビルドされます。

スタティック ライブラリのビルドと C# バインド定義の生成

スタティック ライブラリをビルドし、それに対して C# バインド定義を生成するには、次の手順に従います。

  • [Objective Sharpie をインストールする] は、Xcode によって作成された .h ファイルおよび .m ファイルからバインド定義を生成するために使用されるツールです。

  • Xcode 10 コマンド ライン ツールを使用するように、システムを構成します。

    警告

    選択したコマンド ライン ツールを更新すると、システムにインストールされているすべてのバージョンの Xcode に影響します。 Soup Chef サンプル アプリの使用が完了したら、この設定を元の構成に戻してください。

    • Xcode で、[Xcode] >[基本設定] > [場所] を選択し、システムで利用可能な最新の Xcode 10 にコマンド ライン ツールをインストールに設定します。
  • ターミナルで、cdOrderSoupIntentStaticLib ディレクトリに実行します。

  • make は、次をビルドします。

    • スタティック ライブラリ libOrderSoupIntentStaticLib.a
    • bo 出力ディレクトリで、C# バインドの定義は次のようになります。
      • ApiDefinitions.cs
      • StructsAndEnums.cs

この静的ライブラリとそれに関連付けられているバインド定義に依存する OrderSoupIntentBindings プロジェクトは、これらの項目を自動的にビルドします。 ただし、上記のプロセスを手動で実行すると、想定どおりにビルドされます。

スタティック ライブラリの作成と Objective Sharpie を使用した C# バインド定義の作成の詳細については、「チュートリアル - iOS Objective-Cライブラリのバインド」をご覧ください。

バインド ライブラリの作成

スタティック ライブラリと C# バインド定義が作成されたので、Xamarin.iOS プロジェクトで Xcode で生成された意図関連コードを使用するために必要な残りの部分は、バインド ライブラリです。

Soup Chef リポジトリで、SoupChef.sln ファイルを開きます。 特に、このソリューションには、OrderSoupIntentBinding (前に生成されたスタティック ライブラリへのバインド ライブラリ) が含まれています。

特に、このプロジェクトには次のものが含まれていることに注意してください。

  • ApiDefinitions.cs - 前に Objective Sharpie によって生成され、このプロジェクトに追加されたファイル。 このファイルのビルド アクションは、ObjcBindingApiDefinition に設定されます。

  • StructsAndEnums.cs - 前に Objective Sharpie によって生成され、このプロジェクトに追加された別のファイル。 このファイルのビルド アクションは、ObjcBindingCoreSource に設定されます。

  • 先ほどビルドしたスタティック ライブラリである libOrderSoupIntentStaticLib.a へのネイティブ参照。 ネイティブ参照プロパティを更新し、次の値を指定します。

    1. フレームワーク = Foundation Intents
    2. スマート リンク = On
    3. 強制読み込み = On
    4. 種類 = Static

Note

ApiDefinitions.csStructsAndEnums.cs の両方に、[Watch (5,0), iOS (12,0)] などの属性が含まれています。 Objective Sharpie によって生成されたこれらの属性は、このプロジェクトには必要ないため、コメントアウトされています。

C# バインド ライブラリの作成の詳細については、「チュートリアル - iOS Objective-C ライブラリのバインド」をご覧ください。

SoupChef プロジェクトには、OrderSoupIntentBinding への参照が含まれていることに注意してください。つまり、C# で、含まれるクラス、インターフェイス、列挙型にアクセスできるようになりました。

  • OrderSoupIntent
  • OrderSoupIntentHandling
  • OrderSoupIntentResponse
  • OrderSoupIntenseResponseCode

Swift フレームワークの作成

意図定義のネイティブ コードは、既定でネイティブ プロジェクトの言語を使用して Xcode によって生成されます。 Swift プロジェクトで Intents.intentdefinition ファイルを定義すると、Xcode によって、必要なすべてのクラスを含む 1 つの Swift ファイルが生成されます。このファイルを使用して Swift フレームワークを作成できます。

ヒント

Xcode ビルド設定で、生成された意図コードに必要な言語を選択できます。 [Intent target]\(意図ターゲット\) > [ビルド設定] > [Intent Definition Compiler - Code Generation]\(意図定義コンパイラー - コードの生成\) に移動し、Swift または Objective-C を選択します。 また、ターゲット言語に合わせて自動で保持することもできます。

Swift フレームワークを作成するプロセスは、前に説明したプロセスと似ています。

  1. 新しい Swift フレームワーク プロジェクトを作成します。
  2. 自動的に生成された Swift ファイルと意図コードをこのプロジェクトにコピーします。このファイルは、ここの説明に従って見つけることができます。
  3. Objective-Cブリッジ ヘッダーを有効にして、Objective-C sharpie ヘッダー ファイルに必要なフレームワークが自動的に生成されるようにします。

フレームワークがビルドされたら、前述の同じ手順に従って Xamarin バインドを作成します。 Swift フレームワークのバインドの作成の詳細については、ここをご覧ください。

ソリューションへの意図定義ファイルの追加

C# SoupChef ソリューションでは、SoupKit プロジェクトに、アプリとその拡張機能の間で共有されるコードが含まれています。 Intents.intentdefinition ファイルは、SoupKitBase.lproj ディレクトリに配置されており、コンテンツビルド アクションがあります。 ビルド プロセスでは、このファイルが Soup Chef アプリ バンドルにコピーされます。このバンドルは、アプリが正しく機能するために必要です。

意図の寄付

Siri がショートカットを提案するには、まずショートカットが関連するタイミングを理解する必要があります。

Siri にこの理解を与えるために、Soup Chef は、ユーザーがスープを注文するたびに、Siri に意図を寄付します。 この寄付 (寄付されたとき、寄付された場所、含まれているパラメータ) に基づいて、Siri は、将来のショートカットを提案するタイミングを学びます。

SoupChefSoupOrderDataManager クラスを使用して寄付を行います。 ユーザーのスープ注文を行うために呼び出されると、次に PlaceOrder メソッドは DonateInteraction を呼び出します。

void DonateInteraction(Order order)
{
    var interaction = new INInteraction(order.Intent, null);
    interaction.Identifier = order.Identifier.ToString();
    interaction.DonateInteraction((error) =>
    {
        // ...
    });
}

意図をフェッチすると、意図は INInteraction にラップされます。 INInteractionIdentifier が与えられます。これは、注文の一意の ID と照合されます (後で無効になった意図の寄付を削除するときに役立ちます)。 その後、この対話は Siri に寄付されます。

order.Intent ゲッターの呼び出が、注文を表す OrderSoupIntent をフェッチします。これには、その QuantitySoupOptions、イメージ、Siri が意図に関連付けるためのフレーズをユーザーが記録するときに候補として使用する呼び出しフレーズが設定されています。

public OrderSoupIntent Intent
{
    get
    {
        var orderSoupIntent = new OrderSoupIntent();
        orderSoupIntent.Quantity = new NSNumber(Quantity);
        orderSoupIntent.Soup = new INObject(MenuItem.ItemNameKey, MenuItem.LocalizedString);

        var image = UIImage.FromBundle(MenuItem.IconImageName);
        if (!(image is null))
        {
            var data = image.AsPNG();
            orderSoupIntent.SetImage(INImage.FromData(data), "soup");
        }

        orderSoupIntent.Options = MenuItemOptions
            .ToArray<MenuItemOption>()
            .Select<MenuItemOption, INObject>(arg => new INObject(arg.Value, arg.LocalizedString))
            .ToArray<INObject>();

        var comment = "Suggested phrase for ordering a specific soup";
        var phrase = NSBundleHelper.SoupKitBundle.GetLocalizedString("ORDER_SOUP_SUGGESTED_PHRASE", comment);
        orderSoupIntent.SuggestedInvocationPhrase = String.Format(phrase, MenuItem.LocalizedString);

        return orderSoupIntent;
    }
}

無効な寄付の削除

Siri が役に立たない、または混乱を招くショートカットの提案をしないように、無効になった寄付は削除するのが重要です。

Soup Chef では、[メニューの構成] 画面を使用して、メニュー品目を使用不可としてマークできます。 Siri は、使用できないメニュー品目を注文するショートカットを提案すべきでないため、SoupMenuManagerRemoveDonation メソッドは、使用できなくなったメニュー品目の寄付を削除します。 アプリは、次の方法でこの機能を実装します。

  • 現在使用できないメニュー品目に関連付けられている注文を検索します。
  • 識別子を取得します。
  • 同じ識別子を持つ操作を削除します。
void RemoveDonation(MenuItem menuItem)
{
    if (!menuItem.IsAvailable)
    {
        Order[] orderHistory = OrderManager?.OrderHistory.ToArray<Order>();
        if (orderHistory is null)
        {
            return;
        }

        string[] orderIdentifiersToRemove = orderHistory
            .Where<Order>((order) => order.MenuItem.ItemNameKey == menuItem.ItemNameKey)
            .Select<Order, string>((order) => order.Identifier.ToString())
            .ToArray<string>();

        INInteraction.DeleteInteractions(orderIdentifiersToRemove, (error) =>
        {
            if (!(error is null))
            {
                Console.WriteLine($"Failed to delete interactions with error {error.ToString()}");
            }
            else
            {
                Console.WriteLine("Successfully deleted interactions");
            }
        });
    }
}

寄付の成功の検証

ソリューションには、複数のプロジェクトと特定の構成が含まれています。 場合によっては、構成が不完全なためにアプリケーションがクラッシュする可能性があります。また、対話を警告なしに寄付できないこともあります。 寄付が成功したことを検証するのが重要であり、iOS 開発者設定がそれに役立ちます。 [設定] > [開発者] に移動し、次の開発者オプションを有効にして、最近の寄付とショートカットを表示します。

  • 最近使ったショートカットを表示する
  • ロック画面に寄付を表示する

有効にすると、成功したすべての寄付がロック画面と Siri の提案オプションの下に表示されます。 アプリケーションの実行後に寄付が表示されない場合は、次のトラブルシューティング ケースを確認してください。

  1. アプリが次のエラーで OrderSoupIntent を作成できません。

    型 'NativeLibrary.OrderSoupIntent' のネイティブ インスタンスを作成できませんでした: ネイティブ クラスが読み込まれていません。

    このエラーは、Xamarin が Xamarin バインドを介してネイティブ クラスを読み込めないことを意味します。 これを修正するには、ネイティブ ライブラリに必要なコードが含まれていることを確認し、バインド プロジェクトによって参照され、適切なフラグが設定されていることを確認します。ここで説明されているように、Force Load フラグを On に設定します。

  2. アプリは、次のエラーで意図クラスの読み込まれたネイティブ インスタンスを初期化できません。

    型 'NativeLibrary.OrderSoupIntent' のインスタンスを初期化できませんでした: ネイティブ 'init' メソッドが nil を返しました。

    この問題は、不足している意図定義ファイルに関連しています。 Xamarin アプリには、ここで説明されているように、Content 型の元の意図定義ファイルを含める必要があります。

  3. アプリは意図を作成し、クラッシュせずに寄付メソッドを呼び出しますが、コンソール出力には不明な意図の種類に関する警告が表示され、寄付は行われません。

    有効なショートカットの種類がない OrderSoupIntent との対話を寄付できません

    この問題を解決するには、plist で意図が適切に定義されており、Siri のエンタイトルメントを有効にし、プロジェクト設定を使用して現在のビルド構成に対して選択する必要があります。

    アプリの info.plist は次のとおりです。

    <key>NSUserActivityTypes</key>
    <array>
        <string>ScheduleMeetingIntent</string>
    </array>
    

    Siri 機能を備えたアプリの Entitlements.plist は次のとおりです。

    <key>com.apple.developer.siri</key>
    <true/>
    

    対象のビルド構成に対してカスタム エンタイトルメントを選択する必要があります。 [プロジェクト設定] >[ビルド] > [iOS バンドル署名] に移動し、[カスタム エンタイトルメント] を必要なエンタイトルメントを含む Entitlements.plist ファイルに設定します。

Intents の拡張機能の作成

Siri が意図を呼び出すときに実行されるコードは Intents の拡張機能に配置されます。これは、新しいプロジェクトとして、Soup Chef などの既存の Xamarin.iOS アプリと同じソリューションに追加できます。 SoupChef ソリューションでは、拡張機能は SoupChefIntents と呼ばれます。

SoupChefIntents - Info.plist と Entitlements.plist

SoupChefIntents - Info.plist

SoupChefIntents プロジェクトの Info.plist では、バンドル識別子com.xamarin.SoupChef.SoupChefIntents として定義されます。

Info.plist ファイルには、次のエントリも含まれています。

<key>NSExtension</key>
<dict>
    <key>NSExtensionAttributes</key>
    <dict>
        <key>IntentsRestrictedWhileLocked</key>
        <array/>
        <key>IntentsSupported</key>
        <array>
            <string>OrderSoupIntent</string>
        </array>
        <key>IntentsRestrictedWhileProtectedDataUnavailable</key>
        <array/>
    </dict>
    <key>NSExtensionPointIdentifier</key>
    <string>com.apple.intents-service</string>
    <key>NSExtensionPrincipalClass</key>
    <string>IntentHandler</string>
</dict>

上記の Info.plist は次のとおりです。

  • IntentsRestrictedWhileLocked に、デバイスのロックが解除されたときに処理される意図が一覧表示されます。
  • IntentsSupported に、この拡張機能によって処理される意図が一覧表示されます。
  • NSExtensionPointIdentifier は、アプリ拡張機能の種類を指定します。 詳細については、Apple のドキュメントをご覧ください。
  • NSExtensionPrincipalClass は、この拡張機能でサポートされている意図を処理するために使用するクラスを指定します。
SoupChefIntents - Entitlements.plist

SoupChefIntents プロジェクトの Entitlements.plist には、アプリ グループ機能があります。 この機能は、SoupChef プロジェクトと同じアプリ グループを使用するように構成されています。

<key>com.apple.security.application-groups</key>
<array>
    <string>group.com.xamarin.SoupChef</string>
</array>

NSUserDefaultsSoup Chef でデータが保持されます。 アプリとアプリ拡張機能の間でデータを共有するために、Entitlements.plist ファイル内の同じアプリ グループを参照します。

Note

SoupChefIntents プロジェクトのビルド構成では、カスタム エンタイトルメントEntitlements.plist に設定されます。

OrderSoupIntent バックグラウンド タスクの処理

Intents の拡張機能は、カスタム意図に基づいてショートカットに必要なバックグラウンド タスクを実行します。

Siri は、IntentHandler クラス (Info.plistNSExtensionPrincipalClass として定義されている) GetHandler メソッドを呼び出し、OrderSoupIntentHandling を拡張するクラスのインスタンスを取得します。これは、OrderSoupIntent を処理するために使用できます。

[Register("IntentHandler")]
public class IntentHandler : INExtension
{
    public override NSObject GetHandler(INIntent intent)
    {
        if (intent is OrderSoupIntent)
        {
            return new OrderSoupIntentHandler();
        }
        throw new Exception("Unhandled intent type: ${intent}");
    }

    protected IntentHandler(IntPtr handle) : base(handle) { }
}

共有コードの SoupKit プロジェクトで定義された OrderSoupIntentHandler は、次の 2 つの重要なメソッドを実装します。

  • ConfirmOrderSoup - 意図に関連付けられているタスクを実際に実行する必要があるかどうかを確認します
  • HandleOrderSoup - スープの注文を行い、渡された完了ハンドラーを呼び出してユーザーに応答します。

アプリを開く OrderSoupIntent の処理

アプリは、バックグラウンドで実行されない意図を適切に処理する必要があります。 これらの意図は、NSUserActivity ショートカットと同じ方法で、AppDelegateContinueUserActivity メソッドで処理されます。

public override bool ContinueUserActivity(UIApplication application, NSUserActivity userActivity, UIApplicationRestorationHandler completionHandler)
{
    var intent = userActivity.GetInteraction()?.Intent as OrderSoupIntent;
    if (!(intent is null))
    {
        HandleIntent(intent);
        return true;
    }
    // ...
}  

カスタム意図のユーザー インターフェイスの提供

Intents UI の拡張機能は、Intents の拡張機能のカスタム ユーザー インターフェイスを提供します。 SoupChef ソリューションでは、SoupChefIntentsUI は、SoupChefIntents のインターフェイスを提供する Intents UI の拡張機能です。

SoupChefIntentsUI - Info.plist と Entitlements.plist

SoupChefIntentsUI - Info.plist

SoupChefIntentsUI プロジェクトの Info.plist では、バンドル識別子com.xamarin.SoupChef.SoupChefIntentsui として定義されます。

Info.plist ファイルには、次のエントリも含まれています。

<key>NSExtension</key>
<dict>
    <key>NSExtensionAttributes</key>
    <dict>
        <key>IntentsSupported</key>
        <array>
            <string>OrderSoupIntent</string>
        </array>
        <!-- ... -->
    </dict>
    <key>NSExtensionPointIdentifier</key>
    <string>com.apple.intents-ui-service</string>
    <key>NSExtensionMainStoryboard</key>
    <string>MainInterface</string>
</dict>

上記の Info.plist は次のとおりです。

  • IntentsSupported は、OrderSoupIntent が、この Intents UI の拡張機能によって処理されることを示します。
  • NSExtensionPointIdentifier は、アプリ拡張機能の種類を指定します。 詳細については、Apple のドキュメントをご覧ください。
  • NSExtensionMainStoryboard は、この拡張機能のプライマリ インターフェイスを定義するストーリーボードを指定します

SoupChefIntentsUI - Entitlements.plist

SoupChefIntentsUI プロジェクトには Entitlements.plist ファイルは必要ありません。

ユーザー インターフェイスの作成

SoupChefIntentsUIInfo.plistNSExtensionMainStoryboard キーを MainInterface に設定するため、MainInterace.storyboard ファイルは Intents UI の拡張機能のインターフェイスを定義します。

このストーリーボードには、IntentViewController 型の単一のビュー コントローラーがあります。 次の 2 つのビューを参照します。

  • InvoiceView 型の invoiceView
  • ConfirmOrderView 型の confirmationView

Note

invoiceViewconfirmationView の各インターフェイスは、セカンダリ ビューとして、Main.storyboard で定義されています。 Visual Studio for Mac と Visual Studio 2017 では、セカンダリ ビューの表示または編集はサポートされていません。これを行うには、Xcode の Interface Builder で Main.storyboard を開きます。

IntentViewController が実装する IINUIHostedViewControlling インターフェイス。Siri の意図を操作するときにカスタム インターフェイスを提供するために使用されます。 .ConfigureView メソッドが、このインターフェイスをカスタマイズするために呼び出され、対話が確認されたか (INIntentHandlingStatus.Ready) 正常に実行されたか (INIntentHandlingStatus.Success) に応じて、確認または請求書が表示されます。

[Export("configureViewForParameters:ofInteraction:interactiveBehavior:context:completion:")]
public void ConfigureView(
    NSSet<INParameter> parameters,
    INInteraction interaction,
    INUIInteractiveBehavior interactiveBehavior,
    INUIHostedViewContext context,
    INUIHostedViewControllingConfigureViewHandler completion)
{
    // ...
    if (interaction.IntentHandlingStatus == INIntentHandlingStatus.Ready)
    {
        desiredSize = DisplayInvoice(order, intent);
    }
    else if(interaction.IntentHandlingStatus == INIntentHandlingStatus.Success)
    {
        var response = interaction.IntentResponse as OrderSoupIntentResponse;
        if (!(response is null))
        {
            desiredSize = DisplayOrderConfirmation(order, intent, response);
        }
    }
    completion(true, parameters, desiredSize);
}

ヒント

このメソッドの詳細については、ConfigureViewApple の WWDC 2017 プレゼンテーション「SiriKit の新機能」をご覧ください。

音声ショートカットの作成

Soup Chef には、各注文に音声ショートカットを割り当てるインターフェイスが用意されており、Siri でスープを注文できます。 実際、音声ショートカットの録音と割り当てに使用されるインターフェイスは iOS によって提供され、カスタム コードはほとんど必要としません。

OrderDetailViewController では、ユーザーがテーブルの [Siri に追加] 行をタップすると、RowSelected メソッドは、音声ショートカットを追加または編集するための画面を表示します。

public override void RowSelected(UITableView tableView, NSIndexPath indexPath)
{
    // ...
    else if (TableConfiguration.Sections[indexPath.Section].Type == OrderDetailTableConfiguration.SectionType.VoiceShortcut)
    {
        INVoiceShortcut existingShortcut = VoiceShortcutDataManager?.VoiceShortcutForOrder(Order);
        if (!(existingShortcut is null))
        {
            var editVoiceShortcutViewController = new INUIEditVoiceShortcutViewController(existingShortcut);
            editVoiceShortcutViewController.Delegate = this;
            PresentViewController(editVoiceShortcutViewController, true, null);
        }
        else
        {
            // Since the app isn't yet managing a voice shortcut for
            // this order, present the add view controller
            INShortcut newShortcut = new INShortcut(Order.Intent);
            if (!(newShortcut is null))
            {
                var addVoiceShortcutVC = new INUIAddVoiceShortcutViewController(newShortcut);
                addVoiceShortcutVC.Delegate = this;
                PresentViewController(addVoiceShortcutVC, true, null);
            }
        }
    }
}

現在表示されている順序に対して既存の音声ショートカットが存在するかどうかに基づいて、RowSelectedINUIEditVoiceShortcutViewController または INUIAddVoiceShortcutViewController 型のビュー コントローラーを表示します。 いずれの場合も、OrderDetailViewController はビュー コントローラーの Delegate としてそれ自体を設定します。そのため、次も実装します: IINUIAddVoiceShortcutViewControllerDelegate および IINUIEditVoiceShortcutViewControllerDelegate

デバイスでのテスト

デバイスで Soup Chef を実行するには、このセクションの手順に従います。 自動プロビジョニングに関する注意事項も参照してください。

アプリ グループ、アプリ ID、プロビジョニング プロファイル

Apple 開発者ポータル証明書、ID、プロファイル セクションで、次の手順を実行します。

  • Soup Chef アプリとその拡張機能の間でデータを共有するアプリ グループを作成します。 例: group.com.yourcompanyname.SoupChef

  • 3 つのアプリ ID を作成します。1 つはアプリ自体用、もう 1 つは Intents の拡張機能用、もう 1 つは Intents UI の拡張機能用です。 次に例を示します。

    • アプリ: com.yourcompanyname.SoupChef

      • このアプリ ID に、SiriKit とアプリ グループの機能を割り当てます。
    • Intents の拡張機能: com.yourcompanyname.SoupChef.Intents

      • このアプリ ID に、アプリ グループ機能を割り当てます。
    • Intents UI の拡張機能: com.yourcompanyname.SoupChef.Intentsui

      • このアプリ ID には特別な機能は必要ありません。
  • 上記のアプリ ID を作成したら、アプリに割り当てられているアプリ グループ機能と Intents の拡張機能を編集し、先ほど作成した特定のアプリ グループを指定します。

  • 新しいアプリ ID ごとに 1 つずつ、3 つの新しい開発プロビジョニング プロファイルを作成します。

  • これらのプロビジョニング プロファイルをダウンロードし、各プロファイルをダブルクリックしてインストールします。 Visual Studio for Mac または Visual Studio 2017 が既に実行されている場合は、再起動して、新しいプロビジョニング プロファイルが登録されていることを確認します。

Info.plist、Entitlements.plist、ソース コードの編集

Visual Studio for Mac または Visual Studio 2017 で、次の手順を実行します。

  • ソリューション内のさまざまな Info.plist ファイルを更新します。 アプリ、Intents の拡張機能、Intents UI の拡張機能のバンドル識別子を、前に定義したアプリ ID に設定します。

    • アプリ: com.yourcompanyname.SoupChef
    • Intents の拡張機能: com.yourcompanyname.SoupChef.Intents
    • Intents UI の拡張機能: com.yourcompanyname.SoupChef.Intentsui
  • SoupChef プロジェクトの Entitlements.plist ファイルを更新します。

    • アプリ グループ機能の場合は、グループを前に作成した新しいアプリ グループに設定します (上の例の group.com.yourcompanyname.SoupChef)。
    • SiriKit が有効になっていることを確認します。
  • SoupChefIntents プロジェクトの Entitlements.plist ファイルを更新します。

    • アプリ グループ機能の場合は、グループを前に作成した新しいアプリ グループに設定します (上の例の group.com.yourcompanyname.SoupChef)。
  • 最後に、NSUserDefaultsHelper.cs を開きます。 AppGroup 変数を新しいアプリ グループの値に設定します (たとえば、group.com.yourcompanyname.SoupChef に設定します)。

ビルド設定の構成

Visual Studio for Mac または Visual Studio 2017 の場合:

  • SoupChef プロジェクトのオプションまたはプロパティを開きます。 [iOS バンドル署名] タブで、署名 ID を自動に設定し、プロビジョニング プロファイルを、前に作成した新しいアプリ固有のプロビジョニング プロファイルに設定します。

  • SoupChefIntents プロジェクトのオプションまたはプロパティを開きます。 [iOS バンドル署名] タブで、署名 ID を自動に設定し、プロビジョニング プロファイルを、前に作成した新しい Intents の拡張機能固有のプロビジョニング プロファイルに設定します。

  • SoupChefIntentsUI プロジェクトのオプションまたはプロパティを開きます。 [iOS バンドル署名] タブで、署名 ID を自動に設定し、プロビジョニング プロファイルを、前に作成した新しい Intents UI の拡張機能固有のプロビジョニング プロファイルに設定します。

これらの変更が行われると、アプリは iOS デバイス上で実行されます。

自動プロビジョニング

自動プロビジョニングを使用して、これらのプロビジョニング タスクの多くを IDE で直接実行できます。 ただし、自動プロビジョニングではアプリ グループは設定されません。 新しく作成したアプリ グループを含め、それらをダウンロードしてインストールするために、使用するアプリ グループの名前を使用して Entitlements.plist ファイルを手動で構成し、Apple 開発者ポータルにアクセスしてアプリ グループを作成し、そのアプリ グループを自動プロビジョニングによって作成された各アプリ ID に割り当て、プロビジョニング プロファイル (アプリ、Intents の拡張機能、Intents UI の拡張機能) を再生成する必要があります。