Comandos rápidos de Siri


Una de las características útiles (en mi opinión) de iOS 12 introducidas en WWDC 2018 es Siri Shortcuts .


Atajo ( atajo ): un comando rápido, una forma corta de realizar cualquier acción sin pasar por el script estándar.


En sus aplicaciones, puede tomar atajos para algunas acciones. Aprendiendo cómo y cuándo el usuario los implementa, Siri comienza de manera inteligente, en el momento y lugar correctos, para ofrecerle estos atajos y, lo mejor de todo, ¡el usuario puede llamarlos con frases que él les adjuntará! Debajo del gato más.


Como funciona


Utilizamos aplicaciones que, con ciertas acciones, crean y entregan accesos directos al sistema.


Puede ver estos accesos directos en Configuración → Siri y Buscar .

La captura de pantalla anterior muestra los últimos tres accesos directos que el sistema ha detectado desde diferentes aplicaciones. Si hacemos clic en el botón "Más accesos directos", veremos todos los accesos directos entregados al sistema por cada aplicación.


Con ciertas configuraciones en el código de creación de accesos directos, Siri ofrecerá estos accesos directos al usuario en la pantalla bloqueada, en el centro de notificaciones y búsqueda, centrándose en la frecuencia con la que usamos estas acciones, a qué hora, en qué días de la semana y dónde a otros factores.


Por ejemplo, si el viernes por la noche generalmente busca cajeros automáticos, luego de entrenarse, Siri le ofrecerá un acceso directo con esta acción los viernes por la noche.



Podemos agregar nuestro comando de voz a cada acceso directo si hacemos clic en el icono " + ".


Decimos un comando de voz, presionamos "Listo", y ahora podemos realizar la acción detrás del atajo usando la voz a través de Siri. Resulta que el usuario podrá realizar la funcionalidad de su aplicación a través de Siri sin abrir la aplicación en sí. El acceso directo con la frase se conserva en "Mis accesos directos".


Crear atajos


Para el desarrollo, necesitaremos Xcode 10 e iOS 12. En el momento de la escritura, ambos se encuentran en la etapa Beta .


Se puede crear un acceso directo a través de NSUserActivity o Intent .


El primer caso:


El usuario hace clic en el acceso directo, que pasa el comando con los parámetros ( NSUserActivity ) a nuestra aplicación, y decide cómo se debe procesar este comando (abra la ventana de la tasa actual de USD o la ventana de pedido de nuestra pizza favorita). Este es el buen atajo de Spotlight que todos conocemos, pero que Siri ofrece de manera inteligente.


Segundo caso:


Los accesos directos creados a través de Intent más interesantes: le permiten ejecutar un comando inmediatamente en la interfaz Siri sin iniciar su aplicación. Anteriormente, el conjunto de Intent era difícil para Apple: transferir dinero, enviar mensajes y otros . ¡Ahora, los desarrolladores tenemos la oportunidad de crear nuestras Intent !


Independientemente de cómo se creó el acceso directo, pasa por 3 etapas del ciclo de vida:


  1. Anuncio ( Definir )
  2. Entrega al sistema ( Donar )
  3. Procesamiento por aplicación ( Manija )


Mi investigación mostró que una aplicación no puede entregar más de 20 accesos directos al sistema.


Además, consideraremos cómo darle a nuestra aplicación la capacidad de crear accesos directos y cómo trabajar con ellos dentro de ella.


Crear accesos directos a través de NSUserActivity


Analicemos el primer tipo de acceso directo simple que se abre a través de NSUserActivity .


Por ejemplo, en la aplicación de banco móvil, tenemos una pantalla de búsqueda en cajeros automáticos y a menudo los busco. Para acceder a la pantalla con una tarjeta de cajero automático, tengo que iniciar la aplicación, ir a la pestaña "Más" en la pestaña, seleccionar la sección "Información" y hacer clic en el botón "Cajeros automáticos".
Si creamos un acceso directo que conduce inmediatamente a esta pantalla, el usuario podrá acceder a él con un solo toque cuando Siri se lo ofrezca, por ejemplo, en una pantalla bloqueada.


Declarar acceso directo


El primer paso es declarar un tipo como nuestra NSUserActivity (podemos decir que este es su identificador) en info.playlist :


 <key>NSUserActivityTypes</key> <array> <string>ru.tinkoff.demo.show-cashMachine</string> </array> 

Anunciado


Entregue el acceso directo al sistema (Donar)


Después de la declaración, podemos crear NSUserActivity en el código de nuestra aplicación con el tipo que establecimos anteriormente en info.playlist :


 let activity = NSUserActivity(activityType: "ru.tinkoff.demo.show-cashMachine") 

Para que la actividad entre en la lista de accesos directos del sistema, debe establecerse en title y establecer la propiedad isEligibleForSearch en true . No son necesarias otras propiedades para agregar accesos directos, pero su presencia hace que el acceso directo sea más legible y fácil de usar.


  //   (     ,   ) activity.title = " " if #available(iOS 12.0, *) { // ,             activity.suggestedInvocationPhrase = "  " //           activity.isEligibleForPrediction = true // (     ,   ) activity.isEligibleForSearch = true } //       let attributes = CSSearchableItemAttributeSet(itemContentType: "NSUserActivity.searchableItemContentType") ///    if let image = UIImage(named: "siriAtmIcon") { attributes.thumbnailData = UIImagePNGRepresentation(image) } ///   attributes.contentDescription = "     " ///     activity.contentAttributeSet = attributes 

Fuego! NSUserActivity es, para entregarlo al sistema, queda por dar el último paso.


ViewConroller tiene una propiedad userActivity , a la que debemos asignar la activity creada anteriormente:


 self.userActivity = activity 

Tan pronto como se ejecute esta línea, se creará un acceso directo a partir de esta actividad. Se entregará al sistema y se mostrará en la configuración de Siri ( Configuración → Siri y Buscar ). Entonces Siri podrá ofrecérselo al usuario, y el usuario podrá asignarle su comando de voz.


Nota : La documentación de Apple dice que en lugar de asignar actividad al controlador de vista, es suficiente llamar al método becomeCurrent() en la becomeCurrent() . Sin embargo, esta acción no entregó actividad a mi sistema y el acceso directo no apareció en la lista


Luego, llame al método becomeCurrent() en el objeto de actividad del usuario para marcarlo como actual, que dona la actividad a Siri. Alternativamente, puede adjuntar el objeto a un objeto UIViewController o UIResponder, que también marca la actividad como actual.

Para verificar que todo funcionó, abra Configuración> Siri y busque : el acceso directo basado en nuestra actividad debe estar en la lista.


Atajos de procesamiento por aplicación (asa)


Cuando un usuario navega por el acceso directo desde el centro de notificaciones o lo activa por voz, la aplicación se inicia y debemos procesar este acceso directo.


activity lanzada en AppDelegate 'un método:


 func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { if userActivity.activityType == "ru.tinkoff.demo.show-cashMachine" { //   ,     handleShowCashMachineActivity() return true } return false } 

Total


Un acceso NSUserActivity NSUserActivity se crea de la siguiente manera:


  1. Declare el tipo (identificador) de NSUserActivity en NSUserActivity .
  2. Creamos NSUserActivity en el código y configuramos
  3. viewController' .

Crear comandos de voz desde una aplicación


Entonces, si el usuario abre Configuración> Siri y busca , verá una lista de sus accesos directos, que fue creada por varias aplicaciones, incluida la nuestra. Al hacer clic en " + ", el usuario puede crear cualquier comando de voz y asociarlo con el acceso directo seleccionado. Sin embargo, cada vez que ingresar la configuración es inconveniente para el usuario, muchos ni siquiera se dan cuenta de esta posibilidad.


Es genial que pueda adjuntar un comando de voz a una acción específica directamente dentro de la aplicación.


Supongamos que un usuario realiza alguna acción, se entrega al sistema, quiere guardarlo. Podemos agregar el botón " agregar acción a Siri " (puede nombrar y dibujar el botón como desee) en la pantalla de nuestra aplicación, luego el usuario, al hacer clic en él, podrá asociar esta acción con un comando de voz desde la aplicación sin entrar en la configuración.


Al hacer clic en el botón, debe abrir la pantalla para agregar un comando de voz a un acceso directo en Siri INUIAddVoiceShortcutViewController , o la pantalla para editar un comando de voz INUIEditVoiceShortcutViewController , si ya se ha creado uno. La action no reaccionada action dicho botón será aproximadamente la siguiente:


  @IBAction func addToSiriAction() { // 1.   ,       INVoiceShortcutCenter.shared.getAllVoiceShortcuts { (shortcuts, error) in guard error == nil, let shortcuts = shortcuts else { // TODO: Handle error return } // 2.     ,      let donatedShortcut: INVoiceShortcut? = shortcuts.first(where: { (shorcut) -> Bool in return shorcut.__shortcut.userActivity?.activityType == "com.ba" }) if let shortcut = donatedShortcut { // 3.     -     . //         let editVoiceShortcutViewController = INUIEditVoiceShortcutViewController(voiceShortcut: shortcut) editVoiceShortcutViewController.delegate = self self.present(editVoiceShortcutViewController, animated: true, completion: nil) } else { // 4.         let shortcut = INShortcut(userActivity: self.userActivity!) let addVoiceShortcutViewController = INUIAddVoiceShortcutViewController(shortcut: shortcut) addVoiceShortcutViewController.delegate = self } } } 

Entonces, las pantallas para agregar y editar un comando de voz para el acceso directo de Siri se ven:



También debemos implementar los métodos delegados de estos viewControllers, en los que deben ocultar el dismiss(animated: true, completion: nil) y, si es necesario, actualizar la pantalla actual. Por ejemplo, si antes había un botón "agregar comando de voz" en la pantalla, luego de agregar un comando de voz este botón debería desaparecer o cambiar a "editar comando de voz".


Atajos de intención


Hasta ahora, solo hemos hablado de accesos directos que abren una aplicación y pasan NSUserActivity datos allí a NSUserActivity .


Pero volvamos a los accesos directos creados a través de Intent , que le permiten realizar algunas acciones sin abrir la aplicación. Aquí comienza la diversión.


Imagine que un usuario ordena su pizza favorita. Lo pedirá muchas veces cuando lo desee, e incluso agregó un comando de voz al atajo de esta pizza, y esto simplifica su vida. Pero podemos hacer más por él: podemos asegurarnos de que al dar el comando de voz Siri, el sistema no lo arroje a la aplicación, sino que muestre información de pedidos y pida pizza de inmediato en la interfaz Siri. Este es solo el caso cuando el usuario no necesita abrir la aplicación para realizar alguna acción.


Primero, vaya a la configuración del proyecto, seleccione el objetivo principal, la pestaña Capabilities y habilite el acceso a Siri.


Nuestra aplicación puede interactuar con Siri, pero esto no sucede en el código principal de la aplicación, sino en extensiones de intenciones de extensiones de destino separadas


Para comenzar, se debe crear este objetivo: Archivo → Nuevo → Objetivo , seleccione Extensiones de intención . Xcode ofrecerá crear otra extensión de destino para la ventana que muestra sus acciones en Siri, si es necesario, estamos de acuerdo.



Declarar acceso directo


La principal innovación de SiriKit en iOS 12 es la capacidad de crear sus Inetnts , para aquellos que eran anteriores.



Para hacer esto, cree un nuevo archivo: Archivo → Nuevo → Archivo , seleccionando Archivo de definición de intención SiriKit en la sección Recurso .



Como resultado, aparece un archivo con la extensión .intentdefinition , en el que puede crear sus propios Intents . Abrimos el archivo, y donde dice " Sin intenciones " en la parte inferior hay un icono " + " - haga clic en él. " Nueva intención ". Aparecerá una intención en la lista a la que puede agregar parámetros. En el caso de un pedido de pizza, puede agregar el número de pizzas y el tipo de pizza para ordenar como parámetros. Para la cantidad elegimos el tipo Integer , y para el tipo de pizza seleccionamos el tipo Custom , que en el código estará representado por la clase INObject .


Ahora un par de líneas de frustración:


El usuario no podrá transmitir diferentes parámetros al mismo comando de voz guardado. ¡Ay!



¿Cuáles son los parámetros para:


Suponga que crea una entidad "Mostrar tasa %currency ", donde la currency es un parámetro de entidad. Esto no significa que el usuario pueda decir las frases "Mostrar la tasa de cambio del dólar", "Mostrar la tasa de cambio de Bitcoin", etc. Fuera de la caja, esto no funcionará así. Pero esto significa que si el usuario miró la tasa de cambio del dólar, se creó el acceso directo "Mostrar tasa de USD", entonces cuando miró la tasa de cambio de Bitcoin, se creó el acceso directo "Mostrar la tasa de BTC", etc. En otras palabras, puede tener varias shorkatas que se basan en la misma intención, pero con diferentes parámetros. Cada uno de los accesos directos, el usuario podrá solicitar su comando de voz.


Bueno, al crear una intención en el archivo .intentdefinition , Xcode generará automáticamente una clase para esa intención (nota: no aparecerá en los archivos del proyecto, pero estará disponible para su uso). Este archivo generado automáticamente solo estará en aquellos destinos que tengan el archivo .intentdefinition .


Después de crear la intención en el archivo .intentdefinition , podemos crear nuestras intenciones en el código.


 let intent = OrderPizzaIntent() 

Entregue el acceso directo al sistema (Donar)


Para que esta entidad se incluya en la lista de accesos directos, debe incrustarla. Para hacer esto, se INInteraction un objeto INInteraction con una instancia de su intención, y se llama al método .donate en esta .donate


 let intent = OrderPizzaIntentf() // ...   let interaction = INInteraction(intent: intent, response: nil) interaction.donate { (error) in // ...   /  } 

Después de ejecutar este código, el acceso directo basado en la intención se entregará al sistema y se mostrará en la Configuración de Siri.


Procesamos la aplicación de acceso directo (Handle)


El siguiente paso es procesar el intento cuando el usuario hace clic en él en el Sirjest del Siri o lo llama con un comando de voz.


Ya hemos creado una extensión de destino para Siri y tiene una clase IntentHandler pre-creada, que tiene un único método: `` manejar (por intención) ''


  class IntentHandler: INExtension { override func handler(for intent: INIntent) -> Any { guard intent is OrderPizzaIntent else { fatalError("Unhandled intent type: \(intent)") } return OrderPizzaIntentHandler() } } 

Nota: Si el compilador no ve la clase de su intención, entonces no ha agregado el archivo de extensión de destino .intentdefinition para Siri.

En este método, determinamos el tipo de intención entrante y para cada tipo creamos un objeto controlador que procesará esta intención. Cree un controlador para nuestro OrderPizzaIntent e implemente el protocolo OrderPizzaIntentHandling en él, que ya se genera automáticamente después de crear su Intención en .intentdefinition .


El protocolo contiene dos métodos de confirm y handle . Primero, se llama a confirm donde se verifican todos los datos y se verifica la disponibilidad de la acción. Luego, el handle funcionará en una acción corta que se realizará.


 public class OrderPizzaIntentHandler: NSObject, OrderPizzaIntentHandling { public func confirm(intent: OrderPizzaIntent, completion: @escaping (OrderPizzaIntentResponse) -> Void) { //      // ... completion(OrderPizzaIntentResponse(code: OrderPizzaIntentResponseCode.ready, userActivity: nil)) } public func handle(intent: OrderPizzaIntent, completion: @escaping (OrderPizzaIntentResponse) -> Void) { //     // ... completion(OrderPizzaIntentResponse(code: OrderPizzaIntentResponseCode.success, userActivity: nil)) } } 

Ambos métodos definitivamente deben completion llamada con la respuesta OrderPizzaIntentResponse (también se genera automáticamente), de lo contrario, Siri solo esperará mucho tiempo y luego dará un error.


Respuestas más detalladas de Siri


Existe un conjunto estándar de códigos de respuesta generados automáticamente: enum OrderPizzaIntentResponseCode , pero pueden no ser suficientes para una interfaz amigable. Por ejemplo, en la etapa de confirm , pueden ocurrir varios errores diferentes: la pizza se ha agotado, la pizzería no funciona en este momento, etc. y el usuario debe conocer estos hechos, en lugar del mensaje estándar "Error de aplicación". ¿Recuerdas que creamos Intent en el archivo .intentdefinition ? Junto con la intención en sí, Response su Response en la que puede agregar sus propias opciones para errores y respuestas exitosas, y configurarlas con los parámetros:



Ahora podemos decirle al usuario más errores informativos y respuestas:


 public func confirm(intent: OrderPizzaIntent, completion: @escaping (OrderPizzaIntentResponse) -> Void) { guard let pizzaKindId = intent.kind?.identifier else { //         -     completion(OrderPizzaIntentResponse(code: .failure, userActivity: nil)) return } if pizzeriaManager.isPizzeriaClosed == true { ///     -     completion(OrderPizzaIntentResponse(code: .failurePizzeriaClosed, userActivity: nil)) return } else if pizzeriaManager.menu.isPizzaUnavailable(identifier: pizzaKindId) { ///      -     completion(OrderPizzaIntentResponse(code: .failurePizzaUnavailable(kind: intent.kind), userActivity: nil)) return } //     -    completion(OrderPizzaIntentResponse(code: .ready, userActivity: nil)) } 

Intent representación


Si creamos una extensión de destino de la interfaz de usuario de Intent Extension , entonces podemos dibujar una vista personalizada en Siri para los intentos que necesitamos. Tenemos MainInterface.storyboard e IntentViewController en el que podemos esbozar su diseño. Este controlador de vista implementa el protocolo INUIHostedViewControlling y la vista se configureView en el método configureView


 // Prepare your view controller for the interaction to handle. func configureView(for parameters: Set<INParameter>, of interaction: INInteraction, interactiveBehavior: INUIInteractiveBehavior, context: INUIHostedViewContext, completion: @escaping (Bool, Set<INParameter>, CGSize) -> Void) { // Do configuration here, including preparing views and calculating a desired size for presentation. completion(true, parameters, self.desiredSize) } var desiredSize: CGSize { return self.extensionContext!.hostedViewMaximumAllowedSize } 

Para llamar a este método, es necesario agregar el nombre de nuestra intención a la matriz NSExtension -> NSExtensionAttributes -> IntentsSupported , que se refiere a la interfaz de usuario de Intentos de destino de extensión


 <key>NSExtension</key> <dict> <key>NSExtensionAttributes</key> <dict> <key>IntentsSupported</key> <array> <string>OrderPizzaIntent</string> </array> </dict> 

Dependiendo del diseño de su vista en Siri y de la interaction.intent que se introdujo en el método, puede dibujar esta vista de la manera que desee. A continuación se muestran capturas de pantalla de cómo se ve nuestra intención en Siri, en la búsqueda y en una pantalla bloqueada.



Vale la pena considerar que el usuario no podrá interactuar con botones, desplazamiento y otros controles en su vista, ya que el método se llama con el parámetro interactiveBehavior = .none , esto ciertamente impone una serie de limitaciones.


Total


Un Intent directo basado en la Intent puede representarse en la interfaz siri o en el centro de notificaciones y realizar una acción sin abrir la aplicación. Para crearlo necesitas:


  1. Habilitar capacidades para usar Siri
  2. Crear extensiones de intenciones y UI de extensiones de intenciones
  3. Crear archivo de definición de intención SiriKit
  4. Creamos nuestra Intent en este archivo y le asignamos parámetros.
  5. Cree un IntentHandler en el que implementemos los hanlde confirm y hanlde

Recomendaciones


Código genérico en el objetivo de extensión Siri y la aplicación principal


Si tiene un código que se usa tanto en el destino de Siri como en el objetivo del proyecto principal, hay 2 formas de resolver este problema:


  1. Resalte las clases comunes y agréguelos a ambos objetivos. ( Ver → Utilidades → Mostrar inspector de archivos 'e, en la sección Membresía de destino agregue marcas de verificación a los objetivos que necesitan acceso al archivo seleccionado)
  2. Cree uno o más marcos de destino y lleve el código general allí.

El último método es preferible, ya que puede usar estos marcos en otras extensiones y proyectos. También vale la pena señalar que es aconsejable establecer el indicador Allow app extension API only para estos marcos, luego, al desarrollar el marco, el compilador jurará si intenta usar una API que es ilegal en el desarrollo de extensiones (por ejemplo, UIApplication ).


Los recursos compartidos se pueden hurgar entre objetivos a través de Grupos de aplicaciones


Depuración


Los accesos directos de prueba ayudarán a ayudar:


  1. Configuración del teléfono Configuración → Desarrollador : Mostrar accesos directos recientes y Mostrar donaciones en los interruptores de la pantalla de bloqueo :


  1. Para probar Intens, puede iniciar inmediatamente la extensión de destino especificando en Xcode la frase con la que Siri se abre. Para hacer esto, seleccione el esquema para la extensión de destino Siri


Haga clic en este objetivo, haga clic en Editar esquema ...



En el campo Consulta de intención de Siri , ingrese una frase con la que Siri ya comenzará, como si ya lo hubiera dicho.


Total


Propongo parar y resumir lo que hicimos:


  1. Los accesos directos se pueden crear a través de NSUserActivity o a través de INIntent
  2. Los atajos se deben declarar (declarar), informar al sistema (donar) y procesar (manejar).
  3. Puede agregar el botón " Agregar a Siri " a la aplicación, haciendo clic en el cual el usuario puede agregar una frase para la acción y luego llamarla con su voz.
  4. Puede crear sus propias Intents además de las integradas.
  5. Mediante accesos Intents basados ​​en intenciones Intents puede crear acciones que se realizarán a través de la interfaz Siri (ya sea en una pantalla bloqueada o en búsqueda) sin la necesidad de abrir la aplicación.

En la documentación de Apple hay un enlace al proyecto de demostración , que es útil para descargar y centrarse en él durante el desarrollo.


Me gustaría enfatizar que al momento de escribir este artículo es una API en la etapa beta . Y a menudo atrapo problemas y errores. Durante el trabajo, periódicamente encontré lo siguiente:


  • , Intent Siri, .
  • Siri .
  • Siri.

Referencias


  1. WWDC 2018, session 211: Introduction to Siri Shortcuts
  2. WWDC 2018, session 214: Building for Voice with Siri Shortcuts
  3. Apple Developer: SiriKit
  4. Apple Developer: INUIHostedViewControlling
  5. Demo Soup Chef Apple

Source: https://habr.com/ru/post/es414677/


All Articles