Intențiile SiriKit se potrivesc aplicației dvs.? Dacă da, iată cum să le folosiți
Publicat: 2022-03-10De la iOS 5, Siri a ajutat utilizatorii de iPhone să trimită mesaje, să stabilească mementouri și să caute restaurante cu aplicațiile Apple. Începând cu iOS 10, am putut folosi Siri și în unele dintre propriile noastre aplicații.
Pentru a utiliza această funcționalitate, aplicația dvs. trebuie să se încadreze în „domeniile și intențiile” Siri predefinite de la Apple. În acest articol, vom afla despre ce sunt acestea și vom vedea dacă aplicațiile noastre le pot folosi. Vom lua o aplicație simplă care este un manager de liste de activități și vom învăța cum să adăugați suport Siri. Vom parcurge, de asemenea, ghidurile site-ului web pentru dezvoltatori Apple privind configurarea și codul Swift pentru un nou tip de extensie care a fost introdus cu SiriKit: extensia Intents .
Când ajungeți la partea de codificare a acestui articol, veți avea nevoie de Xcode (cel puțin versiunea 9.x) și ar fi bine dacă sunteți familiarizat cu dezvoltarea iOS în Swift, deoarece vom adăuga Siri la un mic lucru. aplicația. Vom parcurge pașii de configurare a unei extensii pe site-ul web al dezvoltatorului Apple și de adăugare a codului de extensie Siri la aplicație.
„Hei Siri, de ce am nevoie de tine?”
Uneori îmi folosesc telefonul în timp ce sunt pe canapea, cu ambele mâini libere și pot acorda ecranului toată atenția mea. Poate îi voi trimite un mesaj surorii mele pentru a planifica ziua de naștere a mamei noastre sau îi voi răspunde la o întrebare în Trello. Pot vedea aplicația. Pot atinge ecranul. Pot tasta.
Dar s-ar putea să mă plimb prin orașul meu, ascultând un podcast, când apare un mesaj pe ceas. Telefonul meu este în buzunar și nu pot răspunde ușor în timp ce merg pe jos.
Cu Siri, pot să țin apăsat butonul de control al căștilor și să spun: „Trimite-i un mesaj surorii mele că voi fi acolo până la ora două”. Siri este grozav atunci când ești în mișcare și nu poți acorda o atenție deplină telefonului tău sau când interacțiunea este minoră, dar necesită mai multe atingeri și o grămadă de tastare.
Acest lucru este în regulă dacă vreau să folosesc aplicațiile Apple pentru aceste interacțiuni. Dar unele categorii de aplicații, cum ar fi mesageria, au alternative foarte populare. Alte activități, cum ar fi rezervarea unei călătorii sau rezervarea unei mese într-un restaurant, nici măcar nu sunt posibile cu aplicațiile încorporate Apple, dar sunt perfecte pentru Siri.
Abordarea Apple a asistenților vocali
Pentru a activa Siri în aplicațiile terță parte, Apple a trebuit să decidă un mecanism care să preia sunetul de la vocea utilizatorului și să îl ducă într-un fel la aplicație într-un mod în care să poată îndeplini cererea. Pentru a face acest lucru posibil, Apple cere utilizatorului să menționeze numele aplicației în cerere, dar avea mai multe opțiuni de a face cu restul solicitării.
- Ar fi putut trimite un fișier de sunet la aplicație.
Avantajul acestei abordări este că aplicația ar putea încerca să gestioneze literalmente orice solicitare pe care utilizatorul o poate avea. Amazon sau Google ar fi putut să-i fi plăcut această abordare, deoarece au deja servicii sofisticate de recunoaștere a vocii. Dar majoritatea aplicațiilor nu ar fi capabile să se ocupe de acest lucru foarte ușor. - Ar fi putut transforma discursul în text și ar fi trimis asta.
Deoarece multe aplicații nu au implementări sofisticate în limbaj natural, utilizatorul va trebui de obicei să respecte expresii foarte specifice, iar suportul care nu este în limba engleză ar fi la latitudinea dezvoltatorului aplicației să le implementeze. - V-ar fi putut cere să furnizați o listă de fraze pe care le înțelegeți.
Acest mecanism este mai aproape de ceea ce face Amazon cu Alexa (în cadrul său de „competențe”) și permite mult mai multe utilizări ale Alexa decât poate gestiona SiriKit în prezent. Într-o abilitate Alexa, oferiți fraze cu variabile de substituție pe care Alexa le va completa pentru dvs. De exemplu, „Alexa, reamintește-mi la$TIME$
la$REMINDER$
” — Alexa va rula această frază în raport cu ceea ce a spus utilizatorul și îți va spune valorile pentruTIME
șiREMINDER
. Ca și în cazul mecanismului anterior, dezvoltatorul trebuie să facă toată traducerea și nu există multă flexibilitate dacă utilizatorul spune ceva ușor diferit. - Ar putea defini o listă de solicitări cu parametri și ar putea trimite aplicației o solicitare structurată.
Acesta este de fapt ceea ce face Apple, iar avantajul este că poate accepta o varietate de limbi și face toată munca pentru a încerca să înțeleagă toate modurile în care un utilizator ar putea formula o solicitare. Marele dezavantaj este că puteți implementa handlere numai pentru cererile definite de Apple. Acest lucru este grozav dacă aveți, de exemplu, o aplicație de mesagerie, dar dacă aveți un serviciu de streaming de muzică sau un player podcast, nu aveți cum să utilizați SiriKit în acest moment.
În mod similar, există trei moduri prin care aplicațiile pot răspunde utilizatorului: cu sunet, cu text care este convertit sau prin exprimarea tipului de lucru pe care doriți să-l spuneți și lăsând sistemul să descopere modul exact de a-l exprima. Ultima soluție (care este ceea ce face Apple) pune povara traducerii pe Apple, dar vă oferă modalități limitate de a vă folosi propriile cuvinte pentru a descrie lucrurile.
Tipurile de solicitări pe care le puteți gestiona sunt definite în domeniile și intențiile SiriKit. O intenție este un tip de solicitare pe care un utilizator o poate face, cum ar fi trimiterea unui mesaj text unei persoane de contact sau găsirea unei fotografii. Fiecare intenție are o listă de parametri - de exemplu, trimiterea de mesaje text necesită un contact și un mesaj.
Un domeniu este doar un grup de intenții înrudite. Citirea unui text și trimiterea unui text sunt ambele în domeniul mesageriei. Rezervarea unei curse și obținerea unei locații sunt în domeniul rezervări curse. Există domenii pentru efectuarea de apeluri VoIP, începerea antrenamentelor, căutarea de fotografii și alte câteva lucruri. Documentația SiriKit conține o listă completă de domenii și intențiile acestora.
O critică comună la adresa Siri este că pare incapabil să gestioneze solicitările la fel de bine ca Google și Alexa și că ecosistemul de voce de la terți activat de concurenții Apple este mai bogat.
Sunt de acord cu acele critici. Dacă aplicația dvs. nu se încadrează în intențiile actuale, atunci nu puteți utiliza SiriKit și nu puteți face nimic. Chiar dacă aplicația dvs. se potrivește, nu puteți controla toate cuvintele pe care Siri le spune sau le înțelege; deci, dacă ai un anumit mod de a vorbi despre lucruri în aplicația ta, nu-l poți învăța întotdeauna pe Siri.
Speranța dezvoltatorilor iOS este atât că Apple își va extinde foarte mult lista de intenții, cât și că procesarea limbajului său natural devine mult mai bună. Dacă face asta, atunci vom avea un asistent vocal care funcționează fără ca dezvoltatorii să fie nevoiți să traducă sau să înțeleagă toate modurile de a spune același lucru. Iar implementarea suportului pentru cererile structurate este de fapt destul de simplu de făcut - mult mai ușor decât construirea unui parser în limbaj natural.
Un alt mare avantaj al cadrului intents este că nu se limitează la Siri și la solicitările vocale. Chiar și acum, aplicația Maps poate genera o solicitare bazată pe intenții a aplicației dvs. (de exemplu, o rezervare la restaurant). Face acest lucru în mod programatic (nu din voce sau limbaj natural). Dacă Apple ar permite aplicațiilor să descopere intențiile expuse reciproce, am avea o modalitate mult mai bună pentru ca aplicațiile să lucreze împreună (spre deosebire de adresele URL în stil x-callback).
În cele din urmă, deoarece o intenție este o solicitare structurată cu parametri, există o modalitate simplă prin care o aplicație să exprime faptul că parametrii lipsesc sau că are nevoie de ajutor pentru a distinge unele opțiuni. Apoi, Siri poate pune întrebări ulterioare pentru a rezolva parametrii fără ca aplicația să fie nevoie să conducă conversația.
Domeniul Ride-Booking
Pentru a înțelege domeniile și intențiile, să ne uităm la domeniul de rezervare a curselor. Acesta este domeniul pe care l-ați folosi pentru a-i cere lui Siri să vă ia o mașină Lyft.
Apple definește cum să cereți o călătorie și cum să obțineți informații despre aceasta, dar de fapt nu există nicio aplicație Apple încorporată care să poată face față acestei cereri. Acesta este unul dintre puținele domenii în care este necesară o aplicație activată pentru SiriKit.
Puteți invoca una dintre intenții prin voce sau direct din Maps. Unele dintre intențiile pentru acest domeniu sunt:
- Solicitați o plimbare
Folosește-l pentru a rezerva o călătorie. Va trebui să furnizați o locație de preluare și predare, iar aplicația ar putea avea nevoie, de asemenea, să cunoască dimensiunea grupului dvs. și ce fel de călătorie doriți. Un exemplu de expresie ar putea fi „Rezervați-mi o călătorie cu <appname>”. - Obțineți starea călătoriei
Utilizați această intenție pentru a afla dacă solicitarea dvs. a fost primită și pentru a obține informații despre vehicul și șofer, inclusiv locația acestora. Aplicația Maps folosește această intenție pentru a afișa o imagine actualizată a mașinii pe măsură ce se apropie de tine. - Anulați o cursă
Folosiți-l pentru a anula o cursă pe care ați rezervat-o.
Pentru oricare dintre aceste intenții, Siri ar putea avea nevoie să afle mai multe informații. După cum veți vedea când implementăm un handler de intenții, extensia dvs. Intents îi poate spune lui Siri că lipsește un parametru necesar, iar Siri va solicita utilizatorului să îl introducă.
Faptul că intențiile pot fi invocate programatic de Maps arată cum intențiile ar putea permite comunicarea între aplicații în viitor.
Notă : Puteți obține o listă completă de domenii și intențiile acestora pe site-ul web pentru dezvoltatori Apple. Există, de asemenea, un exemplu de aplicație Apple cu multe domenii și intenții implementate, inclusiv rezervarea curselor.
Adăugarea suportului pentru domeniile de liste și note la aplicația dvs
OK, acum că înțelegem elementele de bază ale SiriKit, să vedem cum ați proceda pentru a adăuga suport pentru Siri într-o aplicație care implică multă configurare și o clasă pentru fiecare intenție pe care doriți să o gestionați.
Restul acestui articol constă în pașii detaliați pentru a adăuga suport Siri la o aplicație. Există cinci lucruri la nivel înalt pe care trebuie să le faci:
- Pregătiți-vă să adăugați o nouă extensie la aplicație prin crearea de profiluri de furnizare cu noi drepturi pentru aceasta pe site-ul web al dezvoltatorului Apple.
- Configurați aplicația (prin
plist
) pentru a utiliza drepturile. - Utilizați șablonul Xcode pentru a începe cu un cod exemplu.
- Adăugați codul pentru a vă sprijini intenția Siri.
- Configurați vocabularul lui Siri prin
plist
s.
Nu vă faceți griji: vom parcurge fiecare dintre acestea, explicând extensiile și drepturile pe parcurs.
Pentru a se concentra doar asupra părților Siri, am pregătit un manager simplu de liste de activități, List-o-Mat.
Puteți găsi sursa completă a eșantionului, List-o-Mat, pe GitHub.
Pentru ao crea, tot ce am făcut a fost să încep cu șablonul de aplicație Xcode Master-Detail și să transform ambele ecrane într-un UITableView
. Am adăugat o modalitate de a adăuga și de a șterge liste și articole și o modalitate de a bifa elementele ca fiind finalizate. Toată navigarea este generată de șablon.
Pentru a stoca datele, am folosit protocolul Codable
, (introdus la WWDC 2017), care transformă structurile în JSON și le salvează într-un fișier text în folderul documents
.
Am păstrat în mod deliberat codul foarte simplu. Dacă aveți vreo experiență cu Swift și realizarea de controlere de vizualizare, atunci nu ar trebui să aveți nicio problemă cu el.
Acum putem parcurge pașii de adăugare a suportului SiriKit. Pașii de nivel înalt ar fi aceiași pentru orice aplicație și pentru orice domeniu și intenții pe care intenționați să le implementați. Vom avea de-a face în mare parte cu site-ul web al dezvoltatorului Apple, cu editarea plist
-urilor și să scriem puțin Swift.
Pentru List-o-Mat, ne vom concentra pe domeniul listelor și notelor, care se aplică pe scară largă la lucruri precum aplicațiile de luare de note și listele de activități.
În domeniul liste și note, avem următoarele intenții care ar avea sens pentru aplicația noastră.
- Obțineți o listă de sarcini.
- Adăugați o nouă sarcină la o listă.
Deoarece interacțiunile cu Siri au loc de fapt în afara aplicației dvs. (poate chiar și atunci când aplicația dvs. nu rulează), iOS folosește o extensie pentru a implementa acest lucru.
Extensia Intențiilor
Dacă nu ați lucrat cu extensii, va trebui să știți trei lucruri principale:
- O extensie este un proces separat. Este livrat în pachetul aplicației dvs., dar rulează complet pe cont propriu, cu propriul său sandbox.
- Aplicația și extensia dvs. pot comunica între ele fiind în același grup de aplicații. Cea mai ușoară cale este prin folderele partajate ale grupului (deci, ei pot citi și scrie în aceleași fișiere dacă le puneți acolo).
- Extensiile necesită propriile ID-uri de aplicație, profiluri și drepturi.
Pentru a adăuga o extensie la aplicația dvs., începeți prin a vă conecta la contul de dezvoltator și accesați secțiunea „Certificate, identificatori și profiluri”.
Actualizarea datelor contului dvs. de aplicație pentru dezvoltatori Apple
În contul nostru de dezvoltator Apple, primul lucru pe care trebuie să-l facem este să creăm un grup de aplicații. Accesați secțiunea „Grupuri de aplicații” sub „Identificatori” și adăugați unul.
Trebuie să înceapă cu group
, urmat de identificatorul dvs. obișnuit bazat pe domeniu invers. Deoarece are un prefix, puteți utiliza identificatorul aplicației pentru restul.
Apoi, trebuie să actualizăm ID-ul aplicației noastre pentru a folosi acest grup și pentru a activa Siri:
- Accesați secțiunea „ID-urile aplicației” și faceți clic pe ID-ul aplicației dvs.;
- Faceți clic pe butonul „Editați”;
- Activați grupurile de aplicații (dacă nu sunt activate pentru altă extensie).
- Apoi configurați grupul de aplicații făcând clic pe butonul „Editați”. Alegeți grupul de aplicații dinainte.
- Activați SiriKit.
- Faceți clic pe „Terminat” pentru a-l salva.
Acum, trebuie să creăm un nou ID de aplicație pentru extensia noastră:
- În aceeași secțiune „ID-uri de aplicație”, adăugați un nou ID de aplicație. Acesta va fi identificatorul aplicației dvs., cu un sufix. Nu folosiți doar
Intents
ca sufix, deoarece acest nume va deveni numele modulului dvs. în Swift și ar intra în conflict cuIntents
reale. - Activați acest ID de aplicație și pentru grupurile de aplicații (și configurați grupul așa cum am făcut înainte).
Acum, creați un profil de furnizare de dezvoltare pentru extensia Intents și regenerați profilul de furnizare al aplicației dvs. Descărcați și instalați-le așa cum ați proceda în mod normal.
Acum că profilele noastre sunt instalate, trebuie să mergem la Xcode și să actualizăm drepturile aplicației.
Actualizarea drepturilor aplicației dvs. în Xcode
Înapoi în Xcode, alegeți numele proiectului în navigatorul de proiect. Apoi, alegeți ținta principală a aplicației și accesați fila „Capacități”. Acolo, veți vedea un comutator pentru a activa suportul Siri.
Mai jos în listă, puteți activa grupurile de aplicații și le puteți configura.
Dacă l-ați configurat corect, veți vedea acest lucru în fișierul .entitlements
al aplicației dvs.:
Acum, suntem în sfârșit gata să adăugăm ținta extensiei Intents la proiectul nostru.
Adăugarea extensiei Intents
În sfârșit suntem gata să adăugăm extensia. În Xcode, alegeți „Fișier” → „Țintă nouă”. Această foaie va apărea:
Alegeți „Extensie intenții” și faceți clic pe butonul „Următorul”. Completați următorul ecran:
Numele produsului trebuie să se potrivească cu orice ați creat sufixul în ID-ul aplicației intents de pe site-ul web al dezvoltatorului Apple.
Alegem să nu adăugăm o extensie intents UI. Acest lucru nu este tratat în acest articol, dar îl puteți adăuga mai târziu dacă aveți nevoie de unul. Practic, este o modalitate de a vă pune propriul stil de branding și de afișare în rezultatele vizuale ale lui Siri.
Când ați terminat, Xcode va crea o clasă de tratare a intențiilor pe care o putem folosi ca parte de pornire pentru implementarea noastră Siri.
Managerul de intenții: rezolvați, confirmați și gestionați
Xcode a generat o nouă țintă care are un punct de plecare pentru noi.
Primul lucru pe care trebuie să-l faceți este să configurați această nouă țintă să fie în același grup de aplicații ca și aplicația. Ca și înainte, accesați fila „Capacități” a țintei și activați grupurile de aplicații și configurați-o cu numele grupului dvs. Rețineți, aplicațiile din același grup au un sandbox pe care îl pot folosi pentru a partaja fișiere între ele. Avem nevoie de acest lucru pentru ca solicitările Siri să ajungă la aplicația noastră.
List-o-Mat are o funcție care returnează folderul de documente de grup. Ar trebui să-l folosim ori de câte ori dorim să citim sau să scriem într-un fișier partajat.
func documentsFolder() -> URL? { return FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.app-o-mat.ListOMat") }
De exemplu, când salvăm listele, folosim asta:
func save(lists: Lists) { guard let docsDir = documentsFolder() else { fatalError("no docs dir") } let url = docsDir.appendingPathComponent(fileName, isDirectory: false) // Encode lists as JSON and save to url }
Șablonul de extensie Intents a creat un fișier numit IntentHandler.swift
, cu o clasă numită IntentHandler
. De asemenea, l-a configurat să fie punctul de intrare al intențiilor în plist
-ul extensiei.
În același plist
, veți vedea o secțiune pentru a declara intențiile pe care le susținem. Vom începe cu cea care permite căutarea listelor, care se numește INSearchForNotebookItemsIntent
. Adăugați-l în matrice sub IntentsSupported
.
Acum, accesați IntentHandler.swift
și înlocuiți conținutul acestuia cu acest cod:
import Intents class IntentHandler: INExtension { override func handler(for intent: INIntent) -> Any? { switch intent { case is INSearchForNotebookItemsIntent: return SearchItemsIntentHandler() default: return nil } } }
Funcția handler
este apelată pentru a face ca un obiect să gestioneze o intenție specifică. Puteți doar să implementați toate protocoalele din această clasă și să returnați self
, dar vom pune fiecare intenție în propria sa clasă pentru a o menține mai bine organizată.
Deoarece intenționăm să avem câteva clase diferite, să le oferim o clasă de bază comună pentru codul pe care trebuie să îl împărtășim între ele:
class ListOMatIntentsHandler: NSObject { }
Cadrul intents ne cere să moștenim de la NSObject
. Vom completa câteva metode mai târziu.
Începem implementarea noastră de căutare cu asta:
class SearchItemsIntentHandler: ListOMatIntentsHandler, INSearchForNotebookItemsIntentHandling { }
Pentru a seta un handler de intenție, trebuie să implementăm trei pași de bază
- Rezolvați parametrii.
Asigurați-vă că sunt dați parametrii necesari și eliminați ambiguitatea pe cei pe care nu îi înțelegeți pe deplin. - Confirmați că cererea este realizabilă.
Acest lucru este adesea opțional, dar chiar dacă știți că fiecare parametru este bun, este posibil să aveți nevoie de acces la o resursă externă sau să aveți alte cerințe. - Gestionați cererea.
Faceți lucrul care vi se cere.
INSearchForNotebookItemsIntent
, prima intenție pe care o vom implementa, poate fi folosită ca căutare de sarcini. Tipurile de solicitări pe care le putem trata cu aceasta sunt „În List-o-Mat, arată lista de magazine” sau „În List-o-Mat, arată lista de magazine”.
Deoparte: „List-o-Mat” este de fapt un nume prost pentru o aplicație SiriKit, deoarece Siri are dificultăți cu cratimele în aplicații. Din fericire, SiriKit ne permite să avem nume alternative și să oferim pronunție. În Info.plist
a aplicației, adăugați această secțiune:
Acest lucru permite utilizatorului să spună „list oh mat” și ca acesta să fie înțeles ca un singur cuvânt (fără cratime). Nu arată ideal pe ecran, dar fără el, Siri crede uneori că „Lista” și „Mat” sunt cuvinte separate și devine foarte confuză.
Rezolvare: stabilirea parametrilor
Pentru căutarea articolelor de blocnotes, există mai mulți parametri:
- tipul de element (o sarcină, o listă de sarcini sau o notă),
- titlul articolului,
- conținutul articolului,
- starea de finalizare (dacă sarcina este marcată ca finalizată sau nu),
- locația cu care este asociat,
- data cu care este asociat.
Avem nevoie doar de primele două, așa că va trebui să scriem funcții de rezolvare pentru ele. INSearchForNotebookItemsIntent
are metode pe care să le implementăm.
Deoarece ne pasă doar de afișarea listelor de sarcini, vom codifica asta în soluționarea tipului de articol. În SearchItemsIntentHandler
, adăugați acest lucru:
func resolveItemType(for intent: INSearchForNotebookItemsIntent, with completion: @escaping (INNotebookItemTypeResolutionResult) -> Void) { completion(.success(with: .taskList)) }
Deci, indiferent ce spune utilizatorul, vom căuta liste de sarcini. Dacă dorim să ne extindem suportul de căutare, l-am lăsa pe Siri să încerce să descopere acest lucru din fraza inițială și apoi să folosească completion(.needsValue())
dacă tipul de element lipsea. Alternativ, am putea încerca să ghicim din titlu, văzând ce se potrivește. În acest caz, am finaliza cu succes atunci când Siri știe ce este, și am folosi completion(.notRequired())
când vom încerca mai multe posibilități.
Rezoluția titlului este puțin mai complicată. Ceea ce dorim este ca Siri să folosească o listă dacă găsește una cu o potrivire exactă pentru ceea ce ai spus. Dacă nu este sigur sau dacă există mai multe posibilități, atunci dorim ca Siri să ne ceară ajutor pentru a o descoperi. Pentru a face acest lucru, SiriKit oferă un set de enumări de rezoluție care ne permit să exprimăm ceea ce dorim să se întâmple în continuare.
Deci, dacă spui „Magazin alimentar”, atunci Siri ar avea o potrivire exactă. Dar dacă spui „Magazin”, atunci Siri va prezenta un meniu cu liste de potrivire.
Vom începe cu această funcție pentru a oferi structura de bază:
func resolveTitle(for intent: INSearchForNotebookItemsIntent, with completion: @escaping (INSpeakableStringResolutionResult) -> Void) { guard let title = intent.title else { completion(.needsValue()) return } let possibleLists = getPossibleLists(for: title) completeResolveListName(with: possibleLists, for: title, with: completion) }
Vom implementa getPossibleLists(for:)
și completeResolveListName(with:for:with:)
în clasa de bază ListOMatIntentsHandler
.
getPossibleLists(for:)
trebuie să încerce să potrivească neclar titlul pe care ni-l transmite Siri cu numele listei reale.
public func getPossibleLists(for listName: INSpeakableString) -> [INSpeakableString] { var possibleLists = [INSpeakableString]() for l in loadLists() { if l.name.lowercased() == listName.spokenPhrase.lowercased() { return [INSpeakableString(spokenPhrase: l.name)] } if l.name.lowercased().contains(listName.spokenPhrase.lowercased()) || listName.spokenPhrase.lowercased() == "all" { possibleLists.append(INSpeakableString(spokenPhrase: l.name)) } } return possibleLists }
Parcurgem toate listele noastre. Dacă obținem o potrivire exactă, o vom returna, iar dacă nu, vom returna o serie de posibilități. În această funcție, pur și simplu verificăm pentru a vedea dacă cuvântul spus de utilizator este conținut într-un nume de listă (deci, o potrivire destul de simplă). Acest lucru permite „Băcănie” să se potrivească cu „Magazin alimentar”. Un algoritm mai avansat ar putea încerca să se potrivească pe baza unor cuvinte care sună la fel (de exemplu, cu algoritmul Soundex),
completeResolveListName(with:for:with:)
este responsabil pentru a decide ce să facă cu această listă de posibilități.
public func completeResolveListName(with possibleLists: [INSpeakableString], for listName: INSpeakableString, with completion: @escaping (INSpeakableStringResolutionResult) -> Void) { switch possibleLists.count { case 0: completion(.unsupported()) case 1: if possibleLists[0].spokenPhrase.lowercased() == listName.spokenPhrase.lowercased() { completion(.success(with: possibleLists[0])) } else { completion(.confirmationRequired(with: possibleLists[0])) } default: completion(.disambiguation(with: possibleLists)) } }
Dacă obținem o potrivire exactă, îi spunem lui Siri că am reușit. Dacă avem o potrivire inexactă, îi spunem lui Siri să întrebe utilizatorul dacă am ghicit corect.
Dacă avem mai multe potriviri, atunci folosim completion(.disambiguation(with: possibleLists))
pentru a-i spune lui Siri să arate o listă și să lăsăm utilizatorului să aleagă una.
Acum că știm care este cererea, trebuie să ne uităm la întregul lucru și să ne asigurăm că o putem gestiona.
Confirmați: verificați toate dependențele dvs
În acest caz, dacă am rezolvat toți parametrii, putem oricând să ne ocupăm de cerere. Implementările tipice confirm()
pot verifica disponibilitatea serviciilor externe sau pot verifica nivelurile de autorizare.
Deoarece confirm()
este opțional, nu putem face nimic, iar Siri ar presupune că putem gestiona orice cerere cu parametrii rezolvați. Pentru a fi explicit, am putea folosi asta:
func confirm(intent: INSearchForNotebookItemsIntent, completion: @escaping (INSearchForNotebookItemsIntentResponse) -> Void) { completion(INSearchForNotebookItemsIntentResponse(code: .success, userActivity: nil)) }
Asta înseamnă că ne putem descurca cu orice.
Mâner: Fă-o
Ultimul pas este gestionarea cererii.
func handle(intent: INSearchForNotebookItemsIntent, completion: @escaping (INSearchForNotebookItemsIntentResponse) -> Void) { guard let title = intent.title, let list = loadLists().filter({ $0.name.lowercased() == title.spokenPhrase.lowercased()}).first else { completion(INSearchForNotebookItemsIntentResponse(code: .failure, userActivity: nil)) return } let response = INSearchForNotebookItemsIntentResponse(code: .success, userActivity: nil) response.tasks = list.items.map { return INTask(title: INSpeakableString(spokenPhrase: $0.name), status: $0.done ? INTaskStatus.completed : INTaskStatus.notCompleted, taskType: INTaskType.notCompletable, spatialEventTrigger: nil, temporalEventTrigger: nil, createdDateComponents: nil, modifiedDateComponents: nil, identifier: "\(list.name)\t\($0.name)") } completion(response) }
În primul rând, găsim lista pe baza titlului. În acest moment, resolveTitle
s-a asigurat deja că vom obține o potrivire exactă. Dar dacă există o problemă, putem totuși returna un eșec.
Când avem un eșec, avem opțiunea de a trece o activitate de utilizator. Dacă aplicația dvs. folosește Handoff și are o modalitate de a gestiona exact acest tip de solicitare, atunci Siri ar putea încerca să amâne aplicația dvs. pentru a încerca solicitarea acolo. Nu va face acest lucru atunci când ne aflăm într-un context doar cu voce (de exemplu, ați început cu „Hei Siri”) și nu garantează că o va face în alte cazuri, așa că nu vă bazați pe asta.
Acesta este acum gata de testat. Alegeți extensia de intenție din lista țintă din Xcode. Dar înainte de a-l rula, editați schema.
Aceasta aduce o modalitate de a furniza o interogare direct:
Observați, folosesc „ListOMat” din cauza problemei cu cratimele menționate mai sus. Din fericire, se pronunță la fel ca numele aplicației mele, așa că nu ar trebui să fie o problemă.
Înapoi în aplicație, am făcut o listă „Magazin alimentar” și o listă „Magazin de hardware”. Dacă îi cer lui Siri lista de „magazin”, aceasta va trece prin calea de dezambiguizare, care arată astfel:
Dacă spuneți „Magazin alimentar”, atunci veți obține o potrivire exactă, care merge direct la rezultate.
Adăugarea de articole prin Siri
Acum că cunoaștem conceptele de bază de rezolvare, confirmare și tratare, putem adăuga rapid o intenție de a adăuga un element la o listă.
Mai întâi, adăugați INAddTasksIntent
la plist-ul extensiei:
Apoi, actualizați funcția de handle
a IntentHandler
-ului.
override func handler(for intent: INIntent) -> Any? { switch intent { case is INSearchForNotebookItemsIntent: return SearchItemsIntentHandler() case is INAddTasksIntent: return AddItemsIntentHandler() default: return nil } }
Adăugați un stub pentru noua clasă:
class AddItemsIntentHandler: ListOMatIntentsHandler, INAddTasksIntentHandling { }
Adăugarea unui articol necesită o resolve
pentru căutare, cu excepția unei liste de sarcini țintă în loc de un titlu.
func resolveTargetTaskList(for intent: INAddTasksIntent, with completion: @escaping (INTaskListResolutionResult) -> Void) { guard let title = intent.targetTaskList?.title else { completion(.needsValue()) return } let possibleLists = getPossibleLists(for: title) completeResolveTaskList(with: possibleLists, for: title, with: completion) }
completeResolveTaskList
este la fel ca completeResolveListName
, dar cu tipuri ușor diferite (o listă de activități în loc de titlul unei liste de activități).
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)) } }
Are aceeași logică de dezambiguizare și se comportă exact în același mod. Spunerea „Magazin” trebuie să fie dezambiguată, iar rostirea „Magazin alimentar” ar fi o potrivire exactă.
Vom lăsa confirm
neimplementată și vom accepta implicit. Pentru handle
, trebuie să adăugăm un element la listă și să-l salvăm.
func handle(intent: INAddTasksIntent, completion: @escaping (INAddTasksIntentResponse) -> Void) { var lists = loadLists() guard 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)) return } // 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) }
Primim o listă de articole și o listă țintă. Căutăm lista și adăugăm articolele. De asemenea, trebuie să pregătim un răspuns pentru ca Siri să-l arate cu elementele adăugate și să-l trimitem la funcția de finalizare.
Această funcție poate gestiona o expresie precum „În ListOMat, adăugați mere la lista de cumpărături”. De asemenea, poate gestiona o listă de articole precum „orez, ceapă și măsline”.
Aproape gata, doar câteva setări suplimentare
Toate acestea vor funcționa în simulatorul sau dispozitivul dvs. local, dar dacă doriți să trimiteți acest lucru, va trebui să adăugați o cheie NSSiriUsageDescription
la plist
-ul aplicației dvs., cu un șir care descrie pentru ce utilizați Siri. Ceva de genul „Solicitările tale despre liste vor fi trimise către Siri” este în regulă.
De asemenea, ar trebui să adăugați un apel la:
INPreferences.requestSiriAuthorization { (status) in }
Introduceți acest lucru în viewDidLoad
al controlerului de vizualizare principală pentru a cere utilizatorului accesul Siri. This will show the message you configured above and also let the user know that they could be using Siri for this 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:
- Create a
plist
file in your app (not the extension), namedAppIntentVocabulary.plist
. - Fill out the intents and phrases that you support.
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 <appName>,” but I found other phrases by trial and error (for example, Siri understands what “lists” are too, not just notes).
rezumat
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:
- Make an app ID for an Intents extension.
- Make an app group if you don't already have one.
- Use the app group in the app ID for the app and extension.
- Add Siri support to the app's ID.
- Regenerate the profiles and download them.
And here are the steps in Xcode for creating Siri's Intents extension:
- Add an Intents extension using the Xcode template.
- Update the entitlements of the app and extension to match the profiles (groups and Siri support).
- Add your intents to the extension's
plist
.
And you'll need to add code to do the following things:
- Use the app group sandbox to communicate between the app and extension.
- Add classes to support each intent with resolve, confirm and handle functions.
- Update the generated
IntentHandler
to use those classes. - Ask for Siri access somewhere in your app.
Finally, there are some Siri-specific configuration settings:
- Add the Siri support security string to your app's
plist
. - Add sample phrases to an
AppIntentVocabulary.plist
file in your app. - 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.
Lectură suplimentară
- “SiriKit,” Apple
The technical documentation contains the full list of domains and intents. - “Guides and Sample Code,” Apple
Includes code for many domains. - “Introducing SiriKit” (video, Safari only), WWDC 2016 Apple
- “What's New in SiriKit” (video, Safari only), WWDC 2017, Apple
Apple introduces lists and notes - “Lists and Notes,” Apple
The full list of lists and notes intents.