Accesos directos de Siri en Xamarin.iOS
En iOS 10, Apple introdujo SiriKit, lo que permite crear mensajería, llamadas VoIP, pagos, entrenamientos, reserva de viajes y aplicaciones de búsqueda de fotos que interactúan con Siri.
En iOS 11, SiriKit obtuvo compatibilidad con más tipos de aplicaciones y mayor flexibilidad para la personalización de la interfaz de usuario.
iOS 12 agrega accesos directos de Siri, lo que permite que todos los tipos de aplicaciones expongan su funcionalidad a Siri. Siri aprende cuándo ciertas tareas basadas en aplicaciones son más relevantes para el usuario y usa este conocimiento para sugerir posibles acciones a través de accesos directos. Al pulsar en un acceso directo o invocarlo con un comando de voz, se abrirá una aplicación o se ejecutará una tarea en segundo plano.
Los accesos directos deben usarse para acelerar la capacidad de un usuario para realizar una tarea común, en muchos casos sin necesidad de abrir la aplicación en cuestión.
Aplicación de ejemplo: Soup Chef
Para comprender mejor los accesos directos de Siri, eche un vistazo a la aplicación de ejemplo Soup Chef. Soup Chef permite a los usuarios realizar pedidos desde un restaurante de sopa imaginario, ver su historial de pedidos y definir frases que se usarán al ordenar la sopa interactuando con Siri.
Sugerencia
Antes de probar Soup Chef en un simulador o dispositivo de iOS 12, habilite las dos opciones siguientes, que son útiles al depurar accesos directos:
- En la aplicación Configuración, habilite Desarrollador >Mostrar accesos directos recientes.
- En la aplicación Configuración, habilite Desarrollador>Mostrar donaciones en la pantalla de bloqueo.
Esta configuración de depuración facilita la búsqueda de accesos directos creados recientemente (en lugar de predichos) en la pantalla de bloqueo de iOS y en la pantalla de búsqueda.
Para usar la aplicación de ejemplo:
- Instale y ejecute la aplicación de ejemplo Soup Chef en un simulador o dispositivo de iOS 12.
- Haga clic en el botón + de la esquina superior derecha para crear un nuevo pedido.
- Seleccione un tipo de sopa, especifique una cantidad y opciones y pulse Realizar pedido.
- En la pantalla Historial de pedidos, pulse el pedido recién creado para ver sus detalles.
- En la parte inferior de la pantalla de detalles del pedido, pulse Agregar a Siri.
- Grabe una frase de voz para asociar con el pedido y pulse Listo.
- Minimice Soup Chef, invoque a Siri y vuelva a hacer el pedido mediante la frase de voz que grabó.
- Cuando Siri complete el pedido, vuelva a abrir Soup Chef y observe que el nuevo pedido aparece en la pantalla Historial de pedidos.
La aplicación de ejemplo muestra cómo:
- Usar un acceso directo NSUserActivity para abrir una aplicación.
- Usar un acceso directo de intención personalizada para realizar una tarea.
- Proporcionar una interfaz de usuario para una intención personalizada.
- Crear un acceso directo de voz.
Info.plist y Entitlements.plist
Antes de profundizar más en el código de Soup Chef, eche un vistazo a sus archivos Info.plist y Entitlements.plist.
Info.plist
El archivo Info.plist del proyecto SoupChef define el identificador de agrupación como com.xamarin.SoupChef
. Este identificador de agrupación se usará como prefijo para los identificadores de agrupación de las extensiones de Intents y la interfaz de usuario de Intents que se describen más adelante en este documento.
El archivo Info.plist también contiene la siguiente entrada:
<key>NSUserActivityTypes</key>
<array>
<string>OrderSoupIntent</string>
<string>com.xamarin.SoupChef.viewMenu</string>
</array>
Este NSUserActivityTypes
par clave-valor indica que Soup Chef sabe cómo controlar un valor OrderSoupIntent
y un valor NSUserActivity
teniendo un ActivityType
de "com.xamarin.SoupChef.viewMenu".
Las actividades y las intenciones personalizadas que se pasan a la propia aplicación, en lugar de sus extensiones, se controlan en AppDelegate
(un UIApplicationDelegate
por el método ContinueUserActivity
.
Entitlements.plist
El archivo Entitlements.plist del proyecto SoupChef contiene las siguientes entradas:
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.xamarin.SoupChef</string>
</array>
<key>com.apple.developer.siri</key>
<true/>
Esta configuración indica que la aplicación usa el grupo de aplicaciones "group.com.xamarin.SoupChef". La extensión de aplicación SoupChefIntents usa este mismo grupo de aplicaciones, lo que permite que los dos proyectos compartan datos de NSUserDefaults
.
La clave com.apple.developer.siri
indica que la aplicación interactúa con Siri.
Nota:
La configuración de compilación del proyecto SoupChef establece Derechos personalizados para Entitlements.plist.
Uso de un acceso directo NSUserActivity para abrir una aplicación
Para crear un acceso directo que abra una aplicación para mostrar contenido específico, cree un NSUserActivity
y adjunte al controlador de vista de la pantalla que desea que abra el acceso directo.
Configuración de NSUserActivity
En la pantalla de menú, SoupMenuViewController
crea un NSUserActivity
y lo asigna a la propiedad UserActivity
del controlador de vista:
public override void ViewDidLoad()
{
base.ViewDidLoad();
UserActivity = NSUserActivityHelper.ViewMenuActivity;
}
Al establecer la propiedad UserActivity
se dona la actividad a Siri. A partir de esta donación, Siri obtiene información sobre cuándo y dónde esta actividad es relevante para el usuario y aprende a sugerirla mejor en el futuro.
NSUserActivityHelper
es una clase de utilidad incluida en la solución de SoupChef, en la biblioteca de clases SoupKit. Crea una NSUserActivity
y establece varias propiedades relacionadas con Siri y búsqueda:
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;
}
}
Tenga en cuenta las siguientes características en particular:
- Establecer
EligibleForPrediction
entrue
indica que Siri puede predecir esta actividad y exponerla como acceso directo. - La matriz
ContentAttributeSet
es unCSSearchableItemAttributeSet
estándar que se usa para incluir unNSUserActivity
en los resultados de la búsqueda de iOS. SuggestedInvocationPhrase
es una frase que Siri sugerirá al usuario como una opción potencial al asignar una frase a un acceso directo.
Control de un acceso directo de NSUserActivity
Para controlar un acceso directo NSUserActivity
invocado por un usuario, una aplicación de iOS debe invalidar el método ContinueUserActivity
de la clase AppDelegate
, respondiendo en función del campo ActivityType
del objeto NSUserActivity
pasado:
public override bool ContinueUserActivity(UIApplication application, NSUserActivity userActivity, UIApplicationRestorationHandler completionHandler)
{
// ...
else if (userActivity.ActivityType == NSUserActivityHelper.ViewMenuActivityType)
{
HandleUserActivity();
return true;
}
// ...
}
Este método llama a HandleUserActivity
, que encuentra la transición a la pantalla de menú y la invoca:
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);
}
Asignación de una frase a un NSUserActivity
Para asignar una frase a un NSUserActivity
, abra la aplicación Configuración de iOS y elija Siri & Búsqueda > Mis accesos directos. A continuación, seleccione el acceso directo (en este caso, "Pedir almuerzo") y registre una frase.
Invocar a Siri y usar esta frase abrirá Soup Chef en la pantalla del menú.
Uso de un acceso directo de intención personalizada para realizar una tarea
Definición de una intención personalizada
Para proporcionar un acceso directo que permita al usuario completar rápidamente una tarea específica relacionada con la aplicación, cree una intención personalizada. Una intención personalizada representa una tarea que un usuario puede querer completar, parámetros relevantes para esa tarea y posibles respuestas resultantes de la ejecución de la tarea. Dependiendo de cómo se define una intención personalizada, invocarla puede abrir la aplicación o ejecutar una tarea en segundo plano.
Use Xcode 10 para crear intenciones personalizadas. En el repositorio de SoupChef, la intención personalizada se define en OrderSoupIntentCodeGen, un proyecto de Objective-C. Abra este proyecto y seleccione el archivo Intents.intentdefinition en Navegador de proyecto para ver la intención de OrderSoup.
Tenga en cuenta las siguientes características:
- La intención tiene una Categoría de Pedido. Hay varias categorías predefinidas que se pueden usar para intenciones personalizadas; seleccione la que más coincida con la tarea que su intención personalizada va a habilitar. Dado que esta solución es una aplicación de pedidos de sopa, OrderSoupIntent usa Order.
- La casilla Confirmación indica si Siri debe solicitar confirmación antes de ejecutar la tarea. Para la intención de Pedir sopa en Soup Chef, esta opción está habilitada, ya que el usuario realiza una compra.
- La sección Parámetros del archivo .intentdefinition define los parámetros pertinentes para un acceso directo. Para realizar un pedido de sopa, Soup Chef debe conocer el tipo de sopa, su cantidad y cualquier opción asociada. Cada parámetro tiene un tipo; el parámetro que no se puede representar mediante un tipo predefinido se establece como Personalizado.
- La interfaz Tipos de acceso directo describe las distintas combinaciones de parámetros que Siri puede usar al sugerir el acceso directo. Las secciones asociadas Título y Subtítulo permiten definir los mensajes que Siri usará al presentar un acceso directo sugerido al usuario.
- La casilla Admitir la ejecución en segundo plano debe seleccionarse para cualquier acceso directo que se pueda ejecutar sin abrir la aplicación para obtener más interacción del usuario.
Definición de respuestas de intención personalizadas
El elemento Respuesta anidado debajo de la intención OrderSoup representa las posibles respuestas resultantes de un pedido de sopa.
En la definición de respuesta de la intención OrderSoup, tenga en cuenta las siguientes características:
- Las Propiedadesde una respuesta se pueden usar para personalizar el mensaje que se comunica al usuario. La respuesta de intención de OrderSoup tiene las propiedades soup y waitTime.
- Las Plantillas de respuesta especifican los distintos mensajes de éxito y error que se pueden usar para indicar el estado después de que se haya completado la tarea de una intención.
- La casilla Correcto debe estar seleccionada para las respuestas que indican que se han realizado correctamente.
- La respuesta de éxito OrderSoupIntent utiliza las propiedades soup y waitTime para proporcionar un mensaje descriptivo y útil que describe cuándo estará listo el pedido de sopa.
Generación de código para la intención personalizada
La compilación del proyecto Xcode que contiene esta definición de intención personalizada hace que Xcode genere código que se pueda usar para interactuar mediante programación con la intención personalizada y sus respuestas.
Para ver este código generado:
- Abra AppDelegate.m.
- Agregue una importación al archivo de encabezado de la intención personalizada:
#import "OrderSoupIntent.h"
- En cualquier método de la clase, agregue una referencia a
OrderSoupIntent
. - Haga clic con el botón derecho en
OrderSoupIntent
y seleccione Ir a la definición. - Haga clic con el botón derecho en el archivo recién abierto, OrderSoupIntent.h y seleccione Mostrar en Finder.
- Esta acción abrirá una ventana de Finder que contiene un archivo .h y .m con el código generado.
Este código generado incluye:
OrderSoupIntent
: clase que representa la intención personalizada.OrderSoupIntentHandling
: protocolo que define los métodos que se usarán para confirmar que se debe ejecutar la intención y el método que realmente lo ejecuta.OrderSoupIntentResponseCode
: enumeración que define varios estados de respuesta.OrderSoupIntentResponse
: una clase que representa la respuesta a la ejecución de una intención.
Creación de un enlace a la intención personalizada
Para usar el código generado por Xcode en una aplicación de Xamarin.iOS, cree un enlace de C# para él.
Creación de una biblioteca estática y definiciones de enlace de C#
En el repositorio de SoupChef, eche un vistazo a la carpeta OrderSoupIntentStaticLib y abra el proyecto OrderSoupIntentStaticLib.xcodeproj de Xcode.
Este proyecto de biblioteca estática de Cocoa Touch contiene los archivos OrderSoupIntent.h y OrderSoupIntent.m generados por Xcode.
Configuración de las opciones de compilación del proyecto de biblioteca estática
En el Navegador de proyectode Xcode, seleccione el proyecto de nivel superior, OrderSoupIntentStaticLiby vaya a Fases de compilación > Compilar orígenes. Observe que OrderSoupIntent.m (que importa OrderSoupIntent.h) aparece aquí. En Vincular binario con bibliotecas, observe que se incluyen Intents.framework y Foundation.framework. Con esta configuración en su lugar, el marco se compilará correctamente.
Compilación de la biblioteca estática y generación de definiciones de enlaces de C#
Para compilar la biblioteca estática y generar definiciones de enlaces de C#, siga estos pasos:
Instale Objective Sharpie, la herramienta utilizada para generar definiciones de enlaces a partir de los archivos .h y .m creados por Xcode.
Configure el sistema para usar las Herramientas de línea de comandos de Xcode 10:
Advertencia
Actualizar las Herramientas de línea de comandos seleccionadas afecta a todas las versiones instaladas de Xcode en el sistema. Cuando haya terminado de usar la aplicación de ejemplo Soup Chef, asegúrese de revertir esta configuración a su configuración original.
- En Xcode, elija Xcode> Preferencias >Ubicaciones y establezca Herramientas de línea de comandos en la instalación de Xcode 10 más actual disponible en el sistema.
En el terminal,
cd
en el directorio OrderSoupIntentStaticLib.Escriba
make
, que compila:- La biblioteca estática, libOrderSoupIntentStaticLib.a
- En el directorio de salida bo, las definiciones de enlaces de C#:
- ApiDefinitions.cs
- StructsAndEnums.cs
El proyecto OrderSoupIntentBindings, que se basa en esta biblioteca estática y sus definiciones de enlaces asociados, compila estos elementos automáticamente. Sin embargo, la ejecución manual del proceso anterior garantizará que se compile según lo previsto.
Para obtener más información sobre cómo crear una biblioteca estática y usar Objective Sharpie para crear definiciones de enlaces de C#, eche un vistazo al tutorial Enlace de una biblioteca de Objective-C de iOS.
Creación de una biblioteca de enlaces
Con la biblioteca estática y las definiciones de enlaces de C# creadas, la parte restante necesaria para consumir el código relacionado con la intención generado por Xcode en un proyecto de Xamarin.iOS es una biblioteca de enlaces.
En el repositorio de Soup Chef, abra el archivo SoupChef.sln. Entre otras cosas, esta solución contiene OrderSoupIntentBinding, una biblioteca de enlaces para la biblioteca estática generada anteriormente.
Tenga en cuenta, en particular, que este proyecto incluye:
ApiDefinitions.cs: un archivo generado anteriormente por Objective Sharpie y agregado a este proyecto. La Acción de compilación de este archivo se establece en ObjcBindingApiDefinition.
StructsAndEnums.cs: otro archivo generado anteriormente por Objective Sharpie y agregado a este proyecto. La Acción de compilación de este archivo se establece en ObjcBindingCoreSource.
Una Referencia nativa a libOrderSoupIntentStaticLib.a, la biblioteca estática compilada anteriormente. Actualice las propiedades de referencia nativas y especifique los valores siguientes:
- Frameworks =
Foundation Intents
- Smart Link =
On
- Force Load =
On
- Kind =
Static
- Frameworks =
Nota:
Tanto ApiDefinitions.cs como StructsAndEnums.cs contienen atributos como [Watch (5,0), iOS (12,0)]
. Estos atributos, generados por Objective Sharpie, se han comentado porque no son necesarios para este proyecto.
Para obtener más información sobre cómo crear una biblioteca de enlaces de C#, eche un vistazo al tutorial Enlace de una biblioteca de Objective-C de iOS.
Tenga en cuenta que el proyecto de SoupChef contiene una referencia a OrderSoupIntentBinding, lo que significa que ahora puede acceder, en C#, las clases, las interfaces y enumeraciones que contiene:
OrderSoupIntent
OrderSoupIntentHandling
OrderSoupIntentResponse
OrderSoupIntenseResponseCode
Creación de un marco de Swift
Xcode genera de forma predeterminada el código nativo de definición de intención mediante el lenguaje del proyecto nativo. Si define el archivo Intents.intentdefinition en un proyecto de Swift, Xcode le generará un único archivo Swift con todas las clases necesarias, que puede usar para crear un marco Swift.
Sugerencia
Puede seleccionar un idioma deseado para el código de intención generado en la configuración de compilación de Xcode. Vaya a Destino de intención > Configuración de compilación > Compilador de definiciones de intención: generación de código y seleccione Swift o Objective-C. También puede mantenerlo automático para que coincida con el idioma de destino.
El proceso de creación de un marco de Swift es similar al descrito anteriormente:
- Cree un nuevo proyecto de marco de Swift.
- Copie el archivo Swift generado automáticamente con código de intención en este proyecto. Puede encontrar este archivo como se describe aquí.
- Habilite el Objective-Cencabezado de puente, para que el marco se genere automáticamente con el archivo de encabezado de sharpie Objective-C requerido.
Una vez compilado el marco, siga los mismos pasos descritos anteriormente para crear un enlace de Xamarin. Puede obtener más información sobre cómo crear un enlace para un marco de Swift aquí.
Adición del archivo de definición de intención a la solución
En la solución SoupChef de C#, el proyecto SoupKit contiene código compartido entre la aplicación y sus extensiones. El archivo Intents.intentdefinition se ha colocado en el directorio Base.lproj de SoupKit y tiene una Acción de compilación de Contenido. El proceso de compilación copia este archivo en el paquete de aplicaciones de Soup Chef, donde es necesario que la aplicación funcione correctamente.
Donación de una intención
Para que Siri sugiera un acceso directo, primero debe comprender cuándo es relevante el acceso directo.
Para dar a Siri esta comprensión, Soup Chef dona una intención a Siri cada vez que el usuario realiza un pedido de sopa. En función de esta donación – cuándo se donó, dónde se donó, los parámetros que contiene – Siri aprende cuándo sugerir el acceso directo en el futuro.
SoupChef usa la clase SoupOrderDataManager
para colocar donaciones.
Cuando se llama para realizar un pedido de sopa para un usuario, el método PlaceOrder
a su vez llama a DonateInteraction
:
void DonateInteraction(Order order)
{
var interaction = new INInteraction(order.Intent, null);
interaction.Identifier = order.Identifier.ToString();
interaction.DonateInteraction((error) =>
{
// ...
});
}
Después de capturar una intención, se encapsula en una INInteraction
.
La INInteraction
se da a un Identifier
que coincida con el identificador único del pedido (será útil más adelante al eliminar donaciones de intenciones que ya no son válidas). A continuación, la interacción se dona a Siri.
La llamada al captador order.Intent
captura un OrderSoupIntent
que representa el orden estableciendo su Quantity
, Soup
, Options
, una imagen y una frase de invocación que se usará como sugerencia cuando el usuario registra una frase para que Siri la asocie a la intención:
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;
}
}
Eliminación de donaciones no válidas
Es importante eliminar donaciones que ya no son válidas para que Siri no haga sugerencias de acceso directo poco útiles o confusas.
En Soup Chef, la pantalla Configurar menú se puede usar para marcar un elemento de menú como no disponible. Siri ya no debe sugerir un acceso directo para ordenar el elemento de menú no disponible, por lo que el método RemoveDonation
de SoupMenuManager
elimina donaciones para los elementos de menú que ya no están disponibles. La aplicación implementa esta funcionalidad mediante:
- Búsqueda de pedidos asociados al elemento de menú ahora no disponible.
- Captura de sus identificadores.
- Eliminación de interacciones que tienen los mismos identificadores.
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");
}
});
}
}
Validación de donaciones exitosas
La solución incluye varios proyectos y una configuración específica. En algunos casos, la aplicación puede bloquearse debido a una configuración incompleta, en otros casos, puede no realizar una donación de una interacción de forma silenciosa. Es importante validar las donaciones correctas y la configuración del desarrollador de iOS ayuda con ello. Vaya a Configuración > Desarrollador y habilite las siguientes opciones para desarrolladores para ver donaciones y accesos directos recientes:
- Mostrar accesos directos recientes
- Mostrar donaciones en pantalla de bloqueo
Una vez habilitada, todas las donaciones correctas aparecerán en la pantalla de bloqueo y debajo de las opciones de sugerencias de Siri. Si después de ejecutar la aplicación no ve las donaciones allí, revise los siguientes casos de solución de problemas:
Una aplicación no puede crear la
OrderSoupIntent
con el siguiente error:No se pudo crear una instancia nativa del tipo "NativeLibrary.OrderSoupIntent": no se ha cargado la clase nativa.
Este error significa que Xamarin no puede cargar la clase nativa a través del enlace de Xamarin. Para corregirlo, compruebe que la biblioteca nativa incluye el código necesario, al que hace referencia el proyecto de enlace y que se establecen las marcas adecuadas, como se describe aquí; establezca la marca
Force Load
enOn
.Una aplicación no puede inicializar la instancia nativa cargada de la clase intención con el siguiente error:
No se pudo inicializar una instancia del tipo "NativeLibrary.OrderSoupIntent": el método nativo "init" devolvió nil.
El problema está relacionado con la falta del archivo de definición de intención. La aplicación de Xamarin debe incluir el archivo de definición de intención original con el tipo
Content
, como se describe aquí.Una aplicación crea la intención y llama al método de donación sin bloqueo, pero la salida de la consola muestra una advertencia sobre el tipo de intención desconocido y no se realiza ninguna donación:
No se puede donar la interacción con OrderSoupIntent que no tiene ningún tipo de acceso directo válido
Para corregir el problema, la intención debe estar correctamente definida en el archivo plist, y los derechos de Siri deben habilitarse y seleccionarse para la configuración de compilación actual a través de la configuración del proyecto.
El archivo info.plist de la aplicación:
<key>NSUserActivityTypes</key> <array> <string>ScheduleMeetingIntent</string> </array>
El archivo Entitlements.plist de la aplicación con la funcionalidad Siri:
<key>com.apple.developer.siri</key> <true/>
Los derechos personalizados deben seleccionarse para la configuración de compilación de destino. Vaya a Configuración del proyecto > Compilar > Firmado de lote de iOS y establezca Derechos personalizados en el archivo Entitlements.plist que contiene los derechos necesarios.
Creación de una extensión de Intents
El código que se ejecuta cuando Siri invoca una intención se coloca en una extensión de Intents, que se puede agregar como un nuevo proyecto a la misma solución que una aplicación de Xamarin.iOS existente, como Soup Chef. En la solución SoupChef, la extensión se denomina SoupChefIntents.
SoupChefIntents: Info.plist y Entitlements.plist
SoupChefIntents – Info.plist
El archivo Info.plist del proyecto SoupChefIntents define el Identificador de agrupación como com.xamarin.SoupChef.SoupChefIntents
.
El archivo Info.plist también contiene la siguiente entrada:
<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>
En el archivo Info.plist anterior:
IntentsRestrictedWhileLocked
enumera las intenciones que se van a controlar cuando se desbloquea el dispositivo.IntentsSupported
enumera las intenciones controladas por esta extensión.NSExtensionPointIdentifier
especifica el tipo de extensión de aplicación. Para obtener más información, consulte documentación de Apple.NSExtensionPrincipalClass
especifica la clase que se debe usar para controlar las intenciones admitidas por esta extensión.
SoupChefIntents – Entitlements.plist
El archivo Entitlements.plist del proyecto SoupChefIntents tiene la funcionalidad Grupos de aplicaciones. Esta funcionalidad está configurada para usar el mismo grupo de aplicaciones que el proyecto SoupChef:
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.xamarin.SoupChef</string>
</array>
Soup Chef conserva los datos con NSUserDefaults
. Para compartir datos entre la aplicación y la extensión de la aplicación, hacen referencia al mismo grupo de aplicaciones en sus archivos Entitlements.plist.
Nota:
La configuración de compilación del proyecto SoupChefIntents establece Derechos personalizados para Entitlements.plist.
Control de una tarea en segundo plano OrderSoupIntent
Una extensión de Intents ejecuta las tareas en segundo plano necesarias para un acceso directo basado en una intención personalizada.
Siri llama al método GetHandler
de la clase IntentHandler
(definida en Info.plist como NSExtensionPrincipalClass
) para obtener una instancia de una clase que extiende OrderSoupIntentHandling
, que se puede usar para controlar una 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) { }
}
OrderSoupIntentHandler
, definido en el proyecto SoupKit de código compartido, implementa dos métodos importantes:
ConfirmOrderSoup
: confirma si la tarea asociada a la intención debe ejecutarse realmenteHandleOrderSoup
: coloca el pedido de sopa y responde al usuario llamando al controlador de finalización pasado
Control de un OrderSoupIntent que abre la aplicación
Una aplicación debe controlar correctamente las intenciones que no se ejecutan en segundo plano.
Estas intenciones se controlan de la misma manera que los accesos directos de NSUserActivity
, en el método ContinueUserActivity
de AppDelegate
:
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;
}
// ...
}
Proporcionar una interfaz de usuario para una intención personalizada
Una extensión de interfaz de usuario de Intents proporciona una interfaz de usuario personalizada para una extensión de Intents. En la solución SoupChef, SoupChefIntentsUI es una extensión de interfaz de usuario de Intents que proporciona una interfaz para SoupChefIntents.
SoupChefIntentsUI – Info.plist y Entitlements.plist
SoupChefIntentsUI – Info.plist
El archivo Info.plist del proyecto SoupChefIntentsUI define el Identificador de agrupación como com.xamarin.SoupChef.SoupChefIntentsui
.
El archivo Info.plist también contiene la siguiente entrada:
<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>
En el archivo Info.plist anterior:
IntentsSupported
indica que esta extensión de interfaz de usuario de Intents controlaOrderSoupIntent
NSExtensionPointIdentifier
especifica el tipo de extensión de aplicación. Para obtener más información, consulte documentación de Apple.NSExtensionMainStoryboard
especifica el guión gráfico que define la interfaz principal de esta extensión
SoupChefIntentsUI – Entitlements.plist
El proyecto SoupChefIntentsUI no necesita un archivo Entitlements.plist.
Creación de la interfaz de usuario
Dado que Info.plist para SoupChefIntentsUI establece la claveNSExtensionMainStoryboard
en MainInterface
, el archivo MainInterace.storyboard define la interfaz para la extensión de interfaz de usuario de Intents.
En este guión gráfico, hay un único controlador de vista, de tipo IntentViewController. Hace referencia a dos vistas:
- invoiceView, de tipo
InvoiceView
- confirmationView, de tipo
ConfirmOrderView
Nota:
Las interfaces de invoiceView y confirmationView se definen en Main.storyboard como vistas secundarias. Visual Studio para Mac y Visual Studio 2017 no proporcionan compatibilidad para ver ni editar vistas secundarias; para ello, abra Main.storyboard en Interface Builder de Xcode.
IntentViewController
implementa la interfaz IINUIHostedViewControlling
, que se usa para proporcionar una interfaz personalizada al trabajar con intenciones de Siri. ElEl método ConfigureView
se llama para personalizar la interfaz, mostrando la confirmación o la factura, dependiendo de si la interacción se confirma (INIntentHandlingStatus.Ready
) o se ha ejecutado correctamente (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);
}
Sugerencia
Para obtener más información sobre el método ConfigureView
, vea la presentación de WWDC 2017 de Apple, Novedades de SiriKit.
Creación de un acceso directo de voz
Soup Chef proporciona una interfaz para asignar un acceso directo de voz a cada pedido, lo que permite pedir sopa con Siri. De hecho, iOS proporciona la interfaz que se usa para grabar y asignar accesos directos de voz y requiere poco código personalizado.
En OrderDetailViewController
, cuando un usuario pulsa la fila Agregar a Siri de la tabla, el método RowSelected
presenta una pantalla para agregar o editar un acceso directo de voz:
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);
}
}
}
}
En función de si existe o no un acceso directo de voz existente para el orden mostrado actualmente, RowSelected
presenta un controlador de vista de tipo INUIEditVoiceShortcutViewController
o INUIAddVoiceShortcutViewController
.
En cada caso, OrderDetailViewController
se establece como el Delegate
del controlador de vista, que es el motivo por el que también implementa IINUIAddVoiceShortcutViewControllerDelegate
y IINUIEditVoiceShortcutViewControllerDelegate
.
Pruebas en el dispositivo
Para ejecutar Soup Chef en un dispositivo, siga las instrucciones de esta sección. Lea también la nota sobre el aprovisionamiento automático.
Grupo de aplicaciones, identificadores de aplicación, perfiles de aprovisionamiento
En la sección Certificados, identificadores y perfiles del Portal para desarrolladores de Apple, realice los pasos siguientes:
Cree un grupo de aplicaciones para compartir datos entre la aplicación Soup Chef y sus extensiones. Por ejemplo: group.com.yourcompanyname.SoupChef
Cree tres identificadores de aplicación: uno para la propia aplicación, otro para la extensión de Intents y otro para la extensión de interfaz de usuario de Intents. Por ejemplo:
Aplicación: com.yourcompanyname.SoupChef
- Para este identificador de aplicación, asigne las funcionalidades SiriKit y Grupos de aplicaciones.
Extensión de Intents: com.yourcompanyname.SoupChef.Intents
- Para este identificador de aplicación, asigne la funcionalidad Grupos de aplicaciones.
Extensión de interfaz de usuario de Intents: com.yourcompanyname.SoupChef.Intentsui
- Este identificador de aplicación no necesita funcionalidades especiales.
Después de crear los identificadores de aplicación anteriores, edite la funcionalidad Grupos de aplicaciones asignada a la aplicación y la extensión de Intents, indicando el grupo de aplicaciones específico creado anteriormente.
Cree tres nuevos perfiles de aprovisionamiento de desarrollo, uno para cada uno de los nuevos identificadores de aplicación.
Descargue estos perfiles de aprovisionamiento y haga doble clic en cada uno para instalarlo. Si Visual Studio para Mac o Visual Studio 2017 ya se está ejecutando, reinícielo para asegurarse de que registra los nuevos perfiles de aprovisionamiento.
Edición de Info.plist, Entitlements.plist y código fuente
En Visual Studio para Mac o Visual Studio 2017, siga estos pasos:
Actualice los distintos archivos de Info.plist en la solución. Establezca el Identificador de agrupación de la aplicación, la extensión de Intents y la extensión de interfaz de usuario de Intents en los identificadores de aplicación definidos anteriormente:
- Aplicación: com.yourcompanyname.SoupChef
- Extensión de Intents: com.yourcompanyname.SoupChef.Intents
- Extensión de interfaz de usuario de Intents: com.yourcompanyname.SoupChef.Intentsui
Actualice el archivo Entitlements.plist para el proyecto SoupChef:
- Para la funcionalidad Grupos de aplicaciones, establezca el grupo en el nuevo grupo de aplicaciones creado anteriormente (en el ejemplo anterior, era group.com.yourcompanyname.SoupChef).
- Asegúrese de que SiriKit esté habilitado.
Actualice el archivo Entitlements.plist del proyecto SoupChefIntents:
- Para la funcionalidad Grupos de aplicaciones, establezca el grupo en el nuevo grupo de aplicaciones creado anteriormente (en el ejemplo anterior, era group.com.yourcompanyname.SoupChef).
Por último, abra NSUserDefaultsHelper.cs. Establezca la variable
AppGroup
en el valor del nuevo grupo de aplicaciones (por ejemplo, establézcala engroup.com.yourcompanyname.SoupChef
).
Configuración de las opciones de compilación
En Visual Studio para Mac o Visual Studio 2017:
Abra las opciones o propiedades del proyecto SoupChef. En la pestaña Firma de agrupación de iOS, establezca Identidad de firma en automática y Perfil de aprovisionamiento en el nuevo perfil de aprovisionamiento específico del perfil que creó anteriormente.
Abra las opciones o propiedades del proyecto SoupChefIntents. En la pestaña Firma de agrupación de iOS, establezca Identidad de firma en automática y Perfil de aprovisionamiento en la nueva extensión de Intents específica del perfil que creó anteriormente.
Abra las opciones o propiedades del proyecto de SoupChefIntentsUI. En la pestaña Firma de agrupación de iOS, establezca Identidad de firma en automática y Perfil de aprovisionamiento en la nueva extensión de interfaz de usuario de Intents específica del perfil que creó anteriormente.
Con estos cambios implementados, la aplicación se ejecutará en un dispositivo iOS.
Aprovisionamiento automático
Puede usar el aprovisionamiento automático para realizar muchas de estas tareas de aprovisionamiento directamente en el IDE. Sin embargo, el aprovisionamiento automático no configura grupos de aplicaciones. Tendrá que configurar manualmente los archivos Entitlements.plist con el nombre del grupo de aplicaciones que quiera usar, visitar el Portal para desarrolladores de Apple para crear el grupo de aplicaciones, asignar ese grupo de aplicaciones a cada identificador de aplicación creado mediante el aprovisionamiento automático, regenerar los perfiles de aprovisionamiento (aplicación, extensión de Intents, extensión de interfaz de usuario de Intents) para incluir el grupo de aplicaciones recién creado, y descargarlos e instalarlos.
Vínculos relacionados
- Soup Chef (Swift)
- SiriKit (Apple)
- Introducción a los accesos directos de Siri – WWDC 2018
- Compilación para voz con accesos directos de Siri – WWDC 2018
- Accesos directos de Siri en la esfera de Siri – WWDC 2018
- Novedades de SiriKit – WWDC 2017
- Crear una extensión de aplicación de Intents (Apple)
- Enlazar una biblioteca Swift de iOS