Passen die Absichten von Sirikit zu Ihrer App? Wenn ja, hier ist, wie man sie verwendet

Veröffentlicht: 2022-03-10
Kurze Zusammenfassung ↬ Seit letztem Jahr ist es möglich, einer App Siri-Unterstützung hinzuzufügen, wenn sie in einen von Apples vordefinierten Anwendungsfällen passt. Finden Sie heraus, ob SiriKit für Sie geeignet ist und wie Sie es verwenden.

Seit iOS 5 hilft Siri iPhone-Nutzern, mit Apples Apps Nachrichten zu senden, Erinnerungen einzurichten und Restaurants zu suchen. Ab iOS 10 konnten wir Siri auch in einigen unserer eigenen Apps verwenden.

Um diese Funktion nutzen zu können, muss Ihre App in die vordefinierten „Domains and Intents“ von Siri von Apple passen. In diesem Artikel erfahren wir, welche das sind und ob unsere Apps sie verwenden können. Wir nehmen eine einfache App, die ein Aufgabenlisten-Manager ist, und lernen, wie man Siri-Unterstützung hinzufügt. Wir werden auch die Richtlinien der Apple-Entwickler-Website zur Konfiguration und zum Swift-Code für eine neue Art von Erweiterung durchgehen, die mit SiriKit eingeführt wurde: die Intents -Erweiterung.

Wenn Sie zum Codierungsteil dieses Artikels gelangen, benötigen Sie Xcode (mindestens Version 9.x), und es wäre gut, wenn Sie mit der iOS-Entwicklung in Swift vertraut sind, da wir Siri zu einer kleinen Arbeit hinzufügen werden App. Wir gehen die Schritte zum Einrichten einer Erweiterung auf der Entwickler-Website von Apple und zum Hinzufügen des Siri-Erweiterungscodes zur App durch.

„Hey Siri, warum brauche ich dich?“

Manchmal benutze ich mein Telefon auf meiner Couch, habe beide Hände frei und kann mich voll und ganz auf den Bildschirm konzentrieren. Vielleicht schreibe ich meiner Schwester eine SMS, um den Geburtstag unserer Mutter zu planen, oder beantworte eine Frage in Trello. Ich kann die App sehen. Ich kann auf den Bildschirm tippen. Ich kann tippen.

Aber vielleicht laufe ich durch meine Stadt und höre mir einen Podcast an, wenn auf meiner Uhr eine SMS eingeht. Mein Telefon ist in meiner Tasche, und ich kann nicht einfach antworten, während ich gehe.

Mehr nach dem Sprung! Lesen Sie unten weiter ↓

Mit Siri kann ich die Steuertaste meines Kopfhörers gedrückt halten und sagen: „Schick meiner Schwester eine SMS, dass ich um zwei Uhr da bin.“ Siri ist großartig, wenn Sie unterwegs sind und Ihrem Telefon nicht die volle Aufmerksamkeit schenken können oder wenn die Interaktion gering ist, aber es erfordert mehrere Berührungen und eine Menge Tippen.

Das ist in Ordnung, wenn ich Apple-Apps für diese Interaktionen verwenden möchte. Aber einige Kategorien von Apps, wie Messaging, haben sehr beliebte Alternativen. Andere Aktivitäten, wie das Buchen einer Fahrt oder das Reservieren eines Tisches in einem Restaurant, sind mit Apples eingebauten Apps nicht einmal möglich, aber perfekt für Siri.

Apples Ansatz für Sprachassistenten

Um Siri in Apps von Drittanbietern zu aktivieren, musste Apple sich für einen Mechanismus entscheiden, um den Ton von der Stimme des Benutzers zu nehmen und ihn irgendwie so in die App zu bringen, dass er die Anfrage erfüllen kann. Um dies zu ermöglichen, verlangt Apple, dass der Benutzer den Namen der App in der Anfrage erwähnt, aber sie hatten mehrere Möglichkeiten, was mit dem Rest der Anfrage zu tun ist.

  • Es könnte eine Sounddatei an die App gesendet haben.
    Der Vorteil dieses Ansatzes besteht darin, dass die App versuchen könnte, buchstäblich jede Anfrage des Benutzers zu bearbeiten. Amazon oder Google könnte dieser Ansatz gefallen haben, da sie bereits über ausgeklügelte Spracherkennungsdienste verfügen. Aber die meisten Apps könnten damit nicht so einfach umgehen.
  • Es hätte die Rede in Text umwandeln und diesen senden können.
    Da viele Apps keine ausgefeilten Implementierungen in natürlicher Sprache haben, müsste sich der Benutzer normalerweise an ganz bestimmte Ausdrücke halten, und die Implementierung von nicht-englischer Unterstützung wäre Sache des App-Entwicklers.
  • Es hätte Sie bitten können, eine Liste mit Sätzen bereitzustellen, die Sie verstehen.
    Dieser Mechanismus ist näher an dem, was Amazon mit Alexa macht (in seinem „Skills“-Framework), und er ermöglicht weitaus mehr Verwendungen von Alexa, als SiriKit derzeit bewältigen kann. In einem Alexa-Skill stellen Sie Phrasen mit Platzhaltervariablen bereit, die Alexa für Sie ausfüllt. Zum Beispiel „Alexa, erinnere mich um $TIME$ an $REMINDER$ “ – Alexa wird diesen Satz mit dem vergleichen, was der Benutzer gesagt hat, und Ihnen die Werte für TIME und REMINDER . Wie beim vorherigen Mechanismus muss der Entwickler die gesamte Übersetzung vornehmen, und es gibt nicht viel Flexibilität, wenn der Benutzer etwas anderes sagt.
  • Es könnte eine Liste von Anfragen mit Parametern definieren und der App eine strukturierte Anfrage senden.
    Dies ist eigentlich das, was Apple tut, und der Vorteil ist, dass es eine Vielzahl von Sprachen unterstützen kann und die ganze Arbeit macht, um zu versuchen, alle Möglichkeiten zu verstehen, wie ein Benutzer eine Anfrage formulieren könnte. Der große Nachteil ist, dass Sie nur Handler für Anforderungen implementieren können, die Apple definiert. Das ist großartig, wenn Sie beispielsweise eine Messaging-App haben, aber wenn Sie einen Musik-Streaming-Dienst oder einen Podcast-Player haben, haben Sie derzeit keine Möglichkeit, SiriKit zu verwenden.

Ebenso gibt es drei Möglichkeiten für Apps, mit dem Benutzer zu sprechen: mit Ton, mit Text, der konvertiert wird, oder indem Sie die Art von etwas ausdrücken, das Sie sagen möchten, und das System herausfinden lassen, wie es genau ausgedrückt wird. Die letzte Lösung (was Apple tut) legt die Last der Übersetzung auf Apple, aber es gibt Ihnen begrenzte Möglichkeiten, Ihre eigenen Worte zu verwenden, um Dinge zu beschreiben.

Die Arten von Anfragen, die Sie bearbeiten können, sind in den Domänen und Absichten von Sirikit definiert. Eine Absicht ist eine Art von Anfrage, die ein Benutzer stellen kann, z. B. das Senden einer SMS an einen Kontakt oder das Suchen eines Fotos. Jeder Intent hat eine Liste von Parametern – zum Beispiel erfordert SMS einen Kontakt und eine Nachricht.

Eine Domäne ist nur eine Gruppe verwandter Absichten. Das Lesen eines Textes und das Senden eines Textes liegen beide in der Messaging-Domäne. Das Buchen einer Fahrt und das Abrufen eines Standorts befinden sich in der Domäne „Fahrtbuchung“. Es gibt Domains, um VoIP-Anrufe zu tätigen, Trainings zu starten, nach Fotos zu suchen und einiges mehr. Die Dokumentation von Sirikit enthält eine vollständige Liste der Domänen und ihrer Absichten.

Eine häufige Kritik an Siri ist, dass es anscheinend nicht in der Lage ist, Anfragen so gut zu verarbeiten wie Google und Alexa, und dass das von Apples Konkurrenten ermöglichte Sprachökosystem von Drittanbietern reichhaltiger ist.

Ich stimme dieser Kritik zu. Wenn Ihre App nicht in die aktuellen Absichten passt, können Sie SiriKit nicht verwenden, und es gibt nichts, was Sie tun können. Selbst wenn Ihre App passt, können Sie nicht alle Wörter kontrollieren, die Siri sagt oder versteht; Wenn Sie also eine bestimmte Art haben, über Dinge in Ihrer App zu sprechen, können Sie dies Siri nicht immer beibringen.

Die Hoffnung der iOS-Entwickler ist sowohl, dass Apple seine Liste der Absichten stark erweitern wird, als auch, dass seine natürliche Sprachverarbeitung viel besser wird. Wenn dies der Fall ist, haben wir einen Sprachassistenten, der funktioniert, ohne dass Entwickler Übersetzungen vornehmen oder alle Arten verstehen müssen, dasselbe zu sagen. Und die Implementierung der Unterstützung für strukturierte Anfragen ist eigentlich ziemlich einfach – viel einfacher als das Erstellen eines Parsers für natürliche Sprache.

Ein weiterer großer Vorteil des Intents-Frameworks besteht darin, dass es nicht auf Siri- und Sprachanfragen beschränkt ist. Bereits jetzt kann die Karten-App eine absichtsbasierte Anfrage Ihrer App generieren (z. B. eine Restaurantreservierung). Dies geschieht programmgesteuert (nicht per Stimme oder natürlicher Sprache). Wenn Apple es Apps erlauben würde, die offengelegten Absichten der anderen zu entdecken, hätten wir eine viel bessere Möglichkeit für Apps, zusammenzuarbeiten (im Gegensatz zu URLs im X-Callback-Stil).

Da ein Intent eine strukturierte Anfrage mit Parametern ist, gibt es schließlich eine einfache Möglichkeit für eine App, auszudrücken, dass Parameter fehlen oder dass sie Hilfe bei der Unterscheidung zwischen einigen Optionen benötigt. Siri kann dann Folgefragen stellen, um die Parameter aufzulösen, ohne dass die App das Gespräch führen muss.

Die Ride-Booking-Domäne

Um Domänen und Absichten zu verstehen, sehen wir uns die Domäne „Fahrtbuchung“ an. Dies ist die Domain, die Sie verwenden würden, um Siri zu bitten, Ihnen ein Lyft-Auto zu besorgen.

Apple definiert, wie man nach einer Fahrt fragt und wie man Informationen darüber erhält, aber es gibt tatsächlich keine eingebaute Apple-App, die diese Anfrage tatsächlich verarbeiten kann. Dies ist eine der wenigen Domänen, in denen eine Sirikit-fähige App erforderlich ist.

Sie können eine der Absichten per Sprache oder direkt aus Maps aufrufen. Einige der Absichten für diese Domain sind:

  • Fordern Sie eine Fahrt an
    Verwenden Sie diesen, um eine Fahrt zu buchen. Sie müssen einen Abhol- und Abgabeort angeben, und die App muss möglicherweise auch die Größe Ihrer Gruppe und die Art der gewünschten Fahrt kennen. Ein Beispielsatz könnte lauten: „Buch mir eine Fahrt mit <Appname>.“
  • Rufen Sie den Status der Fahrt ab
    Verwenden Sie diesen Intent, um herauszufinden, ob Ihre Anfrage eingegangen ist, und um Informationen über das Fahrzeug und den Fahrer einschließlich ihres Standorts zu erhalten. Die Karten-App verwendet diese Absicht, um ein aktualisiertes Bild des Autos anzuzeigen, während es sich Ihnen nähert.
  • Fahrt stornieren
    Verwenden Sie dies, um eine von Ihnen gebuchte Fahrt zu stornieren.

Für jede dieser Absichten benötigt Siri möglicherweise weitere Informationen. Wie Sie sehen werden, wenn wir einen Intent-Handler implementieren, kann Ihre Intents-Erweiterung Siri mitteilen, dass ein erforderlicher Parameter fehlt, und Siri wird den Benutzer dazu auffordern.

Die Tatsache, dass Intents programmgesteuert von Maps aufgerufen werden können, zeigt, wie Intents in Zukunft die Kommunikation zwischen Apps ermöglichen könnten.

Hinweis : Eine vollständige Liste der Domains und ihrer Absichten finden Sie auf der Entwickler-Website von Apple. Es gibt auch eine Beispiel-App von Apple mit vielen implementierten Domänen und Absichten, einschließlich der Buchung von Mitfahrgelegenheiten.

Listen- und Notizen-Domänenunterstützung zu Ihrer App hinzufügen

OK, nachdem wir nun die Grundlagen von SiriKit verstanden haben, schauen wir uns an, wie Sie Unterstützung für Siri in einer App hinzufügen würden, die viel Konfiguration und eine Klasse für jede Absicht erfordert, die Sie verarbeiten möchten.

Der Rest dieses Artikels besteht aus den detaillierten Schritten zum Hinzufügen der Siri-Unterstützung zu einer App. Es gibt fünf allgemeine Dinge, die Sie tun müssen:

  1. Bereiten Sie sich darauf vor, der App eine neue Erweiterung hinzuzufügen, indem Sie Bereitstellungsprofile mit neuen Berechtigungen dafür auf der Entwickler-Website von Apple erstellen.
  2. Konfigurieren Sie Ihre App (über ihre plist ), um die Berechtigungen zu verwenden.
  3. Verwenden Sie die Vorlage von Xcode, um mit Beispielcode zu beginnen.
  4. Fügen Sie den Code hinzu, um Ihre Siri-Absicht zu unterstützen.
  5. Konfigurieren Sie das Vokabular von Siri über plist s.

Keine Sorge: Wir gehen alle durch und erklären dabei Erweiterungen und Berechtigungen.

Um mich nur auf die Siri-Teile zu konzentrieren, habe ich einen einfachen To-Do-Listen-Manager vorbereitet, List-o-Mat.

Ein animiertes GIF, das eine Demo von List-o-Mat zeigt
Listen erstellen in List-o-Mat (Große Vorschau)

Die vollständige Quelle des Beispiels, List-o-Mat, finden Sie auf GitHub.

Um es zu erstellen, habe ich lediglich mit der Xcode Master-Detail-App-Vorlage begonnen und beide Bildschirme in eine UITableView . Ich habe eine Möglichkeit hinzugefügt, Listen und Elemente hinzuzufügen und zu löschen, und eine Möglichkeit, Elemente als erledigt abzuhaken. Die gesamte Navigation wird von der Vorlage generiert.

Um die Daten zu speichern, habe ich das Codable Protokoll verwendet (eingeführt auf der WWDC 2017), das Strukturen in JSON umwandelt und in einer Textdatei im documents speichert.

Ich habe den Code bewusst sehr einfach gehalten. Wenn Sie Erfahrung mit Swift und dem Erstellen von View-Controllern haben, sollten Sie damit kein Problem haben.

Jetzt können wir die Schritte zum Hinzufügen der Sirikit-Unterstützung durchgehen. Die allgemeinen Schritte wären für jede App und jede Domäne und Absicht, die Sie implementieren möchten, gleich. Wir werden uns hauptsächlich mit der Entwickler-Website von Apple befassen, plist s bearbeiten und ein bisschen Swift schreiben.

Für List-o-Mat konzentrieren wir uns auf den Bereich Listen und Notizen, der allgemein auf Dinge wie Notizen-Apps und Aufgabenlisten anwendbar ist.

In der Domäne „Listen und Notizen“ haben wir die folgenden Absichten, die für unsere App sinnvoll wären.

  • Holen Sie sich eine Aufgabenliste.
  • Fügen Sie einer Liste eine neue Aufgabe hinzu.

Da die Interaktionen mit Siri tatsächlich außerhalb Ihrer App stattfinden (vielleicht sogar wenn Ihre App nicht läuft), verwendet iOS eine Erweiterung, um dies zu implementieren.

Die Intents-Erweiterung

Wenn Sie noch nicht mit Erweiterungen gearbeitet haben, müssen Sie drei wichtige Dinge wissen:

  1. Eine Verlängerung ist ein separater Vorgang. Es wird innerhalb des Bundles Ihrer App geliefert, läuft aber komplett eigenständig mit seiner eigenen Sandbox.
  2. Ihre App und Erweiterung können miteinander kommunizieren, indem sie sich in derselben App-Gruppe befinden. Der einfachste Weg ist über die freigegebenen Sandbox-Ordner der Gruppe (so dass sie dieselben Dateien lesen und schreiben können, wenn Sie sie dort ablegen).
  3. Erweiterungen erfordern ihre eigenen App-IDs, Profile und Berechtigungen.

Um Ihrer App eine Erweiterung hinzuzufügen, melden Sie sich zunächst bei Ihrem Entwicklerkonto an und gehen Sie zum Abschnitt „Zertifikate, Kennungen und Profile“.

Aktualisieren Ihrer Apple Developer App-Kontodaten

In unserem Apple-Entwicklerkonto müssen wir als erstes eine App-Gruppe erstellen. Gehen Sie zum Abschnitt „App-Gruppen“ unter „Identifikatoren“ und fügen Sie eine hinzu.

Ein Screenshot des Dialogfelds der Apple-Entwicklerwebsite zum Registrieren einer App-Gruppe
Registrieren einer App-Gruppe (Große Vorschau)

Sie muss mit group beginnen, gefolgt von Ihrer üblichen domänenbasierten Reverse-ID. Da es ein Präfix hat, können Sie den Bezeichner Ihrer App für den Rest verwenden.

Dann müssen wir die ID unserer App aktualisieren, um diese Gruppe zu verwenden und Siri zu aktivieren:

  1. Gehen Sie zum Abschnitt „App-IDs“ und klicken Sie auf die ID Ihrer App;
  2. Klicken Sie auf die Schaltfläche „Bearbeiten“;
  3. Aktivieren Sie App-Gruppen (falls nicht für eine andere Erweiterung aktiviert).
    Ein Screenshot der Apple-Entwicklerwebsite, die App-Gruppen für eine App-ID aktiviert
    App-Gruppen aktivieren (große Vorschau)
  4. Konfigurieren Sie dann die App-Gruppe, indem Sie auf die Schaltfläche „Bearbeiten“ klicken. Wählen Sie die vorherige App-Gruppe aus.
    Ein Screenshot des Dialogfelds der Apple-Entwickler-Website zum Festlegen des App-Gruppennamens
    Legen Sie den Namen der App-Gruppe fest (große Vorschau)
  5. Aktivieren Sie Sirikit.
    Ein Screenshot von Sirikit wird aktiviert
    Sirikit aktivieren (große Vorschau)
  6. Klicken Sie auf „Fertig“, um es zu speichern.

Jetzt müssen wir eine neue App-ID für unsere Erweiterung erstellen:

  1. Fügen Sie im selben Abschnitt „App-IDs“ eine neue App-ID hinzu. Dies ist die Kennung Ihrer App mit einem Suffix. Verwenden Sie nicht nur Intents als Suffix, da dieser Name zum Namen Ihres Moduls in Swift wird und dann mit den echten Intents in Konflikt geraten würde.
    Ein Screenshot des Apple-Entwicklerbildschirms zum Erstellen einer App-ID
    App-ID für die Intents-Erweiterung erstellen (große Vorschau)
  2. Aktivieren Sie diese App-ID auch für App-Gruppen (und richten Sie die Gruppe wie zuvor ein).

Erstellen Sie jetzt ein Bereitstellungsprofil für die Entwicklung für die Intents-Erweiterung und generieren Sie das Bereitstellungsprofil Ihrer App neu. Laden Sie sie herunter und installieren Sie sie wie gewohnt.

Nachdem unsere Profile installiert sind, müssen wir zu Xcode gehen und die Berechtigungen der App aktualisieren.

Aktualisieren der Berechtigungen Ihrer App in Xcode

Zurück in Xcode wählen Sie den Namen Ihres Projekts im Projektnavigator aus. Wählen Sie dann das Hauptziel Ihrer App und gehen Sie zur Registerkarte „Fähigkeiten“. Dort sehen Sie einen Schalter zum Aktivieren der Siri-Unterstützung.

Ein Screenshot des Berechtigungsbildschirms von Xcode, der SiriKit zeigt, ist aktiviert
Aktivieren Sie Sirikit in den Berechtigungen Ihrer App. (Große Vorschau)

Weiter unten in der Liste können Sie App-Gruppen aktivieren und konfigurieren.

Ein Screenshot des Berechtigungsbildschirms von Xcode, der die App-Gruppe zeigt, ist aktiviert und konfiguriert
Konfigurieren Sie die App-Gruppe der App (große Vorschau)

Wenn Sie es richtig eingerichtet haben, sehen Sie dies in der .entitlements -Datei Ihrer App:

Ein Screenshot der Plist der App, der zeigt, dass die Berechtigungen festgelegt sind
Die Liste zeigt die von Ihnen festgelegten Berechtigungen (große Vorschau)

Jetzt sind wir endlich bereit, das Intents-Erweiterungsziel zu unserem Projekt hinzuzufügen.

Hinzufügen der Intents-Erweiterung

Wir sind endlich bereit, die Erweiterung hinzuzufügen. Wählen Sie in Xcode „Datei“ → „Neues Ziel“. Dieses Blatt wird angezeigt:

Ein Screenshot, der die Intents-Erweiterung im Dialogfeld „Neues Ziel“ in Xcode zeigt
Fügen Sie Ihrem Projekt die Intents-Erweiterung hinzu (große Vorschau)

Wählen Sie „Intents Extension“ und klicken Sie auf die Schaltfläche „Next“. Füllen Sie den folgenden Bildschirm aus:

Ein Screenshot von Xcode, der zeigt, wie Sie die Intents-Erweiterung konfigurieren
Konfigurieren Sie die Intents-Erweiterung (große Vorschau)

Der Produktname muss mit dem Suffix übereinstimmen, das Sie in der Intents-App-ID auf der Apple-Entwickler-Website erstellt haben.

Wir haben uns entschieden, keine Intents-UI-Erweiterung hinzuzufügen. Dies wird in diesem Artikel nicht behandelt, aber Sie können es später hinzufügen, wenn Sie eines benötigen. Im Grunde ist es eine Möglichkeit, Ihr eigenes Branding und Ihren eigenen Anzeigestil in die visuellen Ergebnisse von Siri einzufügen.

Wenn Sie fertig sind, erstellt Xcode eine Intents-Handler-Klasse, die wir als Startteil für unsere Siri-Implementierung verwenden können.

Der Intents Handler: Lösen, bestätigen und handhaben

Xcode hat ein neues Ziel generiert, das einen Ausgangspunkt für uns hat.

Als erstes müssen Sie dieses neue Ziel so einrichten, dass es sich in derselben App-Gruppe wie die App befindet. Gehen Sie wie zuvor zur Registerkarte „Fähigkeiten“ des Ziels, aktivieren Sie App-Gruppen und konfigurieren Sie sie mit Ihrem Gruppennamen. Denken Sie daran, dass Apps in derselben Gruppe eine Sandbox haben, die sie verwenden können, um Dateien miteinander zu teilen. Wir benötigen dies, damit Siri-Anfragen an unsere App gelangen.

List-o-Mat hat eine Funktion, die den Gruppendokumentordner zurückgibt. Wir sollten es immer dann verwenden, wenn wir eine freigegebene Datei lesen oder in sie schreiben möchten.

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

Wenn wir zum Beispiel die Listen speichern, verwenden wir Folgendes:

 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 }

Die Intents-Erweiterungsvorlage hat eine Datei namens IntentHandler.swift mit einer Klasse namens IntentHandler . Es wurde auch so konfiguriert, dass es der Einstiegspunkt der Absichten in der plist der Erweiterung ist.

Ein Screenshot von Xcode, der zeigt, wie der IntentHandler als Einstiegspunkt konfiguriert ist
Die Intent-Erweiterung plist konfiguriert IntentHandler als Einstiegspunkt

In derselben plist sehen Sie einen Abschnitt, um die von uns unterstützten Absichten anzugeben. Wir beginnen mit derjenigen, die die Suche nach Listen ermöglicht, die INSearchForNotebookItemsIntent . Fügen Sie es dem Array unter IntentsSupported .

Ein Screenshot in Xcode, der zeigt, dass die Erweiterungsliste die Absichten auflisten sollte, die sie verarbeitet
Den Namen des Intents zur Intents-Plist hinzufügen (große Vorschau)

Gehen Sie nun zu IntentHandler.swift und ersetzen Sie den Inhalt durch diesen Code:

 import Intents class IntentHandler: INExtension { override func handler(for intent: INIntent) -> Any? { switch intent { case is INSearchForNotebookItemsIntent: return SearchItemsIntentHandler() default: return nil } } }

Die handler -Funktion wird aufgerufen, um ein Objekt dazu zu bringen, eine bestimmte Absicht zu verarbeiten. Sie können einfach alle Protokolle in dieser Klasse implementieren und self zurückgeben, aber wir packen jede Absicht in eine eigene Klasse, um sie besser zu organisieren.

Da wir beabsichtigen, ein paar verschiedene Klassen zu haben, geben wir ihnen eine gemeinsame Basisklasse für Code, den wir zwischen ihnen teilen müssen:

 class ListOMatIntentsHandler: NSObject { }

Das Intents-Framework erfordert, dass wir von NSObject erben. Wir werden später einige Methoden eintragen.

Wir beginnen unsere Suchimplementierung damit:

 class SearchItemsIntentHandler: ListOMatIntentsHandler, INSearchForNotebookItemsIntentHandling { }

Um einen Intent-Handler festzulegen, müssen wir drei grundlegende Schritte implementieren

  1. Lösen Sie die Parameter auf.
    Stellen Sie sicher, dass die erforderlichen Parameter angegeben sind, und machen Sie alle eindeutig, die Sie nicht vollständig verstehen.
  2. Bestätigen Sie , dass die Anforderung machbar ist.
    Dies ist oft optional, aber selbst wenn Sie wissen, dass jeder Parameter gut ist, benötigen Sie möglicherweise dennoch Zugriff auf eine externe Ressource oder haben andere Anforderungen.
  3. Bearbeiten Sie die Anfrage.
    Tun Sie das, was verlangt wird.

INSearchForNotebookItemsIntent , die erste von uns implementierte Absicht, kann als Aufgabensuche verwendet werden. Die Arten von Anfragen, die wir damit bearbeiten können, sind „In List-o-Mat die Liste der Lebensmittelgeschäfte anzeigen“ oder „In List-o-Mat die Liste der Geschäfte anzeigen“.

Nebenbei: „List-o-Mat“ ist eigentlich ein schlechter Name für eine SiriKit-App, denn Siri tut sich schwer mit Bindestrichen in Apps. Glücklicherweise erlaubt uns SiriKit, alternative Namen zu haben und die Aussprache bereitzustellen. Fügen Sie in der Info.plist der App diesen Abschnitt hinzu:

Ein Screenshot von Xcode, der zeigt, dass die App-Liste alternative App-Namen und Aussprachen hinzufügen kann
Fügen Sie der App-Liste alternative App-Namen und Ausspracheführer hinzu

Dies ermöglicht dem Benutzer, „Liste oh mat“ zu sagen und dies als ein einzelnes Wort (ohne Bindestriche) zu verstehen. Es sieht auf dem Bildschirm nicht ideal aus, aber ohne es denkt Siri manchmal, dass „List“ und „Mat“ getrennte Wörter sind, und wird sehr verwirrt.

Lösung: Herausfinden der Parameter

Für eine Suche nach Notizbuchelementen gibt es mehrere Parameter:

  1. der Elementtyp (eine Aufgabe, eine Aufgabenliste oder eine Notiz),
  2. Titel des Artikels,
  3. der Inhalt des Artikels,
  4. den Fertigstellungsstatus (ob die Aufgabe als erledigt markiert ist oder nicht),
  5. der Ort, mit dem es verbunden ist,
  6. das zugehörige Datum.

Wir benötigen nur die ersten beiden, also müssen wir Auflösungsfunktionen für sie schreiben. INSearchForNotebookItemsIntent hat Methoden, die wir implementieren müssen.

Da uns nur das Anzeigen von Aufgabenlisten wichtig ist, codieren wir dies fest in die Auflösung für den Elementtyp. Fügen Sie in SearchItemsIntentHandler hinzu:

 func resolveItemType(for intent: INSearchForNotebookItemsIntent, with completion: @escaping (INNotebookItemTypeResolutionResult) -> Void) { completion(.success(with: .taskList)) }

Also, egal was der Benutzer sagt, wir suchen nach Aufgabenlisten. Wenn wir unsere Suchunterstützung erweitern wollten, ließen wir Siri versuchen, dies anhand des ursprünglichen Ausdrucks herauszufinden, und dann einfach completion(.needsValue()) verwenden, wenn der Elementtyp fehlte. Alternativ könnten wir versuchen, anhand des Titels zu erraten, indem wir sehen, was dazu passt. In diesem Fall würden wir erfolgreich abschließen, wenn Siri weiß, was es ist, und wir würden completion(.notRequired()) verwenden, wenn wir mehrere Möglichkeiten ausprobieren möchten.

Die Titelauflösung ist etwas kniffliger. Was wir wollen, ist, dass Siri eine Liste verwendet, wenn es eine findet, die genau mit dem übereinstimmt, was Sie gesagt haben. Wenn es unsicher ist oder es mehr als eine Möglichkeit gibt, möchten wir, dass Siri uns um Hilfe bittet, um es herauszufinden. Zu diesem Zweck stellt Sirikit eine Reihe von Auflösungsaufzählungen bereit, mit denen wir ausdrücken können, was als Nächstes geschehen soll.

Wenn Sie also „Lebensmittelgeschäft“ sagen, hätte Siri eine genaue Übereinstimmung. Aber wenn Sie „Speichern“ sagen, würde Siri ein Menü mit passenden Listen präsentieren.

Wir beginnen mit dieser Funktion, um die Grundstruktur zu geben:

 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) }

Wir implementieren getPossibleLists(for:) und completeResolveListName(with:for:with:) in der ListOMatIntentsHandler .

getPossibleLists(for:) muss versuchen, den Titel, den Siri uns übergibt, unscharf mit den tatsächlichen Listennamen abzugleichen.

 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 }

Wir durchlaufen alle unsere Listen. Wenn wir eine genaue Übereinstimmung erhalten, geben wir sie zurück, und wenn nicht, geben wir eine Reihe von Möglichkeiten zurück. In dieser Funktion prüfen wir einfach, ob das Wort, das der Benutzer gesagt hat, in einem Listennamen enthalten ist (also eine ziemlich einfache Übereinstimmung). Dadurch kann „Lebensmittelgeschäft“ mit „Lebensmittelgeschäft“ übereinstimmen. Ein fortschrittlicherer Algorithmus könnte versuchen, anhand von Wörtern, die gleich klingen, Übereinstimmungen zu finden (z. B. mit dem Soundex-Algorithmus).

completeResolveListName(with:for:with:) ist verantwortlich für die Entscheidung, was mit dieser Liste von Möglichkeiten geschehen soll.

 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)) } }

Wenn wir eine genaue Übereinstimmung haben, teilen wir Siri mit, dass wir erfolgreich waren. Wenn wir eine ungenaue Übereinstimmung erhalten, weisen wir Siri an, den Benutzer zu fragen, ob wir es richtig erraten haben.

Wenn wir mehrere Übereinstimmungen erhalten haben, verwenden wir die completion(.disambiguation(with: possibleLists)) , um Siri anzuweisen, eine Liste anzuzeigen und den Benutzer eine auswählen zu lassen.

Jetzt, da wir wissen, was die Anfrage ist, müssen wir uns das Ganze ansehen und sicherstellen, dass wir damit umgehen können.

Bestätigen: Überprüfen Sie alle Ihre Abhängigkeiten

In diesem Fall können wir die Anfrage immer bearbeiten, wenn wir alle Parameter geklärt haben. Typische confirm() Implementierungen prüfen möglicherweise die Verfügbarkeit externer Dienste oder die Autorisierungsebenen.

Da confirm() optional ist, könnten wir einfach nichts tun, und Siri würde davon ausgehen, dass wir jede Anfrage mit aufgelösten Parametern bearbeiten könnten. Um explizit zu sein, könnten wir Folgendes verwenden:

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

Das bedeutet, dass wir alles bewältigen können.

Griff: Mach es

Der letzte Schritt ist die Bearbeitung der Anfrage.

 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) }

Zuerst finden wir die Liste basierend auf dem Titel. Zu diesem Zeitpunkt hat resolveTitle bereits dafür gesorgt, dass wir eine exakte Übereinstimmung erhalten. Aber wenn es ein Problem gibt, können wir immer noch einen Fehler zurückgeben.

Wenn wir einen Fehler haben, haben wir die Möglichkeit, eine Benutzeraktivität weiterzuleiten. Wenn Ihre App Handoff verwendet und genau diese Art von Anfrage verarbeiten kann, versucht Siri möglicherweise, die Anfrage an Ihre App weiterzuleiten, um die Anfrage dort zu versuchen. Es wird dies nicht tun, wenn wir uns in einem reinen Sprachkontext befinden (z. B. wenn Sie mit „Hey Siri“ begonnen haben), und es garantiert nicht, dass es dies in anderen Fällen tun wird, also verlassen Sie sich nicht darauf.

Dies ist jetzt bereit zum Testen. Wählen Sie die Absichtserweiterung in der Zielliste in Xcode aus. Aber bevor Sie es ausführen, bearbeiten Sie das Schema.

Ein Screenshot von Xcode, der zeigt, wie ein Schema bearbeitet wird
Bearbeiten Sie das Schema der Absicht, um einen Beispielsatz zum Debuggen hinzuzufügen.

Das bringt eine Möglichkeit, eine Abfrage direkt bereitzustellen:

Ein Screenshot von Xcode, der den Dialog Schema bearbeiten zeigt
Fügen Sie den Beispielsatz zum Abschnitt Ausführen des Schemas hinzu. (Große Vorschau)

Beachten Sie, dass ich wegen des oben erwähnten Bindestrichproblems „ListOMat“ verwende. Glücklicherweise wird es genauso ausgesprochen wie der Name meiner App, also sollte es kein großes Problem sein.

Zurück in der App habe ich eine „Lebensmittelgeschäft“-Liste und eine „Baumarkt“-Liste erstellt. Wenn ich Siri nach der „Store“-Liste frage, durchläuft es den Disambiguierungspfad, der so aussieht:

Ein animiertes GIF, das zeigt, wie Siri eine Anfrage zum Anzeigen der Store-Liste bearbeitet
Siri bearbeitet die Anfrage, indem sie um Klärung bittet. (Große Vorschau)

Wenn Sie „Lebensmittelgeschäft“ sagen, erhalten Sie eine exakte Übereinstimmung, die direkt zu den Ergebnissen führt.

Elemente über Siri hinzufügen

Jetzt, da wir die grundlegenden Konzepte von Auflösen, Bestätigen und Behandeln kennen, können wir schnell eine Absicht hinzufügen, um ein Element zu einer Liste hinzuzufügen.

Fügen Sie zuerst INAddTasksIntent zur plist der Erweiterung hinzu:

Ein Screenshot in XCode, der die neue Absicht zeigt, die der Plist hinzugefügt wird
INAddTasksIntent zur Erweiterungsliste hinzufügen (große Vorschau)

Aktualisieren Sie dann die handle -Funktion unseres IntentHandler .

 override func handler(for intent: INIntent) -> Any? { switch intent { case is INSearchForNotebookItemsIntent: return SearchItemsIntentHandler() case is INAddTasksIntent: return AddItemsIntentHandler() default: return nil } }

Fügen Sie einen Stub für die neue Klasse hinzu:

 class AddItemsIntentHandler: ListOMatIntentsHandler, INAddTasksIntentHandling { }

Das Hinzufügen eines Elements erfordert eine ähnliche resolve für die Suche, außer mit einer Zielaufgabenliste anstelle eines Titels.

 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 ist genau wie completeResolveListName , aber mit etwas anderen Typen (eine Aufgabenliste anstelle des Titels einer Aufgabenliste).

 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)) } }

Es hat die gleiche Disambiguierungslogik und verhält sich genauso. Die Aussage „Geschäft“ muss eindeutig sein, und die Aussage „Lebensmittelgeschäft“ wäre eine genaue Übereinstimmung.

Wir lassen die confirm unimplementiert und akzeptieren die Standardeinstellung. Für handle müssen wir der Liste ein Element hinzufügen und es speichern.

 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) }

Wir erhalten eine Liste von Gegenständen und eine Zielliste. Wir schlagen die Liste nach und fügen die Elemente hinzu. Wir müssen auch eine Antwort vorbereiten, die Siri mit den hinzugefügten Elementen anzeigen und an die Vervollständigungsfunktion senden kann.

Diese Funktion kann einen Satz wie „Fügen Sie in ListOMat Äpfel zur Einkaufsliste hinzu.“ verarbeiten. Es kann auch eine Liste mit Artikeln wie „Reis, Zwiebeln und Oliven“ verarbeiten.

Ein Screenshot des Simulators, der zeigt, wie Siri Artikel zur Lebensmittelgeschäftliste hinzufügt
Siri fügt der Einkaufsliste ein paar Artikel hinzu

Fast fertig, nur noch ein paar Einstellungen

All dies funktioniert in Ihrem Simulator oder lokalen Gerät, aber wenn Sie dies übermitteln möchten, müssen Sie der plist Ihrer App einen NSSiriUsageDescription Schlüssel mit einer Zeichenfolge hinzufügen, die beschreibt, wofür Sie Siri verwenden. Etwas wie „Ihre Anfragen zu Listen werden an Siri gesendet“ ist in Ordnung.

Sie sollten auch einen Anruf hinzufügen an:

 INPreferences.requestSiriAuthorization { (status) in }

Fügen Sie dies in viewDidLoad Ihres Hauptansichtscontrollers ein, um den Benutzer nach Siri-Zugriff zu fragen. 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. (Große Vorschau)

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).

Zusammenfassung

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.

Weiterführende Lektüre

  • “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.