Xamarin.iOS での SiriKit の実装
この記事では、Xamarin.iOS アプリで SiriKit サポートを実装するために必要な手順を説明します。
iOS 10 の新機能である SiriKit を使用すると、Xamarin.iOS アプリは、iOS デバイス上の Siri と Maps アプリを使用してユーザーがアクセスできるサービスを提供できます。 この記事では、必要な Intents 拡張機能、Intents UI 拡張機能、ボキャブラリを追加して、Xamarin.iOS アプリで SiriKit のサポートを実装するために必要な手順について説明します。
Siri は、関連するタスクの認識アクションのグループである、ドメインの概念により機能します。 アプリが Siri とやり取りする各対話は、次のように既知のサービス ドメインのいずれかに分類される必要があります。
- 音声通話またはビデオ通話。
- 乗車予約。
- トレーニングの管理。
- メッセージング。
- 写真の検索。
- 支払いの送受信。
ユーザーが Siri に App Extension のサービスのいずれかが関与する要求を行うと、SiriKit は拡張機能に、サポート データと共にユーザーの要求を説明する Intent オブジェクトを送信します。 その後、アプリの拡張機能は、指定された Intent に対して適切な Response オブジェクトを生成し、拡張機能による要求の処理方法の詳細を伝えます。
このガイドでは、既存のアプリに SiriKit のサポートを含める簡単な例を示します。 この例では、架空の MonkeyChat アプリを使用します。
MonkeyChat は、このアプリ専用のユーザーの友人の連絡帳を保持し、各友人が画面名 (Bobo など) と関連付けられ、ユーザーが画面名で各友人にテキスト チャットを送信できるようにします。
SiriKit を使用したアプリの拡張
「SiriKit の概念の理解」ガイドに示すように、SiriKit を使用したアプリの拡張には、次の 3 つの主要な部分があります。
これには以下が含まれます。
- Intents 拡張機能 - ユーザーの応答を検証し、アプリが要求を処理できることを確認し、ユーザーの要求を満たすためのタスクを実際に実行します。
- Intents UI 拡張機能 - オプションで、Siri 環境での応答に対するカスタム UI を提供し、アプリ UI とブランド化を Siri に持ち込んで、ユーザー エクスペリエンスに豊かさをもたらします。
- アプリ - Siri の動作を支援するために、ユーザー固有のボキャブラリをアプリに提供します。
これらの要素のすべてと、それらをアプリに含める手順については、以下のセクションで詳しく説明します。
アプリの準備
SiriKit は拡張機能に基づいて構築されていますが、アプリに拡張機能を追加する前に、開発者が SiriKit の導入を支援するために行う必要があることがいくつかあります。
共通の共有コードの移動
最初に、開発者は、アプリと拡張機能の間で共有される共通のコードの一部を、共有プロジェクト、ポータブル クラス ライブラリ (PCL)、またはネイティブ ライブラリのいずれかに移動できます。
拡張機能は、アプリが実行するすべてのことを実行できる必要があります。 サンプルの MonkeyChat アプリでは、連絡先の検索、新しい連絡先の追加、メッセージの送信、メッセージ履歴の取得などです。
この共通のコードを共有プロジェクト、PCL、またはネイティブ ライブラリに移動することで、そのコードを 1 つの共通の場所で簡単に保守し、拡張機能と親アプリが統一されたエクスペリエンスと機能をユーザーに提供できるようにします。
サンプル アプリの MonkeyChat の場合、データ モデルと、ネットワークやデータベースへのアクセスなどの処理コードはネイティブ ライブラリに移動されます。
次の操作を行います。
Visual Studio for Mac を起動し、MonkeyChat アプリを開きます。
Solution Pad でソリューション名を右クリックし、[追加]>[新しいプロジェクト...] を選択します。
[iOS]>[ライブラリ]>[クラス ライブラリ] を選択し、[次へ] ボタンをクリックします。
[名前] に「
MonkeyChatCommon
」と入力し、[作成] ボタンをクリックします。ソリューション エクスプローラーのメイン アプリの References フォルダーを右クリックし、[参照の編集...] を選択します。MonkeyChatCommon プロジェクトにチェックを付け、[OK] ボタンをクリックします。
ソリューション エクスプローラーで、共通の共有コードをメイン アプリからネイティブ ライブラリにドラッグします。
MonkeyChat の場合は、DataModels フォルダーと Processors フォルダーを、メイン アプリからネイティブ ライブラリにドラッグします。
ネイティブ ライブラリに移動されたファイルを編集し、ライブラリの名前空間と一致するように名前空間を変更します。 たとえば、MonkeyChat
を MonkeyChatCommon
に変更するとします。
using System;
namespace MonkeyChatCommon
{
/// <summary>
/// A message sent from one user to another within a conversation.
/// </summary>
public class MonkeyMessage
{
public MonkeyMessage ()
{
}
...
}
}
次に、メイン アプリに戻り、移動されたクラスの 1 つをアプリが使用するすべての場所でネイティブ ライブラリの名前空間の using
ステートメントを追加します。
using System;
using System.Collections.Generic;
using UIKit;
using Foundation;
using CoreGraphics;
using MonkeyChatCommon;
namespace MonkeyChat
{
public partial class MasterViewController : UITableViewController
{
public DetailViewController DetailViewController { get; set; }
DataSource dataSource;
...
}
}
拡張機能に合わせたアプリの設計
通常、アプリは複数のインテントにサイン アップするため、開発者はアプリが適切な数の Intents 拡張機能に合わせて設計されていることを確認する必要があります。
アプリで複数のインテントが必要な場合、開発者には、すべてのインテント処理を 1 つの Intents 拡張機能に配置するか、または各インテントについて個別の Intents 拡張機能を作成するという選択肢があります。
インテントごとに個別の Intents 拡張機能を作成することを選択する場合、開発者は各拡張機能に大量の定型コードを複製し、結果として大量のプロセッサとメモリのオーバーヘッドを作り出してしまう可能性があります。
2 つの選択肢のいずれかを選択するには、対象のインテントに本質的に同種のものがあるかどうかを検討します。 たとえば、オーディオ呼び出しとビデオ呼び出しを行ったアプリでは、これらのインテントの両方を 1 つの Intents 拡張機能に含めて、類似のタスクを処理し、コードの再利用を最大化できる可能性があります。
既存のグループになじまないインテントまたはインテントのグループについては、アプリのソリューションに新しい Intents 拡張機能を作成して含めます。
必須エンタイトルメントの設定
SiriKit 統合を含む Xamarin.iOS アプリには、適切なエンタイトルメントが設定されている必要があります。 開発者がこれらの必要なエンタイトルメントを適切に設定していない場合、実際の iOS 10 (またはそれ以降) のハードウェアにアプリをインストールしたりテストしたりすることができません。これは、iOS 10 シミュレーターが SiriKit をサポートしていないこともあり、要件となっています。
次の操作を行います。
ソリューション エクスプローラーで
Entitlements.plist
ファイルをダブルクリックして、編集用に開きます。[ソース] タブに切り替えます。
com.apple.developer.siri
Propertyを追加し、TypeをBoolean
に設定し、ValueをYes
に設定します。変更をファイルに保存します。
ソリューション エクスプローラーでプロジェクト ファイルをダブルクリックして、編集用に開きます。
[iOS バンドル署名] を選択し、[カスタム エンタイトルメント] フィールドで
Entitlements.plist
ファイルが選択されていることを確認します。[OK] ボタンをクリックして、変更を保存します。
完了すると、アプリの Entitlements.plist
ファイルは次のようになります (外部エディターで開かれます)。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.siri</key>
<true/>
</dict>
</plist>
アプリの適切なプロビジョニング
Apple では SiriKit フレームワークの周辺に厳密なセキュリティを課しており、そのため、SiriKit を実装する Xamarin.iOS アプリには、適切なアプリ ID とエンタイトルメント (上記のセクションを参照) が必須であり、適切なプロビジョニング プロファイルで署名する必要があります。
使用する Mac で次のことを行います。
Web ブラウザーで https://developer.apple.com に移動し、アカウントにログインします。
[証明書]、[識別子]、[プロファイル] をクリックします。
[プロビジョニング プロファイル] を選択し、[アプリ ID] を選択し、+ ボタンをクリックします。
新しいプロファイルの [名前] を入力します。
Apple の名前付けの推奨事項に従って [バンドル ID] を入力します。
[App Services] セクションまで下にスクロールし、[SiriKit] を選択し、[続行] ボタンをクリックします。
すべての設定を確認し、アプリ ID を [送信] します。
[プロビジョニング プロファイル]>[開発] を選択し、+ ボタンをクリックして [Apple ID] を選択して、[続行] をクリックします。
[すべて選択] をクリックし、[続行] をクリックします。
[すべて選択] をもう一度クリックし、[続行] をクリックします。
Apple の名前付け推奨を使用して [プロファイル名] を入力し、[続行] をクリックします。
Xcode を起動します。
[Xcode] メニューから、[基本設定...] を選択します。
[アカウント] を選択し、[詳細の表示..] ボタンをクリックします。
左下隅にある [すべてのプロファイルのダウンロード] ボタンをクリックします。
上記で作成した [プロビジョニング プロファイル] が Xcode にインストールされていることを確認します。
プロジェクトを開き、Visual Studio for Mac で SiriKit のサポートを追加します。
ソリューション エクスプローラーで、
Info.plist
ファイルをダブルクリックします。[バンドル識別子] が、上記の Apple の開発者ポータルで作成したものと一致していることを確認します。
ソリューション エクスプローラーで [プロジェクト] をクリックします。
プロジェクトを右クリックし、[オプション] を選択します。
[iOS バンドル署名] を選択し、[スピニング ID] と上記で作成した [プロビジョニング プロファイル] を選択します。
[OK] ボタンをクリックして、変更を保存します。
重要
SiriKit のテストは、実際の iOS 10 ハードウェア デバイスでのみ動作します。iOS 10 シミュレーターでは動作しません。 SiriKit 対応 Xamarin.iOS アプリを実際のハードウェアにインストールする際に問題が発生する場合は、Apple の開発者ポータルと Visual Studio for Mac の両方で、必要なエンタイトルメント、アプリ ID、署名識別子、プロビジョニング プロファイルが適切に構成されていることを確認してください。
Siri 認可の要求
アプリがユーザー固有のボキャブラリを追加したり Intents 拡張機能が Siri に接続したりする前に、ユーザーから Siri にアクセスするための認可を要求する必要があります。
アプリの Info.plist
ファイルを編集し、[ソース] ビューに切り替えて、アプリで Siri を使用する方法と送信されるデータの種類を説明する文字列値を含む NSSiriUsageDescription
キーを追加します。 たとえば、MonkeyChat アプリでは "MonkeyChat の連絡先が Siri に送信されます" と表示される場合があります。
アプリの初回起動時に INPreferences
クラスの RequestSiriAuthorization
メソッドを呼び出します。 AppDelegate.cs
クラスを編集し、FinishedLaunching
メソッドを次のようにします:
using Intents;
...
public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions)
{
// Request access to Siri
INPreferences.RequestSiriAuthorization ((INSiriAuthorizationStatus status) => {
// Respond to returned status
switch (status) {
case INSiriAuthorizationStatus.Authorized:
break;
case INSiriAuthorizationStatus.Denied:
break;
case INSiriAuthorizationStatus.NotDetermined:
break;
case INSiriAuthorizationStatus.Restricted:
break;
}
});
return true;
}
このメソッドが初めて呼び出されると、アプリが Siri にアクセスすることを許可するようユーザーに求めるアラートが表示されます。 開発者が上記 NSSiriUsageDescription
に追加したメッセージがこのアラートに表示されます。 ユーザーが最初にアクセスを拒否した場合は、Settings アプリを使用してアプリへのアクセスを許可できます。
アプリでは、INPreferences
クラスの SiriAuthorizationStatus
メソッドを呼び出すことで、アプリが Siri にアクセスできるかどうかをいつでもチェックできます。
ローカリゼーションと Siri
iOS デバイスでは、ユーザーは、システムの既定の言語とは異なる言語を Siri について選択できます。 ローカライズされたデータを処理する場合、アプリは INPreferences
クラスの SiriLanguageCode
メソッドを使用して Siri から言語コードを取得する必要があります。 次に例を示します。
var language = INPreferences.SiriLanguageCode();
// Take action based on language
if (language == "en-US") {
// Do something...
}
ユーザー固有のボキャブラリの追加
ユーザー固有のボキャブラリは、アプリの個々のユーザーに固有の単語または語句を提供します。 これらは、実行時に (アプリ拡張機能ではなく) メイン アプリから、ユーザーにとって最も重要な使用優先順位で並べ替えられた一連の用語として、最も重要な用語が一覧の先頭に表示された状態で提供されます。
ユーザー固有のボキャブラリは、次のいずれかのカテゴリに属している必要があります。
- 連絡先名 (連絡先フレームワークによって管理されていない)。
- 写真のタグ。
- フォト アルバム名。
- ワークアウト名。
カスタム ボキャブラリとして登録する用語を選択する際には、アプリに慣れていないユーザーが誤解する可能性がある用語のみを選択します。 "マイ ワークアウト" や "マイ アルバム" などの一般的な用語を登録しないでください。 たとえば、MonkeyChat アプリでは、ユーザーのアドレス帳の各連絡先に関連付けられているニックネームが登録されます。
このアプリは、INVocabulary
クラスの SetVocabularyStrings
メソッドを呼び出し、メイン アプリから NSOrderedSet
コレクション内で渡すことによって、ユーザー固有のボキャブラリを提供します。 アプリでは、新しい用語を追加する前に既存の用語を除去するために、常に最初に RemoveAllVocabularyStrings
メソッドを呼び出す必要があります。 次に例を示します。
using System;
using System.Linq;
using System.Collections.Generic;
using Foundation;
using Intents;
namespace MonkeyChatCommon
{
public class MonkeyAddressBook : NSObject
{
#region Computed Properties
public List<MonkeyContact> Contacts { get; set; } = new List<MonkeyContact> ();
#endregion
#region Constructors
public MonkeyAddressBook ()
{
}
#endregion
#region Public Methods
public NSOrderedSet<NSString> ContactNicknames ()
{
var nicknames = new NSMutableOrderedSet<NSString> ();
// Sort contacts by the last time used
var query = Contacts.OrderBy (contact => contact.LastCalledOn);
// Assemble ordered list of nicknames by most used to least
foreach (MonkeyContact contact in query) {
nicknames.Add (new NSString (contact.ScreenName));
}
// Return names
return new NSOrderedSet<NSString> (nicknames.AsSet ());
}
// This method MUST only be called on a background thread!
public void UpdateUserSpecificVocabulary ()
{
// Clear any existing vocabulary
INVocabulary.SharedVocabulary.RemoveAllVocabularyStrings ();
// Register new vocabulary
INVocabulary.SharedVocabulary.SetVocabularyStrings (ContactNicknames (), INVocabularyStringType.ContactName);
}
#endregion
}
}
このコードが適切に配置されると、次のように呼び出されることになります。
using System;
using System.Threading;
using UIKit;
using MonkeyChatCommon;
using Intents;
namespace MonkeyChat
{
public partial class ViewController : UIViewController
{
#region AppDelegate Access
public AppDelegate ThisApp {
get { return (AppDelegate)UIApplication.SharedApplication.Delegate; }
}
#endregion
#region Constructors
protected ViewController (IntPtr handle) : base (handle)
{
// Note: this .ctor should not contain any initialization logic.
}
#endregion
#region Override Methods
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Do we have access to Siri?
if (INPreferences.SiriAuthorizationStatus == INSiriAuthorizationStatus.Authorized) {
// Yes, update Siri's vocabulary
new Thread (() => {
Thread.CurrentThread.IsBackground = true;
ThisApp.AddressBook.UpdateUserSpecificVocabulary ();
}).Start ();
}
}
public override void DidReceiveMemoryWarning ()
{
base.DidReceiveMemoryWarning ();
// Release any cached data, images, etc that aren't in use.
}
#endregion
}
}
重要
Siri はカスタム ボキャブラリをヒントとして扱い、できるだけ多くの用語を組み込みます。 ただし、カスタム ボキャブラリの領域は限られているため、混乱を招く可能性のある用語のみを登録することが重要であり、登録される用語の合計数を最小限に抑える必要があります。
詳細については、「ユーザー固有のボキャブラリの参照」とAppleの「カスタム ボキャブラリの参照の指定」を参照してください。
アプリ固有のボキャブラリの追加
アプリ固有のボキャブラリでは、車両の種類やワークアウト名など、アプリのすべてのユーザーに知られる特定の単語とフレーズを定義します。 これらはそのアプリケーションの一部であるため、メイン アプリ バンドルの一部として AppIntentVocabulary.plist
ファイルで定義されます。 さらに、これらの単語と語句はローカライズする必要があります。
アプリ固有のボキャブラリ用語は、次のいずれかのカテゴリに属している必要があります。
- 乗車オプション。
- ワークアウト名。
アプリ固有のボキャブラリ ファイルには、次の 2 つのルート レベル のキーが含まれています。
ParameterVocabularies
Required - 適用するアプリのカスタム用語と意図パラメーターを定義します。IntentPhrases
Optional -ParameterVocabularies
で定義されているカスタム用語を使用したフレーズの例が含まれます。
ParameterVocabularies
内の各エントリは、ID 文字列、用語、および用語が適用されるインテントを指定する必要があります。 さらに、1 つの用語が複数のインテントに適用される場合があります。
許容される値と必要なファイル構造の完全な一覧については、Apple の「アプリ ボキャブラリ ファイル形式リファレンス」を参照してください。
アプリ プロジェクトに AppIntentVocabulary.plist
ファイルを追加するには、次を行います。
ソリューション エクスプローラーでプロジェクト名を右クリックして、[追加]>[新しいファイル...]>[iOS] を選択します。
ソリューション エクスプローラーで
AppIntentVocabulary.plist
ファイルをダブルクリックして、編集用に開きます。キーを追加するには + をクリックし、[名前] を
ParameterVocabularies
に、[種類] をArray
に設定します。ParameterVocabularies
を展開して + ボタンをクリックし、[種類] をDictionary
に設定します。新しいキーを追加するには + をクリックし、[名前] を
ParameterNames
に、[種類] をArray
に設定します。+ をクリックして、[種類] が
String
で値が使用可能なパラメーター名の 1 である新しいキーを追加します。INStartWorkoutIntent.workoutName
の例を次に示します。ParameterVocabularies
キーに [種類] がArray
のParameterVocabulary
キーを追加します。[種類] が
Dictionary
の新しいキーを追加します。[種類] が
String
のVocabularyItemIdentifier
キーを追加し、用語の一意の ID を指定します。[種類] が
Array
のVocabularyItemSynonyms
キーを追加します。[種類] が
Dictionary
の新しいキーを追加します。[種類] が
String
でアプリが定義している用語を含むVocabularyItemPhrase
キーを追加します。[種類] が
String
のVocabularyItemPronunciation
キーを、用語の発音と共に追加します。[種類] が
Array
のVocabularyItemExamples
キーを追加します。用語の使用例と共に、いくつかの
String
キーを追加します。アプリで定義する必要があるその他のカスタム用語について、上記の手順を繰り返します。
ParameterVocabularies
キーの表示を折りたたみます。[種類] が
Array
のIntentPhrases
キーを追加します。[種類] が
Dictionary
の新しいキーを追加します。[種類] が
String
で例についてのインテントをもつIntentName
キーを追加します。[種類] が
Array
のIntentExamples
キーを追加します。用語の使用例と共に、いくつかの
String
キーを追加します。アプリが使用例を提供するために必要なインテントについて、上記の手順を繰り返します。
重要
この AppIntentVocabulary.plist
は開発中にテスト デバイスで Siri に登録され、Siri がカスタム ボキャブラリを組み込むのに時間がかかる場合があります。 その結果、テスト担当者は、アプリ固有のボキャブラリが更新されたときに、そのテストの前に数分待つ必要があります。
詳細については、「アプリ固有のボキャブラリの参照」とAppleの「カスタム ボキャブラリの参照の指定」を参照してください。
Intents 拡張機能の追加
SiriKit を導入する準備ができました。開発者は、Siri 統合に必要なインテントを処理するために、1 つ (または複数) の Intents 拡張機能をソリューションに追加する必要があります。
必要な Intents 拡張機能ごとに、次の操作を行います。
- Intents 拡張機能プロジェクトを Xamarin.iOS アプリ ソリューションに追加します。
- Intents 拡張機能
Info.plist
ファイルを構成します。 - Intents 拡張機能の main クラスを変更します。
詳細については、「Intents 拡張機能のリファレンス」と Apple の「Intents 拡張機能の作成に関するリファレンス」を参照してください。
拡張機能の作成
ソリューションに Intents 拡張機能を追加するには、次のようにします。
Solution Pad で [ソリューション名] を右クリックし、[追加]>[新しいプロジェクトの追加...] を選択します。
ダイアログ ボックスで [iOS]>[拡張機能]>[Intents 拡張機能] を選択し、[次へ] ボタンをクリックします。
次に、Intents 拡張機能の [名前] を入力し、[次へ] ボタンをクリックします。
最後に、[作成] ボタンをクリックして、Intents ト拡張機能をアプリ ソリューションに追加します。
ソリューション エクスプローラーで、新しく作成された Intents 拡張機能の References フォルダーを右クリックします。 共通の共有コード ライブラリ プロジェクト (上記でアプリにより作成されたもの) の名前を確認し、[OK] ボタンをクリックします。
アプリに必要な Intents 拡張機能の数 (上記の 「拡張機能のためのアプリの設計」セクションに基づく) だけ、これらの手順を繰り返します。
Info.plist の構成
アプリのソリューションに追加されたそれぞれの Intents 拡張機能について、アプリと協調して動作するよう、Info.plist
ファイルで構成する必要があります。
他の一般的なアプリ拡張機能と同様に、アプリには既存の NSExtension
キーと NSExtensionAttributes
キーがあります。 Intents 拡張機能には、構成が必要な次の 2 つの新しい属性があります。
- IntentsSupported - 必須であり、アプリが Intents 拡張機能によるサポートを必要とする Intent クラス名の配列で構成されます。
- IntentsRestrictedWhileLocked - 拡張機能のロック画面の動作を指定するアプリの省略可能なキーです。 これは、アプリが Intents 拡張機能から使用するためにユーザーのログインを要求する Intent クラス名の配列で構成されます。
Intents 拡張機能の Info.plist
ファイルを構成するには、ソリューション エクスプローラーでダブルクリックして編集用に開きます。 次に、ソース ビューに切り替え、エディターで NSExtension
キーと NSExtensionAttributes
キーを展開します。
IntentsSupported
キーを展開し、この拡張機能でサポートされる Intent クラスの名前を追加します。 例の MonkeyChat アプリでは、INSendMessageIntent
がサポートされています。
アプリで、必要に応じて、特定のインテントを使用するためにはユーザーがデバイスにログオンしている必要がある場合は、IntentRestrictedWhileLocked
キーを展開し、アクセスが制限されているインテントのクラス名を追加します。 例の MonkeyChat アプリでは、チャット メッセージを送信するにはユーザーがログインしている必要があるため、次のように INSendMessageIntent
を追加しました。
使用可能な Intent ドメインの完全な一覧については、Apple の「Intent ドメイン リファレンス」を参照してください。
Main クラスの構成
次に、開発者は、Intents 拡張機能の Siri への主要なエントリ ポイントとして機能する main クラスを構成する必要があります。 これは、IINIntentHandler
デリゲートに準拠する INExtension
のサブクラスである必要があります。 次に例を示します。
using System;
using System.Collections.Generic;
using Foundation;
using Intents;
namespace MonkeyChatIntents
{
[Register ("IntentHandler")]
public class IntentHandler : INExtension, IINSendMessageIntentHandling, IINSearchForMessagesIntentHandling, IINSetMessageAttributeIntentHandling
{
#region Constructors
protected IntentHandler (IntPtr handle) : base (handle)
{
// Note: this .ctor should not contain any initialization logic.
}
#endregion
#region Override Methods
public override NSObject GetHandler (INIntent intent)
{
// This is the default implementation. If you want different objects to handle different intents,
// you can override this and return the handler you want for that particular intent.
return this;
}
#endregion
...
}
}
アプリが Intents 拡張機能の main クラスに実装しなければならないメソッドが 1 つだけあります。GetHandler
メソッドです。 このメソッドは SiriKit によってインテントを渡され、アプリは、指定されたインテントの種類に一致するインテント ハンドラーを返す必要があります。
例の MonkeyChat アプリでは 1 つのインテントのみを処理するため、GetHandler
メソッド内で自身を返しています。 拡張機能が複数のインテントを処理するとした場合、開発者はインテントの種類ごとにクラスを追加し、メソッドに渡された Intent
に基づいてここでインスタンスを返します。
解決ステージの処理
解決ステージは、Intents 拡張機能が Siri から渡され、ユーザーの会話を介して設定されたパラメーターを明確にし、検証する場所です。
Siri から送信されるパラメーターそれぞれについて、Resolve
メソッドがあります。 アプリでは、ユーザーから適切な回答を得るために Siri の助けが必要になる可能性があるすべてのパラメーターについて、このメソッドを実装する必要があります。
例の MonkeyChat アプリの場合、Intents 拡張機能では、メッセージの送信先として 1 人以上の受信者が必要になります。 リスト内の受信者ごとに、拡張機能は連絡先検索を実行する必要があります。その結果は、次のようになる可能性があります。
- 一致する連絡先が 1 つだけ見つかる。
- 一致する連絡先が 2 つ以上見つかる。
- 一致する連絡先が見つからない。
さらに、MonkeyChat には、メッセージ本文の内容が必要です。 ユーザーがこれを指定していない場合、Siri はユーザーに内容の入力を求める必要があります。
Intents 拡張機能では、これらの各ケースを適切に処理する必要があります。
[Export ("resolveRecipientsForSearchForMessages:withCompletion:")]
public void ResolveRecipients (INSendMessageIntent intent, Action<INPersonResolutionResult []> completion)
{
var recipients = intent.Recipients;
// If no recipients were provided we'll need to prompt for a value.
if (recipients.Length == 0) {
completion (new INPersonResolutionResult [] { (INPersonResolutionResult)INPersonResolutionResult.NeedsValue });
return;
}
var resolutionResults = new List<INPersonResolutionResult> ();
foreach (var recipient in recipients) {
var matchingContacts = new INPerson [] { recipient }; // Implement your contact matching logic here to create an array of matching contacts
if (matchingContacts.Length > 1) {
// We need Siri's help to ask user to pick one from the matches.
resolutionResults.Add (INPersonResolutionResult.GetDisambiguation (matchingContacts));
} else if (matchingContacts.Length == 1) {
// We have exactly one matching contact
resolutionResults.Add (INPersonResolutionResult.GetSuccess (recipient));
} else {
// We have no contacts matching the description provided
resolutionResults.Add ((INPersonResolutionResult)INPersonResolutionResult.Unsupported);
}
}
completion (resolutionResults.ToArray ());
}
[Export ("resolveContentForSendMessage:withCompletion:")]
public void ResolveContent (INSendMessageIntent intent, Action<INStringResolutionResult> completion)
{
var text = intent.Content;
if (!string.IsNullOrEmpty (text))
completion (INStringResolutionResult.GetSuccess (text));
else
completion ((INStringResolutionResult)INStringResolutionResult.NeedsValue);
}
詳細については、「解決ステージ リファレンス」と Apple の「インテントの解決と処理のリファレンス」を参照してください。
確認ステージの処理
確認ステージは、Intents 拡張機能がユーザーの要求を満たすためにすべての情報が揃っていることを確認する場所です。 アプリは、Siri に何が起こっているのかをサポートするすべての詳細と共に確認を送信して、これが意図したアクションであることをユーザーに確認する必要があります。
[Export ("confirmSendMessage:completion:")]
public void ConfirmSendMessage (INSendMessageIntent intent, Action<INSendMessageIntentResponse> completion)
{
// Verify user is authenticated and the app is ready to send a message.
...
var userActivity = new NSUserActivity (nameof (INSendMessageIntent));
var response = new INSendMessageIntentResponse (INSendMessageIntentResponseCode.Ready, userActivity);
completion (response);
}
詳細については、「確認ステージのリファレンス」を参照してください。
インテントの処理
これは、Intents 拡張機能が実際にユーザーの要求を満たすためのタスクを実行し、結果を Siri に渡して、ユーザーに知らせることができるようにする箇所です。
public void HandleSendMessage (INSendMessageIntent intent, Action<INSendMessageIntentResponse> completion)
{
// Implement the application logic to send a message here.
...
var userActivity = new NSUserActivity (nameof (INSendMessageIntent));
var response = new INSendMessageIntentResponse (INSendMessageIntentResponseCode.Success, userActivity);
completion (response);
}
public void HandleSearchForMessages (INSearchForMessagesIntent intent, Action<INSearchForMessagesIntentResponse> completion)
{
// Implement the application logic to find a message that matches the information in the intent.
...
var userActivity = new NSUserActivity (nameof (INSearchForMessagesIntent));
var response = new INSearchForMessagesIntentResponse (INSearchForMessagesIntentResponseCode.Success, userActivity);
// Initialize with found message's attributes
var sender = new INPerson (new INPersonHandle ("sarah@example.com", INPersonHandleType.EmailAddress), null, "Sarah", null, null, null);
var recipient = new INPerson (new INPersonHandle ("+1-415-555-5555", INPersonHandleType.PhoneNumber), null, "John", null, null, null);
var message = new INMessage ("identifier", "I am so excited about SiriKit!", NSDate.Now, sender, new INPerson [] { recipient });
response.Messages = new INMessage [] { message };
completion (response);
}
public void HandleSetMessageAttribute (INSetMessageAttributeIntent intent, Action<INSetMessageAttributeIntentResponse> completion)
{
// Implement the application logic to set the message attribute here.
...
var userActivity = new NSUserActivity (nameof (INSetMessageAttributeIntent));
var response = new INSetMessageAttributeIntentResponse (INSetMessageAttributeIntentResponseCode.Success, userActivity);
completion (response);
}
詳細については、「処理ステージのリファレンス」を参照してください。
Intents UI 拡張機能の追加
オプションの Intents UI 拡張機能は、アプリの UI とブランドを Siri エクスペリエンスに持ち込み、ユーザーがアプリと繋がっていると感じさせる機会を提供します。 この拡張機能を使用すると、アプリはブランドだけでなく、ビジュアルやその他の情報をトランスクリプトに取り込むことができます。
Intents 拡張機能と同様に、開発者は Intents UI 拡張機能について次の手順を実行します。
- Intents UI 拡張機能プロジェクトを Xamarin.iOS アプリ ソリューションに追加します。
- Intents UI 拡張機能
Info.plist
ファイルを構成します。 - Intents UI 拡張機能の main クラスを変更します。
詳細については、「Intents UI 拡張機能のリファレンス」と Apple の「カスタム インターフェイスの提供のリファレンス」を参照してください。
拡張機能の作成
ソリューションに Intents UI 拡張機能を追加するには、次のようにします。
Solution Pad で [ソリューション名] を右クリックし、[追加]>[新しいプロジェクトの追加...] を選択します。
ダイアログ ボックスで [iOS]>[拡張機能]>[Intents UI 拡張機能] を選択し、[次へ] ボタンをクリックします。
次に、Intents 拡張機能の [名前] を入力し、[次へ] ボタンをクリックします。
最後に、[作成] ボタンをクリックして、Intents ト拡張機能をアプリ ソリューションに追加します。
ソリューション エクスプローラーで、新しく作成された Intents 拡張機能の References フォルダーを右クリックします。 共通の共有コード ライブラリ プロジェクト (上記でアプリにより作成されたもの) の名前を確認し、[OK] ボタンをクリックします。
Info.plist の構成
Intents UI 拡張機能の Info.plist
ファイルを構成して、アプリで動作するようにします。
他の一般的なアプリ拡張機能と同様に、アプリには既存の NSExtension
キーと NSExtensionAttributes
キーがあります。 Intents 拡張機能には、構成が必要な次の 1 つの新しい属性があります。
IntentsSupported - 必須であり、アプリが Intents 拡張機能によるサポートを必要とする Intent クラス名の配列で構成されます。
Intents UI 拡張機能の Info.plist
ファイルを構成するには、ソリューション エクスプローラーでダブルクリックして編集用に開きます。 次に、ソース ビューに切り替え、エディターで NSExtension
キーと NSExtensionAttributes
キーを展開します。
IntentsSupported
キーを展開し、この拡張機能でサポートされる Intent クラスの名前を追加します。 例の MonkeyChat アプリでは、INSendMessageIntent
がサポートされています。
使用可能な Intent ドメインの完全な一覧については、Apple の「Intent ドメイン リファレンス」を参照してください。
Main クラスの構成
Intents UI 拡張機能の Siri への主要なエントリ ポイントとして機能する main クラスを構成します。 これは、IINUIHostedViewController
インターフェイスに準拠する UIViewController
のサブクラスである必要があります。 次に例を示します。
using System;
using Foundation;
using CoreGraphics;
using Intents;
using IntentsUI;
using UIKit;
namespace MonkeyChatIntentsUI
{
public partial class IntentViewController : UIViewController, IINUIHostedViewControlling
{
#region Constructors
protected IntentViewController (IntPtr handle) : base (handle)
{
// Note: this .ctor should not contain any initialization logic.
}
#endregion
#region Override Methods
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
// Do any required interface initialization here.
}
public override void DidReceiveMemoryWarning ()
{
// Releases the view if it doesn't have a superview.
base.DidReceiveMemoryWarning ();
// Release any cached data, images, etc that aren't in use.
}
#endregion
#region Public Methods
[Export ("configureWithInteraction:context:completion:")]
public void Configure (INInteraction interaction, INUIHostedViewContext context, Action<CGSize> completion)
{
// Do configuration here, including preparing views and calculating a desired size for presentation.
if (completion != null)
completion (DesiredSize ());
}
[Export ("desiredSize:")]
public CGSize DesiredSize ()
{
return ExtensionContext.GetHostedViewMaximumAllowedSize ();
}
#endregion
}
}
Siri は、INInteraction
クラス インスタンスを Intents UI 拡張機能内の UIViewController
インスタンスの Configure
メソッドに渡します。
この INInteraction
オブジェクトは、拡張機能に次の 3 つの重要な情報を提供します。
- 処理中の Intent オブジェクト。
- Intents 拡張機能の
Confirm
メソッドとHandle
メソッドからの Intent Response オブジェクト。 - アプリと Siri の間の対話の状態を定義する Interaction State。
この UIViewController
インスタンスは、Siri との対話の原則クラスであり、UIViewController
から継承されるため、UIKit のすべての機能にアクセスできます。
Siri が UIViewController
の Configure
メソッドを呼び出すと、ビュー コントローラーが Siri Snippit と Maps Card のどちらでホストされるかを示すビュー コンテキストで渡されます。
Siri は、アプリが構成を完了したときに、アプリがビューの目的のサイズを返すために必要な完了ハンドラーも渡します。
iOS Designer で UI を設計する
iOS Designer で Intents UI 拡張機能のユーザー インターフェイスをレイアウトします。 ソリューション エクスプローラーで拡張機能の MainInterface.storyboard
ファイルをダブルクリックして、編集用に開きます。 必要なすべての UI 要素をドラッグしてユーザー インターフェイスを構築し、変更を保存します。
重要
UIButtons
や UITextFields
などの対話型要素は、Intents UI 拡張機能の UIViewController
に追加できますが、非対話型での Intents UI は厳密に禁止されており、ユーザーはそれらを操作できません。
ユーザー インターフェイスを接続する
iOS Designer で作成された Intents UI 拡張機能のユーザー インターフェイスを使用して、UIViewController
サブクラスを編集し、次のように Configure
メソッドをオーバーライドします。
[Export ("configureWithInteraction:context:completion:")]
public void Configure (INInteraction interaction, INUIHostedViewContext context, Action<CGSize> completion)
{
// Do configuration here, including preparing views and calculating a desired size for presentation.
...
// Return desired size
if (completion != null)
completion (DesiredSize ());
}
[Export ("desiredSize:")]
public CGSize DesiredSize ()
{
return ExtensionContext.GetHostedViewMaximumAllowedSize ();
}
既定の Siri UI のオーバーライド
Intents UI 拡張機能は、UI の上部にあるアプリ アイコンや名前などの他の Siri コンテンツと共に常に表示されるか、インテントに基づいてボタン ([送信] や [キャンセル] など) が下部に表示されます。
Siri が既定でユーザーに表示しているメッセージングやマップなどの情報を、アプリが置き換えられる場合がいくつかあります。この場合、アプリは、既定のエクスペリエンスをそのアプリに合わせて調整されたエクスペリエンスに置き換えられます。
Intents UI 拡張機能で既定の Siri UI の要素をオーバーライドする必要がある場合、UIViewController
サブクラスは IINUIHostedViewSiriProviding
インターフェイスを実装し、特定のインターフェイス要素を表示することをオプトインする必要があります。
次のコードを UIViewController
サブクラスに追加して、Intents UI 拡張機能にメッセージの内容が既に表示されていることを Siri に伝えます。
public bool DisplaysMessage {
get {return true;}
}
考慮事項
Apple では、Intents UI 拡張機能を設計して実装する際に、開発者が次の事項を考慮するよう提案しています。
- メモリ使用量を意識する - Intents UI 拡張機能は一時的なもので、短時間しか表示されないため、システムでは、完全なアプリで使用されるよりも厳密なメモリ制約が適用されます。
- 最小ビュー サイズと最大ビュー サイズを考慮する - Intents UI 拡張機能がすべての iOS デバイスの種類、サイズ、向きに適していることを確認します。 さらに、アプリが Siri に送り返す目的のサイズを許可できない場合があります。
- 柔軟なアダプティブ レイアウト パターンを使用する - 繰り返しになりますが、すべてのデバイスで UI が適切に表示されるようにします。
まとめ
この記事では、SiriKit について説明し、それを Xamarin.iOS アプリに追加して、iOS デバイス上で Siri と Maps アプリを使用してユーザーがアクセスできるサービスを提供する方法を示しました。