- Об авторе Лу Франко - инженер Atlassian, работающий над Trello iOS. Он разрабатывает для мобильных...
- Подход Apple к голосовым помощникам
- Домен Ride-Booking
- Добавление поддержки списков и заметок в ваше приложение
- Расширение Intents
- Обновление данных вашей учетной записи Apple Developer App
- Обновление прав вашего приложения в Xcode
- Добавление расширения Intents
- Обработчик содержимого: разрешение, подтверждение и обработка
- Решить: выяснить параметры
- Подтвердите: проверьте все ваши зависимости
- Ручка: сделай это
- Добавление предметов через Siri
- Почти готово, еще несколько настроек
- Резюме
Об авторе
Лу Франко - инженер Atlassian, работающий над Trello iOS. Он разрабатывает для мобильных устройств с 2000 года и является соавтором книги для начинающих ... Подробнее о Лу ...
С прошлого года стало возможным добавлять поддержку Siri в приложение, если оно вписывается в один из предопределенных вариантов использования Apple. Узнайте, подойдет ли SiriKit для вас и как его использовать.
Начиная с iOS 5, Siri помогает пользователям iPhone отправлять сообщения, устанавливать напоминания и искать рестораны с помощью приложений Apple. Начиная с iOS 10, мы смогли использовать Siri и в некоторых наших собственных приложениях.
Чтобы использовать эту функциональность, ваше приложение должно вписываться в предопределенные Apple «домены и намерения» Siri. В этой статье мы узнаем, что это такое, и посмотрим, могут ли наши приложения их использовать. Мы возьмем простое приложение, которое является менеджером списка дел, и узнаем, как добавить поддержку Siri. Мы также пройдемся по рекомендациям веб-сайта Apple для разработчиков по настройке и коду Swift для нового типа расширения, представленного в SiriKit: расширения Intents .
Когда вы перейдете к части кода этой статьи, вам понадобится Xcode (по крайней мере, версия 9.x), и было бы хорошо, если вы знакомы с разработкой iOS в Swift, потому что мы собираемся добавить Siri в небольшой рабочий приложение. Мы пройдем этапы настройки расширения на веб-сайте Apple для разработчиков и добавления кода расширения Siri в приложение.
«Эй, Сири, зачем ты мне нужен?»
Иногда я использую свой телефон, когда я лежу на диване, и обе руки свободны, и я могу уделить экрану все свое внимание. Может быть, я напишу сестре, чтобы спланировать день рождения нашей мамы, или отвечу на вопрос в Трелло. Я могу видеть приложение. Я могу коснуться экрана. Я могу печатать.
Но я могу прогуливаться по своему городу, слушать подкаст, когда на моих часах появляется сообщение. Мой телефон у меня в кармане, и я не могу легко ответить во время ходьбы.
С помощью Siri я могу удерживать нажатой кнопку управления наушниками и сказать: «Напиши моей сестре, что я буду там к двум часам». Siri великолепна, когда ты в пути, и не можешь уделить все свое внимание своему телефону. или когда взаимодействие незначительное, но оно требует нескольких нажатий и кучу печатания.
Это хорошо, если я хочу использовать приложения Apple для этих взаимодействий. Но у некоторых категорий приложений, таких как обмен сообщениями, есть очень популярные альтернативы. Другие действия, такие как бронирование поездки или бронирование стола в ресторане, даже невозможны со встроенными приложениями Apple, но идеально подходят для Siri.
Подход Apple к голосовым помощникам
Чтобы включить Siri в сторонних приложениях, Apple должна была выбрать механизм, позволяющий извлекать звук из голоса пользователя и каким-то образом передавать его в приложение таким образом, чтобы оно могло выполнить запрос. Чтобы сделать это возможным, Apple требует, чтобы пользователь упомянул имя приложения в запросе, но у них было несколько вариантов того, что делать с остальной частью запроса.
- Он мог отправить звуковой файл в приложение.
Преимущество этого подхода состоит в том, что приложение может пытаться обрабатывать буквально любой запрос, который может потребоваться пользователю. Amazon или Google, возможно, понравился бы этот подход, потому что у них уже есть сложные услуги распознавания голоса. Но большинство приложений не смогут справиться с этим очень легко. - Это могло бы превратить речь в текст и послать это.
Поскольку во многих приложениях нет сложных реализаций на естественном языке, пользователю обычно приходится придерживаться очень определенных фраз, а поддержка не на английском языке будет зависеть от разработчика приложения. - Он мог бы попросить вас предоставить список фраз, которые вы понимаете.
Этот механизм ближе к тому, что Amazon делает с Alexa (в своей структуре «навыков»), и он позволяет использовать гораздо больше Alexa, чем SiriKit в настоящее время может справиться. В навыке Alexa вы предоставляете фразы с переменными-заполнителями, которые Alexa заменит для вас. Например, «Alexa, напомните мне от $ TIME $ до $ REMINDER $» - Alexa запустит эту фразу против того, что сказал пользователь, и сообщит вам значения для TIME и REMINDER. Как и в предыдущем механизме, разработчик должен выполнить весь перевод, и гибкость невелика, если пользователь говорит что-то немного другое. - Он может определять список запросов с параметрами и отправлять приложению структурированный запрос.
Это на самом деле то, что делает Apple, и преимущество в том, что она может поддерживать различные языки, и она делает всю работу, пытаясь понять все способы, которыми пользователь может сформулировать запрос. Большим недостатком является то, что вы можете реализовывать обработчики только для запросов, определенных Apple. Это замечательно, если у вас есть, например, приложение для обмена сообщениями, но если у вас есть служба потоковой передачи музыки или проигрыватель подкастов, у вас нет возможности использовать SiriKit прямо сейчас.
Аналогичным образом, существует три способа, которыми приложения могут общаться с пользователем: со звуком, с преобразованным текстом или с помощью выражения того, что вы хотите сказать, и позволяя системе определить точный способ выразить это. Последнее решение (то, что делает Apple) возлагает бремя перевода на Apple, но дает вам ограниченные способы использовать свои собственные слова для описания вещей.
Типы запросов, которые вы можете обрабатывать, определены в доменах и намерениях SiriKit. Намерение - это тип запроса, который может сделать пользователь, например, отправка сообщения контакту или поиск фотографии. Каждое намерение имеет список параметров - например, текстовые сообщения требуют контакта и сообщения.
Домен - это просто группа связанных намерений. Чтение текста и отправка текста находятся в домене сообщений. Бронирование поездки и получение местоположения находятся в домене бронирования поездок. Есть домены для звонков по VoIP, начала тренировок, поиска фотографий и еще нескольких вещей. Документация SiriKit содержит полный список доменов и их намерений ,
Распространенная критика Siri заключается в том, что она, похоже, не в состоянии обрабатывать запросы, а также Google и Alexa, и что сторонняя голосовая экосистема, поддерживаемая конкурентами Apple, богаче.
Я согласен с этой критикой. Если ваше приложение не соответствует текущим намерениям, вы не можете использовать SiriKit, и вы ничего не можете сделать. Даже если ваше приложение подходит, вы не можете контролировать все слова, которые Сири говорит или понимает; поэтому, если у вас есть определенный способ говорить о вещах в вашем приложении, вы не всегда можете научить этому Сири.
Надежда разработчиков iOS заключается в том, что Apple значительно расширит список своих намерений, и что его обработка естественного языка станет намного лучше. Если это так, то у нас будет голосовой помощник, который работает без необходимости делать переводчики или понимать все способы сказать то же самое. А реализовать поддержку структурированных запросов на самом деле довольно просто - намного проще, чем создание синтаксического анализатора на естественном языке.
Еще одним большим преимуществом структуры намерений является то, что она не ограничивается Siri и голосовыми запросами. Даже сейчас приложение «Карты» может генерировать запрос вашего приложения на основе интентов (например, бронирование ресторана). Это делается программно (не голосом или естественным языком). Если бы Apple позволяла приложениям обнаруживать намерения друг друга, у нас был бы гораздо лучший способ совместной работы приложений (в отличие от URL-адреса в стиле x-callback ).
Наконец, поскольку намерение - это структурированный запрос с параметрами, у приложения есть простой способ выразить, что параметры отсутствуют или что ему требуется помощь в различении некоторых параметров. Затем Siri может задать дополнительные вопросы, чтобы определить параметры, и приложение не будет вести беседу.
Домен Ride-Booking
Чтобы понять домены и намерения, давайте посмотрим на домен для бронирования поездок , Это домен, который вы бы использовали, чтобы попросить Siri купить вам автомобиль Lyft.
Apple определяет, как запросить поездку и как получить информацию о ней, но на самом деле нет встроенного приложения Apple, которое могло бы обработать этот запрос. Это один из немногих доменов, где требуется приложение с поддержкой SiriKit.
Вы можете вызвать одно из намерений с помощью голоса или непосредственно из Карт. Некоторые из намерений для этого домена:
- Заказать поездку
Используйте это, чтобы заказать поездку. Вам нужно будет указать место для посадки и высадки, а приложению также может понадобиться узнать размер вашей группы и тип поездки, которую вы хотите. Пример фразы может быть «Закажи мне поездку с <appname>». - Получить статус поездки
Используйте это намерение, чтобы выяснить, был ли ваш запрос получен, и получить информацию о транспортном средстве и водителе, включая их местоположение. Приложение «Карты» использует это намерение для отображения обновленного изображения автомобиля, когда он приближается к вам. - Отменить поездку
Используйте это, чтобы отменить поездку, которую вы забронировали.
Для любого из этих намерений Сири, возможно, нужно знать больше информации. Как вы увидите, когда мы реализуем обработчик намерений, ваше расширение Intents может сообщить Siri, что обязательный параметр отсутствует, и Siri запросит его у пользователя.
Тот факт, что Карты могут вызывать намерения программно, показывает, как намерения могут обеспечить взаимодействие между приложениями в будущем.
Примечание : вы можете получить полный список доменов и их намерений на сайте разработчика Apple. Также есть пример приложения Apple с множеством реализованных доменов и намерений , в том числе поездка-бронирование.
Добавление поддержки списков и заметок в ваше приложение
Хорошо, теперь, когда мы понимаем основы SiriKit, давайте посмотрим, как бы вы добавили поддержку Siri в приложение, которое включает в себя множество настроек и класс для каждого намерения, которое вы хотите обработать.
Остальная часть этой статьи состоит из подробных шагов по добавлению поддержки Siri в приложение. Есть пять высокоуровневых вещей, которые вам нужно сделать:
- Подготовьтесь добавить новое расширение в приложение, создав профили обеспечения с новыми разрешениями для него на веб-сайте Apple для разработчиков.
- Сконфигурируйте ваше приложение (через его список) для использования прав.
- Используйте шаблон Xcode, чтобы начать работу с примером кода.
- Добавьте код, чтобы поддержать ваше намерение Siri.
- Настройте словарный запас Сири с помощью списков.
Не волнуйтесь: мы рассмотрим каждый из них, объяснив расширения и права.
Чтобы сосредоточиться только на деталях Siri, я подготовил простой менеджер списков дел List-o-Mat.
Составление списков в List-o-Mat ( Большой предварительный просмотр )
Вы можете найти полный источник образца, List-o-Mat, на GitHub ,
Чтобы создать его, я только начал с шаблона приложения Xcode Master-Detail и превратил оба экрана в UITableView. Я добавил способ добавления и удаления списков и элементов, а также способ пометить элементы, как сделано. Вся навигация генерируется шаблоном.
Для хранения данных я использовал протокол Codable, ( представлен на WWDC 2017 ), который превращает структуры в JSON и сохраняет его в текстовом файле в папке документов.
Я специально держал код очень просто. Если у вас есть опыт работы со Swift и создания контроллеров представления, то у вас не должно быть проблем с этим.
Теперь мы можем пройти этапы добавления поддержки SiriKit. Шаги высокого уровня будут одинаковыми для любого приложения и любого домена и намерений, которые вы планируете реализовать. В основном мы будем иметь дело с веб-сайтом разработчика Apple, редактировать списки и писать немного о Swift.
Для List-o-Mat мы сосредоточимся на домены списков и заметок , который широко применим к таким вещам, как приложения для заметок и списки дел.
В области списков и заметок у нас есть следующие намерения, которые имеют смысл для нашего приложения.
- Получить список задач.
- Добавить новую задачу в список.
Поскольку взаимодействие с Siri фактически происходит за пределами вашего приложения (возможно, даже если ваше приложение не запущено), iOS использует расширение для реализации этого.
Расширение Intents
Если вы не работали с расширениями, вам нужно знать три основных момента:
- Расширение - это отдельный процесс. Он поставляется внутри пакета вашего приложения, но работает полностью сам по себе, с собственной песочницей.
- Ваше приложение и расширение могут общаться друг с другом, находясь в одной группе приложений. Самый простой способ - через общие папки песочницы группы (так что они могут читать и записывать в одни и те же файлы, если вы их там поместите).
- Для расширений требуются собственные идентификаторы приложений, профили и права.
Чтобы добавить расширение в свое приложение, начните с входа в свой аккаунт разработчика и перейти к разделу «Сертификаты, идентификаторы и профили».
Обновление данных вашей учетной записи Apple Developer App
В нашей учетной записи разработчика Apple первое, что нам нужно сделать, это создать группу приложений. Перейдите в раздел «Группы приложений» в разделе «Идентификаторы» и добавьте один.
Регистрация группы приложений ( Большой предварительный просмотр )
Он должен начинаться с группы, за которой следует ваш обычный обратный идентификатор на основе домена. Поскольку у него есть префикс, вы можете использовать идентификатор вашего приложения для остальных.
Затем нам нужно обновить идентификатор нашего приложения, чтобы использовать эту группу и включить Siri:
- Перейдите в раздел «Идентификаторы приложений» и нажмите на идентификатор вашего приложения;
- Нажмите кнопку «Редактировать»;
- Включить группы приложений (если не включено для другого расширения).
Включить группы приложений ( Большой предварительный просмотр ) - Затем настройте группу приложений, нажав кнопку «Редактировать». Выберите группу приложений из ранее.
Установите название группы приложений ( Большой предварительный просмотр ) - Включить SiriKit.
Включить SiriKit ( Большой предварительный просмотр ) - Нажмите «Готово», чтобы сохранить его.
Теперь нам нужно создать новый идентификатор приложения для нашего расширения:
- В том же разделе «Идентификаторы приложений» добавьте новый идентификатор приложения. Это будет идентификатор вашего приложения с суффиксом. Не используйте только Intents в качестве суффикса, потому что это имя станет именем вашего модуля в Swift и будет конфликтовать с реальным Intents.
Создайте идентификатор приложения для расширения Intents ( Большой предварительный просмотр ) - Включите этот идентификатор приложения для групп приложений (и настройте группу, как мы делали раньше).
Теперь создайте профиль обеспечения разработки для расширения Intents и заново создайте профиль обеспечения вашего приложения. Загрузите и установите их, как вы это обычно делаете.
Теперь, когда наши профили установлены, нам нужно перейти на XCode и обновить права доступа к приложению.
Обновление прав вашего приложения в Xcode
Вернувшись в Xcode, выберите название вашего проекта в навигаторе проекта. Затем выберите основную цель вашего приложения и перейдите на вкладку «Возможности». Там вы увидите переключатель для включения поддержки Siri.
Включите SiriKit в правах вашего приложения. ( Большой предварительный просмотр )
Далее по списку вы можете включить группы приложений и настроить их.
Настройте группу приложений приложения ( Большой предварительный просмотр )
Если вы настроили его правильно, вы увидите это в файле .entitlements вашего приложения:
Плист показывает права, которые вы установили ( Большой предварительный просмотр )
Теперь мы наконец готовы добавить цель расширения Intents в наш проект.
Добавление расширения Intents
Мы наконец готовы добавить расширение. В XCode выберите «Файл» → «Новая цель». Этот лист всплывет:
Добавьте расширение Intents в ваш проект ( Большой предварительный просмотр )
Выберите «Расширение содержимого» и нажмите кнопку «Далее». Заполните следующий экран:
Настройте расширение Intents ( Большой предварительный просмотр )
Название продукта должно соответствовать суффиксу, указанному в идентификаторе приложения намерений на веб-сайте Apple.
Мы решили не добавлять расширение интерфейса намерений. Это не рассматривается в этой статье, но вы можете добавить его позже, если вам это нужно. По сути, это способ внести свой собственный стиль брендинга и отображения в визуальные результаты Siri.
Когда вы закончите, Xcode создаст класс обработчика намерений, который мы можем использовать в качестве начальной части для нашей реализации Siri.
Обработчик содержимого: разрешение, подтверждение и обработка
Xcode сгенерировал новую цель, которая имеет отправную точку для нас.
Первое, что вам нужно сделать, это настроить новую цель в той же группе приложений, что и приложение. Как и раньше, перейдите на вкладку «Возможности» цели, включите группы приложений и настройте их с именем группы. Помните, что у приложений в одной группе есть песочница, которую они могут использовать, чтобы делиться файлами друг с другом. Нам это нужно для того, чтобы запросы Siri попадали в наше приложение.
List-o-Mat имеет функцию, которая возвращает папку с документами группы. Мы должны использовать его всякий раз, когда мы хотим читать или записывать в общий файл.
func documentsFolder () -> URL? {return FileManager.default.containerURL (forSecurityApplicationGroupIdentifier: "group.com.app-o-mat.ListOMat")}
Например, когда мы сохраняем списки, мы используем это:
func save (lists: Lists) {guard let docsDir = documentsFolder () else {fatalError ("no docs dir")} let url = docsDir.appendingPathComponent (fileName, isDirectory: false) // Кодировать списки как JSON и сохранять в URL}
Шаблон расширения Intents создал файл с именем IntentHandler.swift с классом с именем IntentHandler. Он также настроил его как точку входа намерений в списке расширений.
Список расширений намерений настраивает IntentHandler в качестве точки входа
В этом же списке вы увидите раздел, в котором изложены намерения, которые мы поддерживаем. Начнем с того, который позволяет искать списки, который называется INSearchForNotebookItemsIntent. Добавьте его в массив под IntentsSupported.
Добавьте название намерения к списку намерений ( Большой предварительный просмотр )
Теперь перейдите к IntentHandler.swift и замените его содержимое следующим кодом:
import Intents class IntentHandler: INExtension {переопределить обработчик функции (для намерения: INIntent) -> Есть? {switch намерение {регистр INSearchForNotebookItemsIntent: вернуть SearchItemsIntentHandler () по умолчанию: вернуть nil}}}
Функция-обработчик вызывается, чтобы получить объект для обработки определенного намерения. Вы можете просто реализовать все протоколы в этом классе и вернуть self, но мы поместим каждое намерение в свой собственный класс, чтобы лучше организовать его.
Поскольку мы намереваемся иметь несколько разных классов, давайте дадим им общий базовый класс для кода, которым мы должны делиться между ними:
Класс ListOMatIntentsHandler: NSObject {}
Структура намерений требует от нас наследования от NSObject. Мы заполним некоторые методы позже.
Мы начинаем нашу поисковую реализацию с этого:
Класс SearchItemsIntentHandler: ListOMatIntentsHandler, INSearchForNotebookItemsIntentHandling {}
Чтобы установить обработчик намерений, нам нужно реализовать три основных шага
- Разрешите параметры.
Убедитесь, что заданы необходимые параметры, и устраните неоднозначность, если вы не полностью понимаете. - Подтвердите, что запрос выполним.
Это часто необязательно, но даже если вы знаете, что каждый параметр хорош, вам все равно может понадобиться доступ к внешнему ресурсу или другие требования. - Обработайте запрос.
Делай то, что запрашивается.
INSearchForNotebookItemsIntent, первое намерение, которое мы реализуем, может использоваться для поиска задач. Типы запросов, которые мы можем обработать, это: «В List-o-Mat показать список продуктов» или «В List-o-Mat показать список магазинов».
Кроме того: «List-o-Mat» на самом деле является дурным названием для приложения SiriKit, потому что Siri трудно переносить дефисы в приложениях. К счастью, SiriKit позволяет нам иметь альтернативные имена и предоставлять произношение. В Info.plist приложения добавьте этот раздел:
Добавить альтернативные названия приложений и руководства по произношению в список приложений
Это позволяет пользователю произносить «list oh mat» и понимать его как одно слово (без дефисов). На экране это не выглядит идеально, но без этого Сири иногда думает, что «Список» и «Мат» - это отдельные слова, и это приводит их в замешательство.
Решить: выяснить параметры
Для поиска предметов ноутбука есть несколько параметров:
- тип элемента (задача, список задач или заметка),
- название предмета,
- содержание предмета,
- состояние завершения (помечена ли задача как выполненная или нет),
- место, с которым оно связано,
- дата, с которой он связан.
Нам нужны только первые два, поэтому нам нужно написать для них функции разрешения. У INSearchForNotebookItemsIntent есть методы для реализации.
Поскольку мы заботимся только об отображении списков задач, мы жестко закодируем это в разрешении для типа элемента. В SearchItemsIntentHandler добавьте это:
func resolItemType (для намерения: INSearchForNotebookItemsIntent, с завершением: @escaping (INNotebookItemTypeResolutionResult) -> Void) {завершение (.success (with: .taskList))}
Поэтому, что бы ни говорил пользователь, мы будем искать списки задач. Если бы мы хотели расширить нашу поддержку поиска, мы позволили бы Siri попытаться выяснить это из исходной фразы, а затем просто использовать завершение (.needsValue ()), если тип элемента отсутствует. В качестве альтернативы, мы могли бы попытаться угадать из названия, посмотрев, что соответствует ему. В этом случае мы успешно завершим, когда Siri знает, что это такое, и мы будем использовать завершение (.notRequired ()), когда мы собираемся попробовать несколько вариантов.
Разрешение заголовка немного сложнее. Мы хотим, чтобы Siri использовал список, если он найдет список с точным соответствием тому, что вы сказали. Если это неуверенно или есть более чем одна возможность, то мы хотим, чтобы Сири попросила нас помочь разобраться. Для этого SiriKit предоставляет набор перечислений разрешения, которые позволяют нам выразить то, что мы хотим, чтобы произошло дальше.
Так что, если вы скажете «Продуктовый магазин», то у Сири будет точное совпадение. Но если вы скажете «Магазин», то Siri представит меню соответствующих списков.
Мы начнем с этой функции, чтобы дать базовую структуру:
func resolTitle (для намерения: INSearchForNotebookItemsIntent, с завершением: @escaping (INSpeakableStringResolutionResult) -> Void) {guard let title = intent.title else {завершение (.needsValue ()) return} letableLists = getPossibleLame (для: title) complete with: возможных списков, для: заголовок, с: завершение)}
Мы реализуем getPossibleLists (для :) и completeResolveListName (с: для: с :) в базовом классе ListOMatIntentsHandler.
getPossibleLists (для :) нужно попытаться нечетко сопоставить заголовок, который Siri передает нам с фактическими именами списка.
public func getPossibleLists (для listName: INSpeakableString) -> [INSpeakableString] {var возможныйLists = [INSpeakableString] () для l в loadLists () {если l.name.lowercased () == listName.spokenPhrase.lowercased () {return [ INSpeakableString (spokenPhrase: l.name)]}, если l.name.lowercased (). Содержит (listName.spokenPhrase.lowercased ()) || listName.spokenPhrase.lowercased () == "all" {возможныйLists.append (INSpeakableString (spokenPhrase: l.name))}} вернуть возможный список}
Мы перебираем все наши списки. Если мы получим точное совпадение, мы вернем его, а если нет, мы вернем массив возможностей. В этой функции мы просто проверяем, содержится ли слово, сказанное пользователем, в имени списка (так что это довольно простое совпадение). Это позволяет «Grocery» соответствовать «Grocery Store». Более продвинутый алгоритм может попытаться найти совпадение, основываясь на словах, которые звучат одинаково (например, с Алгоритм Soundex ),
completeResolveListName (with: for: with :) отвечает за решение, что делать с этим списком возможностей.
public func completeResolveListName (с возможными списками: [INSpeakableString], для listName: INSpeakableString, с завершением: @escaping (INSpeakableStringResolutionResult) -> Void) {switchableLists.count {случай 0: завершение (.unsupported ()) case 1: если возможно 0] .spokenPhrase.lowercased () == listName.spokenPhrase.lowercased () {завершение (.success (with: возможный список [0]))} else {завершение (.confirmationRequired (with: возможный список [0]))} default: завершение (.disambiguation (with :ableLists))}}
Если мы получили точное совпадение, мы говорим Сири, что нам это удалось. Если мы получили одно неточное совпадение, мы просим Siri спросить пользователя, правильно ли мы угадали.
Если мы получили несколько совпадений, то мы используем завершение (.disambiguation (with: возможный список)), чтобы сообщить Siri, что нужно показать список и позволить пользователю выбрать один.
Теперь, когда мы знаем, что это за запрос, нам нужно посмотреть на все это и убедиться, что мы справимся.
Подтвердите: проверьте все ваши зависимости
В этом случае, если мы разрешили все параметры, мы всегда можем обработать запрос. Типичные реализации verify () могут проверять доступность внешних сервисов или проверять уровни авторизации.
Поскольку подтверждение () является необязательным, мы могли бы просто ничего не делать, и Сири предположил бы, что мы можем обработать любой запрос с разрешенными параметрами. Чтобы быть явным, мы могли бы использовать это:
функция подтверждения (намерение: INSearchForNotebookItemsIntent, завершение: @escaping (INSearchForNotebookItemsIntentResponse) -> Void) {завершение (INSearchForNotebookItemsIntentResponse (код: .success, userActivity: nil))}
Это означает, что мы можем справиться с чем угодно.
Ручка: сделай это
Последний шаг - обработать запрос.
дескриптор функции (intent: INSearchForNotebookItemsIntent, завершение: @escaping (INSearchForNotebookItemsIntentResponse) -> Void) {guard let title = intent.title, let list = loadLists (). filter ({$ 0.name.lowercased () == title.spokenPhrase. lowercased ()}). first else {завершение (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), статус: $ 0.done? INTaskStatus.completed: INTaskStatus.notCompleted, taskType: INTaskType.notCompletable ,atialEventTrigger: nil, temporalEventTrigger: nilponate: nilated: selected: nilatedDateed: создан, ноль: создан: nil, создан: nil, создан: nil, создан: nil, создан: nil: создан: ноль, создан: ноль, создан: ноль, создан: ноль, создан: ноль, создан: ноль, создан: ноль, создан: ноль, создан: ноль, создан: ноль, создан: ноль, создан: ноль, создан: ноль, создан: ноль, создан: ноль, создан: ноль, создан: ниль : "\ (list.name) \ t \ ($ 0.name)")} завершение (ответ)}
Сначала мы находим список на основе заголовка. На данный момент resolTitle уже удостоверился, что мы получим точное совпадение. Но если есть проблема, мы все равно можем вернуть ошибку.
Когда у нас происходит сбой, у нас есть возможность передать активность пользователя. Если ваше приложение использует Раздача и у него есть способ обработать этот точный тип запроса, тогда Сири может попробовать перенести запрос в ваше приложение. Он не будет делать это, когда мы находимся в голосовом контексте (например, вы начали с «Привет, Сири»), и это не гарантирует, что он сделает это в других случаях, поэтому не рассчитывайте на это.
Теперь это готово к тестированию. Выберите расширение намерения в списке целей в XCode. Но прежде чем запустить его, отредактируйте схему.
Отредактируйте схему намерения добавить образец фразы для отладки.
Это вызывает способ предоставить запрос напрямую:
Добавьте образец фразы в раздел «Выполнить» схемы. ( Большой предварительный просмотр )
Обратите внимание, я использую «ListOMat» из-за проблемы дефисов, упомянутых выше. К счастью, оно произносится так же, как имя моего приложения, так что это не должно быть проблемой.
Вернувшись в приложение, я составил список «Продуктовый магазин» и «Магазин бытовой техники». Если я попрошу у Siri список «store», он пойдет по пути устранения неоднозначности, который выглядит следующим образом:
Сири обрабатывает запрос, прося разъяснений. ( Большой предварительный просмотр )
Если вы скажете «Продуктовый магазин», тогда вы получите точное совпадение, которое соответствует результатам.
Добавление предметов через Siri
Теперь, когда мы знаем основные понятия разрешения, подтверждения и обработки, мы можем быстро добавить намерение добавить элемент в список.
Сначала добавьте INAddTasksIntent в список расширений:
Добавьте INAddTasksIntent к списку расширений ( Большой предварительный просмотр )
Затем обновите функцию дескриптора нашего IntentHandler.
переопределить обработчик func (для намерения: INIntent) -> Any? {switch намерение {регистр INSearchForNotebookItemsIntent: возвращать SearchItemsIntentHandler () регистр INAddTasksIntent: возвращать AddItemsIntentHandler () по умолчанию: возвращать ноль}}
Добавьте заглушку для нового класса:
Класс AddItemsIntentHandler: ListOMatIntentsHandler, INAddTasksIntentHandling {}
Для добавления элемента требуется аналогичное разрешение для поиска, за исключением списка целевых задач вместо заголовка.
func resolTargetTaskList (для intent: INAddTasksIntent, с завершением: @escaping (INTaskListResolutionResult) -> Void) {guard let title = intent.targetTaskList? .title else {завершение (.needsValue ()) return} let allowLists = getPible ) completeResolveTaskList (с :ableLists, для: заголовок, с: завершение)}
completeResolveTaskList аналогичен completeResolveListName, но с немного другими типами (список задач вместо заголовка списка задач).
public func completeResolveTaskList (с возможными списками: [INSpeakableString], для listName: INSpeakableString, с завершением: @escaping (INTaskListResolutionResult) -> Void) {let taskLists = возможныйLists.map {вернуть INTaskList (название: $ 0, группа: []: nil, creationDateComponents: nil ,ifiedDateComponents: nil, идентификатор: nil)} switchableLists.count {case 0: завершение (.unsupported ()) case 1: ifableLists [0] .spokenPhrase.lowercased () == listName.spokenPhrase. lowercased () {завершение (.success (with: taskLists [0]))} else {завершение (.confirmationRequired (with: taskLists [0]))} по умолчанию: завершение (.disambiguation (with: taskLists))}}
Он имеет ту же логику устранения неоднозначности и ведет себя точно так же. Слова «Магазин» должны быть неоднозначными, а «Продуктовый магазин» - точное совпадение.
Мы оставим подтверждение невыполненным и примем значение по умолчанию. Для дескриптора нам нужно добавить элемент в список и сохранить его.
дескриптор функции (intent: INAddTasksIntent, завершение: @escaping (INAddTasksIntentResponse) -> Void) {var lists = loadLists () guard let taskList = intent.targetTaskList, let listIndex = lists.index (где: {$ 0.name.lowercased () == taskList.title.spokenPhrase.lowercased ()}), let itemNames = intent.taskTitles, itemNames.count> 0 else {завершение (INAddTasksIntentResponse (код: .failure, userActivity: nil)) return} // Получить список var list = lists [listIndex] // Добавить элементы var addTasks = [INTask] () для элемента в itemNames {list.addItem (name: item.spokenPhrase, at: list.items.count) AddedTasks.append (INTask (title: item, status: .notCompleted, taskType: .notCompletable ,atialEventTrigger: nil, temporalEventTrigger: nil, creationDateComponents: nil ,ifiedDateComponents: nil, identifier: nil))} // Сохранить списки новых списков [listIndex] = сохранить список (списки: списки) // Ответить добавленными элементами let response = INAddTasksIntentResponse (code: .success, userActivity: nil) response.addedTasks = a завершение ddedTasks (ответ)}
Мы получаем список предметов и целевой список. Мы смотрим список и добавляем элементы. Мы также должны подготовить ответ для Siri, чтобы показать с добавленными элементами и отправить его в функцию завершения.
Эта функция может обрабатывать фразу типа «В ListOMat, добавить яблоки в список покупок». Она также может обрабатывать список элементов, таких как «рис, лук и оливки».
Сири добавляет несколько предметов в список продуктового магазина
Почти готово, еще несколько настроек
Все это будет работать на вашем симуляторе или локальном устройстве, но если вы хотите отправить это, вам нужно добавить ключ NSSiriUsageDescription в список свойств вашего приложения со строкой, описывающей, для чего вы используете Siri. Что-то вроде «Ваши запросы о списках будут отправлены в Siri» - это хорошо.
Вы также должны добавить вызов:
INPreferences.requestSiriAuthorization {(status) in}
Поместите это в viewDidLoad контроллера основного представления, чтобы запросить у пользователя доступ к Siri. Это покажет сообщение, которое вы настроили выше, а также сообщит пользователю, что он может использовать Siri для этого приложения.
Устройство запросит разрешение, если вы попытаетесь использовать Siri в приложении.
Наконец, вам нужно сказать Siri, что сказать пользователю, если пользователь спрашивает, что может сделать ваше приложение, предоставив некоторые примеры фраз :
- Создайте файл plist в вашем приложении (не расширение) с именем AppIntentVocabulary.plist.
- Укажите намерения и фразы, которые вы поддерживаете.
Добавьте AppIntentVocabulary.plist, чтобы перечислить примеры фраз, которые вы собираетесь обрабатывать. ( Большой предварительный просмотр )
Сири будет использоваться для намерений, но Apple предоставит несколько примеров для каждого намерения в своей документации. примеры фраз для поиска в списке задач покажите нам, что Сири может понимать «Покажите мне все мои заметки на <appName>», но я нашел другие фразы методом проб и ошибок (например, Сири понимает, что такое «списки», а не только заметки).
Резюме
Как вы можете видеть, добавление поддержки Siri в приложение имеет много шагов, с большой конфигурацией. Но код, необходимый для обработки запросов, был довольно прост.
Есть много шагов, но каждый из них маленький, и вы можете быть знакомы с некоторыми из них, если раньше использовали расширения.
Вот что вам нужно для подготовки нового расширения на веб-сайте Apple для разработчиков:
- Создайте идентификатор приложения для расширения Intents.
- Создайте группу приложений, если у вас ее еще нет.
- Используйте группу приложений в идентификаторе приложения для приложения и расширения.
- Добавьте поддержку Siri к идентификатору приложения.
- Восстановите профили и загрузите их.
А вот шаги в Xcode для создания расширения Inri Siri:
- Добавьте расширение Intents, используя шаблон Xcode.
- Обновите права доступа к приложению и расширению, чтобы они соответствовали профилям (группы и поддержка Siri).
- Добавьте свои намерения в список расширений.
И вам нужно будет добавить код, чтобы сделать следующие вещи:
- Используйте изолированную программную среду группы приложений для связи между приложением и расширением.
- Добавьте классы для поддержки каждого намерения с разрешением, подтверждением и обработкой функций.
- Обновите сгенерированный IntentHandler для использования этих классов.
- Спросите Siri доступ где-нибудь в вашем приложении.
Наконец, есть некоторые специфичные для Siri параметры конфигурации:
- Добавьте строку безопасности поддержки Siri в список приложений.
- Добавьте примеры фраз в файл AppIntentVocabulary.plist в вашем приложении.
- Запустите цель намерения для проверки; отредактируйте схему для предоставления фразы.
Хорошо, это много, но если ваше приложение соответствует одному из доменов Siri, то пользователи ожидают, что они могут взаимодействовать с ним с помощью голоса. А поскольку конкуренция за голосовых помощников настолько хороша, мы можем только ожидать, что WWDC 2018 принесет еще больше доменов и, надеюсь, намного лучше Siri.
Дальнейшее чтение
«Эй, Сири, зачем ты мне нужен?Func documentsFolder () -> URL?
0.done?
Переопределить обработчик func (для намерения: INIntent) -> Any?
TargetTaskList?