Fermer

avril 11, 2018

Les intentions de SiriKit vontelles s'adapter à votre application? Si oui, voici comment les utiliser


Depuis iOS 5, Siri a aidé les utilisateurs d'iPhone à envoyer des messages, à définir des rappels et à rechercher des restaurants avec les applications d'Apple. Depuis iOS 10, nous avons également pu utiliser Siri dans certaines de nos applications.

Pour utiliser cette fonctionnalité, votre application doit s'intégrer dans les «domaines et intentions» prédéfinis de Siri d'Apple. En savoir plus sur ce que ceux-ci sont et voir si nos applications peuvent les utiliser. Nous allons prendre une application simple qui est un gestionnaire de liste de choses à faire et apprendre à ajouter le support Siri. Nous passerons également en revue les directives du site Web du développeur Apple sur la configuration et le code Swift pour un nouveau type d'extension introduit avec SiriKit: l'extension Intents .

Lorsque vous arriverez à la partie codage de cette article, vous aurez besoin de Xcode (au moins la version 9.x), et il serait bon si vous êtes familier avec le développement iOS dans Swift parce que nous allons ajouter Siri à une petite application de travail. Nous allons suivre les étapes de la mise en place d'une extension sur le site Web des développeurs d'Apple et de l'ajout du code d'extension Siri à l'application.

Parfois j'utilise mon téléphone sur mon canapé, les deux mains libres. écran mon attention complète. Je vais peut-être envoyer un texto à ma sœur pour planifier l'anniversaire de notre mère ou répondre à une question à Trello. Je peux voir l'application. Je peux taper sur l'écran. Je peux taper.

Mais je suis peut-être en train de me promener dans ma ville en écoutant un podcast lorsqu'un texte arrive sur ma montre. Mon téléphone est dans ma poche et je ne peux pas répondre facilement en marchant.

Avec Siri, je peux appuyer sur le bouton de contrôle de mon casque et dire: «Envoyez une lettre à ma sœur pour que je sois là avant deux heures. "Siri est génial quand vous êtes en déplacement et ne peut pas accorder toute l'attention à votre téléphone ou lorsque l'interaction est mineure, mais cela nécessite plusieurs taps et un tas de frappe.

C'est très bien si je veux utiliser Apple applications pour ces interactions. Mais certaines catégories d'applications, comme la messagerie, ont des alternatives très populaires. D'autres activités, comme réserver un tour ou réserver une table dans un restaurant, ne sont même pas possibles avec les applications intégrées d'Apple, mais sont parfaites pour Siri.

Approche d'Apple pour les assistants vocaux

Pour activer Siri dans un tiers applications, Apple a dû décider d'un mécanisme pour prendre le son de la voix de l'utilisateur et en quelque sorte l'obtenir à l'application de manière à pouvoir répondre à la demande. Pour que cela soit possible, Apple demande à l'utilisateur de mentionner le nom de l'application dans la demande, mais il avait plusieurs options pour faire le reste de la demande.

  • Il aurait pu envoyer un fichier son à l'application. ] L'avantage de cette approche est que l'application pourrait essayer de gérer littéralement toute requête que l'utilisateur pourrait avoir pour cela. Amazon ou Google aurait aimé cette approche car ils disposent déjà de services de reconnaissance vocale sophistiqués. Mais la plupart des applications ne seraient pas capables de gérer cela très facilement.
  • Il aurait pu transformer le discours en texte et l'envoyer.
    Parce que de nombreuses applications n'ont pas d'implémentations sophistiquées en langage naturel, l'utilisateur devrait tenez-vous-en à des phrases très particulières, et le développeur de l'application pourrait mettre en œuvre un support non anglais
  • Il aurait pu vous demander de fournir une liste de phrases que vous comprenez.
    Ce mécanisme est plus proche de ce qu'Amazon fait avec Alexa (dans son cadre de «compétences»), et il permet beaucoup plus d'utilisations d'Alexa que SiriKit peut actuellement gérer. Dans une compétence Alexa, vous fournissez des expressions avec des variables d'espace réservé qu'Alexa remplira pour vous. Par exemple, "Alexa, rappelez-moi à $ TIME $ à $ REMINDER $ " – Alexa lancera cette phrase contre ce que l'utilisateur a dit et vous dira les valeurs pour TIME et RAPPEL . Comme avec le mécanisme précédent, le développeur doit faire toute la traduction, et il n'y a pas beaucoup de flexibilité si l'utilisateur dit quelque chose de légèrement différent.
  • Il pourrait définir une liste de requêtes avec des paramètres et envoyer une application à demande structurée
    C'est effectivement ce que fait Apple, et l'avantage est qu'il peut supporter une variété de langages, et il fait tout le travail pour essayer de comprendre toutes les façons dont un utilisateur peut formuler une requête. Le gros inconvénient est que vous ne pouvez implémenter des gestionnaires que pour les requêtes définies par Apple. C'est génial si vous avez, par exemple, une application de messagerie, mais si vous avez un service de diffusion musicale ou un lecteur de podcast, vous n'avez aucun moyen d'utiliser SiriKit maintenant.

De même, parlez à l'utilisateur: avec le son, avec le texte converti, ou en exprimant le genre de chose que vous voulez dire et en laissant le système déterminer la manière exacte de l'exprimer. La dernière solution (qui est ce qu'Apple fait) place le fardeau de la traduction sur Apple, mais il vous donne des moyens limités d'utiliser vos propres mots pour décrire les choses.

Les types de requêtes que vous pouvez gérer sont définis dans les domaines et les intentions de SiriKit . Une intention est un type de requête qu'un utilisateur peut faire, comme envoyer un texto à un contact ou trouver une photo. Chaque intention a une liste de paramètres – par exemple, le texting nécessite un contact et un message.

Un domaine est juste un groupe d'intentions apparentées. La lecture d'un texte et l'envoi d'un texte sont tous deux dans le domaine de la messagerie. Réservation d'un trajet et obtenir un emplacement sont dans le domaine du covoiturage. Il existe des domaines pour faire des appels VoIP, commencer des séances d'entraînement, rechercher des photos et quelques autres choses. La documentation de SiriKit contient une liste complète des domaines et leurs intentions .

Une critique commune de Siri est qu'il semble incapable de traiter les requêtes aussi bien que Google et Alexa, et que l'écosystème vocal tiers par les concurrents d'Apple est plus riche.

Je suis d'accord avec ces critiques. Si votre application ne rentre pas dans les intentions actuelles, vous ne pouvez pas utiliser SiriKit, et vous ne pouvez rien faire. Même si votre application est adaptée, vous ne pouvez pas contrôler tous les mots que Siri dit ou comprend; Donc, si vous avez une façon particulière de parler de choses dans votre application, vous ne pouvez pas toujours l'enseigner à Siri.

L'espoir des développeurs iOS est à la fois qu'Apple élargisse considérablement sa liste d'intentions et que son langage naturel le traitement devient beaucoup mieux. Si c'est le cas, nous aurons un assistant vocal qui fonctionnera sans que les développeurs aient à traduire ou à comprendre toutes les façons de dire la même chose. Et mettre en œuvre le support pour les requêtes structurées est en fait assez simple à faire – beaucoup plus facile que de construire un analyseur de langage naturel.

Un autre gros avantage du framework intents est qu'il n'est pas limité aux requêtes Siri et vocales. Même maintenant, l'application Maps peut générer une requête basée sur les intentions de votre application (par exemple, une réservation de restaurant). Il le fait par programme (pas de voix ou de langage naturel). Si Apple permettait aux applications de découvrir les intentions exposées les unes des autres, nous aurions une bien meilleure façon de travailler ensemble (par opposition aux URL de style callback ).

est une requête structurée avec des paramètres, il existe un moyen simple pour une application d'exprimer que les paramètres sont manquants ou qu'elle a besoin d'aide pour distinguer certaines options. Siri peut alors poser des questions de suivi pour résoudre les paramètres sans que l'application ait besoin de mener la conversation.

Le domaine Ride-Booking

Pour comprendre les domaines et les intentions, regardons le domaine de réservation de voyage . C'est le domaine que vous utiliseriez pour demander à Siri de vous procurer une voiture Lyft.

Apple définit comment demander un trajet et comment obtenir des informations à ce sujet, mais il n'y a en fait aucune application Apple intégrée qui peut effectivement gérer cette demande. C'est l'un des rares domaines où une application compatible avec SiriKit est requise.

Vous pouvez invoquer l'une des intentions via la voix ou directement depuis Maps. Certaines des intentions pour ce domaine sont:

  • Demander un tour
    Utilisez celui-ci pour réserver un tour. Vous devrez fournir un lieu de prise en charge et de restitution, et l'application pourrait également avoir besoin de connaître la taille de votre groupe et le type de trajet que vous souhaitez. Un exemple de phrase pourrait être: «Réservez-moi un tour avec
  • Obtenez le statut de la balade
    Utilisez cette intention pour savoir si votre demande a été reçue et pour obtenir des informations sur le véhicule et le conducteur. emplacement. L'application Cartes utilise cette intention pour afficher une image mise à jour de la voiture alors qu'elle s'approche de vous
  • Annuler une balade
    Utilisez cette option pour annuler une balade que vous avez réservée.

Pour l'une de ces intentions, Siri pourrait avoir besoin de connaître plus d'informations. Comme vous le verrez lorsque nous implémentons un gestionnaire d'intention, votre extension Intents peut indiquer à Siri qu'un paramètre requis est manquant et Siri le demandera à l'utilisateur.

Le fait que les intentions puissent être invoquées par programme par Maps montre comment les intentions pourrait permettre la communication inter-applications dans le futur.

Note : Vous pouvez obtenir une liste complète des domaines et de leurs intentions sur le site Web des développeurs d'Apple. Il y a aussi un exemple d'application Apple avec de nombreux domaines et intentions implémentés y compris ride-booking.

Ajout des listes et du support du domaine Notes à votre application

OK, maintenant que nous comprenons les bases de SiriKit , regardons comment vous allez ajouter le support pour Siri dans une application qui implique beaucoup de configuration et une classe pour chaque intention que vous voulez gérer.

Le reste de cet article se compose des étapes détaillées pour ajouter le support Siri à une application. Il y a cinq choses de haut niveau à faire:

  1. Préparez-vous à ajouter une nouvelle extension à l'application en créant des profils de provisionnement avec de nouveaux droits sur le site Web des développeurs d'Apple
  2. Utiliser le modèle de Xcode pour commencer avec un exemple de code
  3. Ajouter le code pour soutenir votre intention Siri
  4. Configurer le vocabulaire de Siri via plist

Ne vous inquiétez pas: nous passerons en revue chacun d'entre eux, en expliquant les extensions et les droits en cours de route.

Pour me concentrer uniquement sur les parties Siri, j'ai préparé un simple gestionnaire de liste de tâches, List-o-Mat

 Un GIF animé montrant une démo de List-o-Mat
Faire des listes dans List-o-Mat ( Grand aperçu )

Vous pouvez trouver le source complète de l'exemple, List-o-Mat, sur GitHub .

Pour le créer, tout ce que j'ai fait c'est commencer par l'application Xcode Master-Detail templ mangé et faire les deux écrans dans un UITableView . J'ai ajouté un moyen d'ajouter et de supprimer des listes et des éléments, et un moyen de cocher les éléments comme fait.

Pour stocker les données, j'ai utilisé le protocole Codable ( introduit à la WWDC 2017 ), qui transforme les structures en JSON et économise dans un fichier texte dans le dossier documents .

J'ai délibérément gardé le code très simple. Si vous avez de l'expérience avec Swift et que vous faites des contrôleurs de vue, vous ne devriez pas avoir de problème avec cela.

Maintenant, nous pouvons suivre les étapes d'ajout du support de SiriKit. Les étapes de haut niveau sont les mêmes pour toutes les applications et tous les domaines et toutes les intentions que vous envisagez d'implémenter. Nous traiterons principalement du site Web des développeurs d'Apple, en éditant plist s et en écrivant un peu de Swift.

Pour List-o-Mat, nous nous concentrerons sur le domaine des listes et des notes qui est largement applicable à des choses comme les applications de prise de notes et les listes de tâches

Dans le domaine des listes et des notes, nous avons les intentions suivantes qui auraient du sens pour notre application.

  • Obtenir une liste Ajouter une nouvelle tâche à une liste

Comme les interactions avec Siri se produisent réellement en dehors de votre application (peut-être même lorsque votre application n'est pas en cours d'exécution), iOS utilise une extension pour l'implémenter. The Intents Extension

Si vous n'avez pas travaillé avec des extensions, vous devez connaître trois choses principales:

  1. Une extension est un processus séparé. Il est livré à l'intérieur de l'ensemble de votre application, mais il fonctionne entièrement seul, avec son propre bac à sable.
  2. Votre application et votre extension peuvent communiquer entre elles en étant dans le même groupe d'applications. Le moyen le plus simple consiste à utiliser les dossiers sandbox partagés du groupe (ainsi, ils peuvent lire et écrire dans les mêmes fichiers si vous les mettez)
  3. Les extensions nécessitent leurs propres ID d'application, profils et droits. Pour commencer, connectez-vous à votre compte de développeur et accédez à la section "Certificats, identifiants et profils"

    Mise à jour des données de votre compte Apple Developer

    Dans notre compte développeur Apple, La première chose à faire est de créer un groupe d'applications. Accédez à la section "Groupes d'applications" sous "Identificateurs" et ajoutez-en une.

     Une capture d'écran de la boîte de dialogue du développeur Apple pour enregistrer un groupe d'applications
    Enregistrement d'un groupe d'applications ( Grand aperçu ) [19659071] Il doit commencer par le groupe suivi de votre identifiant de domaine inversé habituel. Parce qu'il a un préfixe, vous pouvez utiliser l'identifiant de votre application pour le reste.

    Ensuite, nous devons mettre à jour l'identifiant de notre application pour utiliser ce groupe et activer Siri:

    1. Allez à la section "App IDs" et cliquez
    2. Activer les groupes d'applications (si cette option n'est pas activée pour une autre extension)
       Une capture d'écran du site Web des développeurs Apple permettant l'activation des groupes d'applications
      Activer groupes d'applications ( Grand aperçu )
    3. Puis configurez le groupe d'applications en cliquant sur le bouton "Modifier".
       Une capture d'écran du dialogue du site Web du développeur Apple pour définir le nom du groupe d'applications
      Définissez le nom du groupe d'applications ( Grand aperçu )
    4. Activer SiriKit.
       Une capture d'écran de SiriKit étant activée
      Activer SiriKit ( Grand aperçu )
    5. Cliquez sur "Terminé" pour le sauvegarder. Maintenant, nous devons créer un nouvel identifiant d'application pour notre extension:

      1. Dans la même section "App IDs", ajoutez un nouvel identifiant d'application. Ce sera l'identifiant de votre application, avec un suffixe. Do pas utilise simplement Intents comme suffixe car ce nom deviendra le nom de votre module dans Swift et entrerait alors en conflit avec le réel Intents . Créer un ID d'application pour l'extension Intents ( Grand aperçu )
  4. Activer cet ID d'application pour les groupes d'applications ( et configurez le groupe comme nous l'avons déjà fait.)

Maintenant, créez un profil de provisionnement de développement pour l'extension Intents, et régénérez le profil d'approvisionnement de votre application. Téléchargez et installez-les comme vous le feriez normalement

Maintenant que nos profils sont installés, nous devons aller sur Xcode et mettre à jour les droits de l'application.

Mise à jour des droits de votre application Dans Xcode

le nom du projet dans le navigateur du projet. Ensuite, choisissez la cible principale de votre application et accédez à l'onglet "Capacités". Là, vous verrez un commutateur pour activer le support Siri.

 Une capture d'écran de l'écran des droits de Xcode montrant SiriKit est activée
Activer SiriKit dans les droits de votre application. ( Grand aperçu )

Plus bas dans la liste, vous pouvez activer les groupes d'applications et les configurer.

 Une capture d'écran de l'écran des droits de Xcode montrant le groupe d'applications est activée et configurée "width =" "height =" 214
Configurer le groupe d'applications de l'application ( Grand aperçu )

Si vous l'avez configuré correctement, vous le verrez dans votre application .entitlements file:

 Une capture d'écran du plist de l'App montrant que les droits sont définis
Le plist montre les droits que vous avez définis ( Large preview )

Maintenant, nous sommes enfin prêts à ajouter Intents extension target to our projet

Ajout de l'extension Intents

Nous sommes enfin prêts à ajouter l'extension. Dans Xcode, choisissez "Fichier" → "Nouvelle cible". Cette feuille apparaîtra:

 Une capture d'écran montrant l'extension Intents dans la boîte de dialogue Nouvelle Cible dans Xcode
Ajouter l'extension Intents à votre projet ( Large preview )

Choisissez "Intents Extension" et cliquez sur le bouton "Next". Remplissez l'écran suivant:

 Un screeenshot de Xcode montrant comment vous configurez l'extension Intents
Configurez l'extension Intents ( Large preview )

Le nom du produit doit correspondre à ce que vous avez fait suffixe dans l'ID de l'application intents sur le site Web du développeur Apple.

Nous choisissons de ne pas ajouter d'extension d'interface utilisateur. Ceci n'est pas couvert dans cet article, mais vous pouvez l'ajouter plus tard si vous en avez besoin. Fondamentalement, c'est un moyen de mettre votre propre style de marque et d'affichage dans les résultats visuels de Siri.

Lorsque vous avez terminé, Xcode créera une classe de gestionnaire d'intentions que nous pouvons utiliser comme élément de départ pour notre implémentation Siri. Intents Handler: Résoudre, Confirmer et gérer

Xcode a généré une nouvelle cible qui a un point de départ pour nous.

La ​​première chose à faire est de configurer cette nouvelle cible pour être dans le même groupe d'applications que l'application. . Comme précédemment, accédez à l'onglet "Capacités" de la cible, activez les groupes d'applications et configurez-le avec le nom de votre groupe. N'oubliez pas que les applications du même groupe disposent d'un sandbox qu'elles peuvent utiliser pour partager des fichiers entre elles. Nous en avons besoin pour que Siri puisse accéder à notre application.

List-o-Mat a une fonction qui renvoie le dossier de documents de groupe. Nous devrions l'utiliser chaque fois que nous voulons lire ou écrire dans un fichier partagé.

 func documentsFolder () -> URL? {
    return FileManager.default.containerURL (forSecurityApplicationGroupIdentifier: "group.com.app-o-mat.ListOMat")
}

Par exemple, lorsque nous sauvegardons les listes, nous utilisons ceci:

 func save (lists: Lists) {
    garde let docsDir = documentsFolder () else {
        fatalError ("aucun dossier docs")
    }

    let url = docsDir.appendingPathComponent (nomfichier, isDirectory: faux)

    // Encode les listes en JSON et enregistre en url
}

Le modèle d'extension Intents créait un fichier nommé IntentHandler.swift avec une classe nommée IntentHandler . Il l'a également configuré pour être le point d'entrée des intentions dans la plist de l'extension .

 Une capture d'écran de Xcode montrant comment IntentHandler est configuré comme point d'entrée
Le plist d'extension d'intention configure IntentHandler comme point d'entrée

Dans ce même plist vous verrez une section pour déclarer les intentions que nous soutenons. Nous allons commencer par celui qui permet de rechercher des listes, nommé INSearchForNotebookItemsIntent . Ajoutez-le au tableau sous IntentsSupported .

 Une capture d'écran dans Xcode montrant que le plist d'extension doit lister les intentions qu'il manipule
Ajouter le nom de l'intention au plist intentions ( Large preview )

Maintenant, allez dans IntentHandler.swift et remplacez son contenu par ce code:

 import Intents

class IntentHandler: INExtension {
    override func handler (pour intention: INIntent) -> Any? {
        changer l'intention {
        Le cas est INSearchForNotebookItemsIntent:
            return SearchItemsIntentHandler ()
        défaut:
            retour nul
        }
    }
}

La fonction gestionnaire est appelée pour obtenir un objet pour gérer une intention spécifique. Vous pouvez simplement implémenter tous les protocoles dans cette classe et renvoyer self mais nous allons mettre chaque intention dans sa propre classe pour mieux l'organiser.

Parce que nous avons l'intention d'avoir quelques classes différentes , donnons-leur une classe de base commune pour le code que nous devons partager entre eux:

 class ListOMatIntentsHandler: NSObject {
}

Le cadre des intentions nous oblige à hériter de NSObject . Nous allons remplir quelques méthodes plus tard.

Nous commençons notre implémentation de recherche avec ceci:

 class SearchItemsIntentHandler: ListOMatIntentsHandler,
                                                       INSearchForNotebookItemsIntentHandling {
}

Pour définir un gestionnaire d'intention, nous devons implémenter trois étapes de base Résoudre les paramètres
Assurez-vous que les paramètres requis sont donnés et désambiguïsez tout ce que vous ne comprenez pas complètement. 19659012] Confirmez
que la requête est faisable
Ceci est souvent optionnel, mais même si vous savez que chaque paramètre est bon, vous devrez peut-être avoir accès à une ressource externe ou avoir d'autres besoins.

  • la demande
    Faites ce qui est demandé
  • INSearchForNotebookItemsIntent la première intention que nous implémenterons, peut être utilisée comme recherche de tâche. Les types de demandes que nous pouvons traiter avec ceci sont: "Dans List-o-Mat, afficher la liste des épiceries" ou "Dans List-o-Mat, afficher la liste des magasins."

    A part: "List-o- Mat "est en fait un mauvais nom pour une application SiriKit parce que Siri a du mal avec les tirets dans les applications. Heureusement, SiriKit nous permet d'avoir d'autres noms et de fournir une prononciation. Dans l'application Info.plist ajoutez cette section:

     Une capture d'écran de Xcode montrant que l'appli peut ajouter des noms et des prononciations d'application
    Ajouter des guides de noms et de prononciation

    Ceci permet à l'utilisateur de dire "liste oh mat" et pour cela d'être compris comme un seul mot (sans tiret). Il ne semble pas idéal sur l'écran, mais sans lui, Siri pense parfois que "List" et "Mat" sont des mots séparés et deviennent très confus.

    Résoudre: Déterminer les paramètres

    , il existe plusieurs paramètres:

    1. le type d'élément (une tâche, une liste de tâches ou une note),
    2. le titre de l'élément,
    3. le contenu de l'élément,
    4. le statut d'achèvement ( que la tâche soit marquée ou non),
    5. l'emplacement auquel elle est associée,
    6. la date à laquelle elle est associée.

    Nous n'avons besoin que des deux premiers, nous aurons donc besoin d'écrire des fonctions de résolution pour leur. INSearchForNotebookItemsIntent a des méthodes que nous devons implémenter

    Comme nous nous intéressons uniquement à l'affichage des listes de tâches, nous le coderons en dur dans la résolution du type d'élément. Dans SearchItemsIntentHandler ajoutez ceci:

     func resolveItemType (pour intention: INSearchForNotebookItemsIntent,
                             avec achèvement: @escaping (INNotebookItemTypeResolutionResult) -> Void) {
    
        achèvement (.success (avec: .taskList))
    }
    

    Donc, peu importe ce que dit l'utilisateur, nous allons chercher des listes de tâches. Si nous voulions étendre notre support de recherche, nous laisserions Siri essayer de comprendre cela à partir de la phrase d'origine et ensuite utiliser simplement achèvement (.needsValue ()) si le type d'élément manquait. Alternativement, nous pourrions essayer de deviner à partir du titre en voyant ce qui lui correspond. Dans ce cas, nous aurions terminé avec succès quand Siri sait ce que c'est, et nous utiliserions l'achèvement (.notRequired ()) quand nous allons essayer plusieurs possibilités.

    La ​​résolution de titre est un peu plus difficile. Ce que nous voulons, c'est que Siri utilise une liste si elle en trouve une avec une correspondance exacte pour ce que vous avez dit. Si c'est incertain ou s'il y a plus d'une possibilité, alors nous voulons que Siri nous demande de l'aide pour le comprendre. Pour ce faire, SiriKit fournit un ensemble d'instructions de résolution qui nous permettent d'exprimer ce que nous voulons arriver ensuite.

    Donc, si vous dites "épicerie", alors Siri aurait une correspondance exacte. Mais si vous dites "Store", alors Siri présentera un menu de listes correspondantes.

    Nous allons commencer par cette fonction pour donner la structure de base:

     func resolveTitle (pour l'intention: INSearchForNotebookItemsIntent, avec l'achèvement: @escaping (INSpeakableStringResolutionResult) -> Annulation) {
        garde let title = intent.title else {
            achèvement (.needsValue ())
            revenir
        }
    
        let possibleLists = getPossibleLists (pour: title)
        completeResolveListName (avec: possibleLists, pour: title, avec: completion)
    }
    

    Nous allons implémenter getPossibleLists (pour:) et completeResolveListName (avec: pour: avec:) dans la classe de base ListOMatIntentsHandler

    getPossibleLists (for:) doit essayer de faire correspondre le titre que Siri nous a transmis avec les noms de liste réels.

     public func getPossibleLists (pour listName: INSpeakableString) -> [INSpeakableString] {
        var possibleLists = [INSpeakableString] ()
        pour l dans loadLists () {
            if l.name.lowercased () == listName.spokenPhrase.lowercased () {
                retour [INSpeakableString(spokenPhrase: l.name)]
            }
            si l.name.lowercased (). contient (listName.spokenPhrase.lowercased ()) || listName.spokenPhrase.lowercased () == "all" {
                possibleLists.append (INSpeakableString (spokenPhrase: l.name))
            }
        }
        return possibleListes
    }
    

    Nous parcourons toutes nos listes. Si nous obtenons une correspondance exacte, nous la renverrons, et sinon, nous retournerons un tableau de possibilités. Dans cette fonction, nous vérifions simplement si le mot indiqué par l'utilisateur est contenu dans un nom de liste (donc, une correspondance assez simple). Cela permet à "Grocery" de correspondre à "Epicerie". Un algorithme plus avancé peut essayer de faire correspondre des mots qui semblent identiques (par exemple, avec l'algorithme Soundex ),

    completeResolveListName ( avec: pour: avec:) est responsable de décider quoi faire avec cette liste de possibilités.

     public func completeResolveListName (avec possibleLists: [INSpeakableString]pour listName: INSpeakableString, avec completion: @escaping (INSpeakableStringResolutionResult) -> Annulation) {
        switch possibleLists.count {
        cas 0:
            achèvement (.unsupported ())
        cas 1:
            si possibleListes [0] .spokenPhrase.lowercased () == listName.spokenPhrase.lowercased () {
                achèvement (.success (avec: possibleLists [0]))
            } autre {
                achèvement (.confirmationRequired (avec: possibleLists [0]))
            }
        défaut:
            achèvement (.disambiguation (avec: possibleListes))
        }
    }
    

    Si nous obtenons une correspondance exacte, nous disons à Siri que nous avons réussi. Si nous avons une correspondance inexacte, nous disons à Siri de demander à l'utilisateur si nous l'avons deviné.

    Si nous avons plusieurs correspondances, alors nous utilisons l'achèvement (.disambiguation (avec: possibleLists)) Siri montre une liste et laisse l'utilisateur en choisir un.

    Maintenant que nous connaissons la requête, nous devons regarder le tout et nous assurer que nous pouvons le gérer.

    Confirmer: Vérifier toutes vos dépendances

    Dans ce cas, si nous avons résolu tous les paramètres, nous pouvons toujours gérer la requête. Des implémentations confirm () typiques peuvent vérifier la disponibilité de services externes ou vérifier les niveaux d'autorisation. Parce que confirm () est facultatif, nous pourrions simplement ne rien faire, et Siri supposerait que nous pourrait gérer n'importe quelle requête avec des paramètres résolus. Pour être explicite, nous pourrions utiliser ceci:

     func confirm (intention: INSearchForNotebookItemsIntent, achèvement: @escaping (INSearchForNotebookItemsIntentResponse) -> Void) {
        completion (INSearchForNotebookItemsIntentResponse (code: .success, userActivity: nil))
    }
    

    Cela signifie que nous pouvons gérer n'importe quoi.

    Handle: Do It

    La dernière étape consiste à gérer la requête

     func handle (intention: INSearchForNotebookItemsIntent, achèvement: @escaping (INSearchForNotebookItemsIntentResponse) -> Void) {
        garde
            let title = intention.title,
            let list = loadLists (). filter ({$ 0.name.lowercased () == title.spokenPhrase.lowercased ()}).
        autre {
            completion (INSearchForNotebookItemsIntentResponse (code: .failure, userActivity: nil))
            revenir
        }
    
        let response = INSearchForNotebookItemsIntentResponse (code: .success, userActivity: nil)
        response.tasks = list.items.map {
            return INTask (titre: INSpeakableString (speakPhrase: $ 0.name),
                          statut: $ 0.done? INTaskStatus.completed: INTaskStatus.notCompleted,
                          taskType: INTaskType.notCompletable,
                          spatialEventTrigger: nul,
                          temporalEventTrigger: nul,
                          createdDateComponents: nil,
                          modifiedDateComponents: nil,
                          identifiant: " (liste.nom)  t  ($ 0.nom)")
        }
        achèvement (réponse)
    }
    

    Tout d'abord, nous trouvons la liste basée sur le titre. À ce stade, resolveTitle s'est déjà assuré que nous obtiendrons une correspondance exacte. Mais s'il y a un problème, nous pouvons toujours retourner un échec.

    En cas d'échec, nous avons la possibilité de passer une activité utilisateur. Si votre application utilise Handoff et a un moyen de gérer ce type exact de requête, alors Siri pourrait essayer de se reporter à votre application pour y essayer la requête. Il ne le fera pas lorsque nous sommes dans un contexte de voix seulement (par exemple, vous avez commencé avec "Hey Siri"), et cela ne garantit pas qu'il le fera dans d'autres cas, alors ne comptez pas dessus.

    Ceci est maintenant prêt à tester. Choisissez l'extension d'intention dans la liste cible dans Xcode. Mais avant de l'exécuter, modifiez le schéma.

     Une capture d'écran de Xcode montrant comment modifier un schéma
    Modifiez le schéma de l'intention d'ajouter un exemple de phrase pour le débogage.

    fournir une requête directement:

     Une capture d'écran de Xcode montrant la boîte de dialogue du schéma d'édition
    Ajouter l'exemple de phrase à la section Exécuter du schéma. ( Grand aperçu )

    Notez bien que j'utilise "ListOMat" à cause du problème des tirets mentionné plus haut. Heureusement, il est prononcé de la même manière que le nom de mon application, donc ça ne devrait pas poser de problème.

    De retour dans l'application, j'ai fait une liste "Epicerie" et une liste "Magasin de matériel". Si je demande à Siri la liste "store", elle passera par le chemin de désambiguïsation, qui ressemble à ceci:

     Un GIF animé montrant Siri manipulant une requête pour afficher la liste Store
    Siri gère la requête en demandant clarification. ( Grand aperçu )

    Si vous dites "Épicerie", alors vous obtiendrez une correspondance exacte, qui ira directement aux résultats.

    Ajouter des éléments via Siri

    Maintenant que nous connaître les concepts de base de résoudre, confirmer et gérer, nous pouvons rapidement ajouter une intention d'ajouter un élément à une liste.

    Tout d'abord, ajoutez INAddTasksIntent au plist de l'extension:

     Une capture d'écran dans XCode montrant la nouvelle intention ajoutée au plist
    Ajouter le INAddTasksIntent à l'extension plist ( Grand aperçu )

    Ensuite, mettez à jour notre IntentHandler de handle function.

     override func gestionnaire (pour intention: INIntent) -> Any? {
        changer l'intention {
        Le cas est INSearchForNotebookItemsIntent:
            return SearchItemsIntentHandler ()
        Le cas est INAddTasksIntent:
            return AddItemsIntentHandler ()
        défaut:
            retour nul
        }
    }
    

    Add a stub for the new class:

    class AddItemsIntentHandler: ListOMatIntentsHandler, INAddTasksIntentHandling {
    }
    

    Adding an item needs a similar resolve for searching, except with a target task list instead of a title.

    func resolveTargetTaskList(for intent: INAddTasksIntent, with completion: @escaping (INTaskListResolutionResult) -> Void) {
    
        guard let title = intent.targetTaskList?.title else {
            completion(.needsValue())
            revenir
        }
    
        let possibleLists = getPossibleLists(for: title)
        completeResolveTaskList(with: possibleLists, for: title, with: completion)
    }
    

    completeResolveTaskList is just like completeResolveListNamebut with slightly different types (a task list instead of the title of a task list).

    public func completeResolveTaskList(with possibleLists: [INSpeakableString]for listName: INSpeakableString, with completion: @escaping (INTaskListResolutionResult) -> Void) {
    
        let taskLists = possibleLists.map {
            return INTaskList(title: $0, tasks: []groupName: nil, createdDateComponents: nil, modifiedDateComponents: nil, identifier: nil)
        }
    
        switch possibleLists.count {
        case 0:
            completion(.unsupported())
        case 1:
            if possibleLists[0].spokenPhrase.lowercased() == listName.spokenPhrase.lowercased() {
                completion(.success(with: taskLists[0]))
            } else {
                completion(.confirmationRequired(with: taskLists[0]))
            }
        default:
            completion(.disambiguation(with: taskLists))
        }
    }
    

    It has the same disambiguation logic and behaves in exactly the same way. Saying “Store” needs to be disambiguated, and saying “Grocery Store” would be an exact match.

    We’ll leave confirm unimplemented and accept the default. For handlewe need to add an item to the list and save it.

    func handle(intent: INAddTasksIntent, completion: @escaping (INAddTasksIntentResponse) -> Void) {
        var lists = loadLists()
        garde
            let taskList = intent.targetTaskList,
            let listIndex = lists.index(where: { $0.name.lowercased() == taskList.title.spokenPhrase.lowercased() }),
            let itemNames = intent.taskTitles, itemNames.count > 0
        else {
                completion(INAddTasksIntentResponse(code: .failure, userActivity: nil))
                revenir
        }
    
        // Get the list
        var list = lists[listIndex]
    
        // Add the items
        var addedTasks = [INTask]()
        for item in itemNames {
            list.addItem(name: item.spokenPhrase, at: list.items.count)
            addedTasks.append(INTask(title: item, status: .notCompleted, taskType: .notCompletable, spatialEventTrigger: nil, temporalEventTrigger: nil, createdDateComponents: nil, modifiedDateComponents: nil, identifier: nil))
        }
    
        // Save the new list
        lists[listIndex] = list
        save(lists: lists)
    
        // Respond with the added items
        let response = INAddTasksIntentResponse(code: .success, userActivity: nil)
        response.addedTasks = addedTasks
        completion(response)
    }
    

    We get a list of items and a target list. We look up the list and add the items. We also need to prepare a response for Siri to show with the added items and send it to the completion function.

    This function can handle a phrase like, “In ListOMat, add apples to the grocery list.” It can also handle a list of items like, “rice, onions and olives.”

    A screenshot of the simulator showing Siri adding items to the grocery store list
    Siri adds a few items to the grocery store list

    Almost Done, Just A Few More Settings

    All of this will work in your simulator or local device, but if you want to submit this, you’ll need to add a NSSiriUsageDescription key to your app’s plistwith a string that describes what you are using Siri for. Something like “Your requests about lists will be sent to Siri” is fine.

    You should also add a call to:

    INPreferences.requestSiriAuthorization { (status) in }
    

    Put this in your main view controller’s viewDidLoad to ask the user for Siri access. This will show the message you configured above and also let the user know that they could be using Siri for this app.

    A screenshot of the dialog that a device pops up when you ask for Siri permission
    The device will ask for permission if you try to use Siri in the app.

    Finally, you’ll need to tell Siri what to tell the user if the user asks what your app can do, by providing some sample phrases:

    1. Create a plist file in your app (not the extension), named AppIntentVocabulary.plist.
    2. Fill out the intents and phrases that you support.
    A screenshot of the AppIntentVocabulary.plist showing sample phrases
    Add an AppIntentVocabulary.plist to list the sample phrases that will invoke the intent you handle. (Large preview)

    There is no way to really know all of the phrases that Siri will use for an intent, but Apple does provide a few samples for each intent in its documentation. The sample phrases for task-list searching show us that Siri can understand “Show me all my notes on ,” but I found other phrases by trial and error (for example, Siri understands what “lists” are too, not just notes).

    Summary

    As you can see, adding Siri support to an app has a lot of steps, with a lot of configuration. But the code needed to handle the requests was fairly simple.

    There are a lot of steps, but each one is small, and you might be familiar with a few of them if you have used extensions before.

    Here is what you’ll need to prepare for a new extension on Apple’s developer website:

    1. Make an app ID for an Intents extension.
    2. Make an app group if you don’t already have one.
    3. Use the app group in the app ID for the app and extension.
    4. Add Siri support to the app’s ID.
    5. Regenerate the profiles and download them.

    And here are the steps in Xcode for creating Siri’s Intents extension:

    1. Add an Intents extension using the Xcode template.
    2. Update the entitlements of the app and extension to match the profiles (groups and Siri support).
    3. Add your intents to the extension’s plist.

    And you’ll need to add code to do the following things:

    1. Use the app group sandbox to communicate between the app and extension.
    2. Add classes to support each intent with resolve, confirm and handle functions.
    3. Update the generated IntentHandler to use those classes.
    4. Ask for Siri access somewhere in your app.

    Finally, there are some Siri-specific configuration settings:

    1. Add the Siri support security string to your app’s plist.
    2. Add sample phrases to an AppIntentVocabulary.plist file in your app.
    3. Run the intent target to test; edit the scheme to provide the phrase.

    OK, that is a lot, but if your app fits one of Siri’s domains, then users will expect that they can interact with it via voice. And because the competition for voice assistants is so good, we can only expect that WWDC 2018 will bring a bunch more domains and, hopefully, much better Siri.

    Further Reading

    Smashing Editorial(da, ra, al, il)




    Source link