Розробка кастомних Intents для Siri
Кастомні Intents — наступний рівень після базової інтеграції Siri Shortcuts. Замість простого «відкрий такий-то екран» користувач отримує повноцінний діалог: Siri уточнює параметри, обробляє команду у фоні, повертає результат голосом. Все це працює без відкриття програми через INExtension.
Структура кастомного Intent
Файл .intentdefinition — центральний елемент. Xcode генерує з нього Swift-код: класи Intent, Response, параметри з типами. Структура параметра:
Parameter: date
Type: Date (INDateComponentsResolutionResult)
Display Name: "дата"
Prompt: "На яке число?"
Siri Dialog: "На яке число назначити зустріч?"
Типи параметрів: String, Integer, Boolean, Date, CLPlacemark (геолокація), INPerson (контакт з адресної книги), кастомні INObject-типи для сутностей вашої програми.
Кастомний INObject потрібен, коли параметр — це сутність з вашої бази даних (проект, завдання, трек):
// Автогенеровано з .intentdefinition
class ProjectObject: INObject {
// identifier та displayString обов'язкові
}
// У IntentHandler
func provideProjectOptionsCollection(
for intent: CreateTaskIntent,
with completion: @escaping (INObjectCollection<ProjectObject>?, Error?) -> Void
) {
let projects = ProjectRepository().fetchAll()
let objects = projects.map {
ProjectObject(identifier: $0.id, display: $0.name)
}
completion(INObjectCollection(items: objects), nil)
}
Siri покаже список проектів для вибору — або користувач вимовить назву, або Siri уточнить.
Resolve, Confirm, Handle
Життєвий цикл Intent — три методи:
Resolve — валідація та уточнення кожного параметра:
func resolveTaskName(for intent: CreateTaskIntent,
with completion: @escaping (INStringResolutionResult) -> Void) {
guard let name = intent.taskName, name.count >= 2 else {
completion(.needsValue()) // Siri: "Як назвати завдання?"
return
}
guard name.count <= 255 else {
completion(.unsupported(forReason: .tooLong))
return
}
completion(.success(with: name))
}
Confirm — фінальна перевірка перед виконанням:
func confirm(intent: CreateTaskIntent,
completion: @escaping (CreateTaskIntentResponse) -> Void) {
guard ProjectRepository().exists(id: intent.project?.identifier) else {
let response = CreateTaskIntentResponse(code: .failure, userActivity: nil)
response.failureReason = "Проект не найден"
completion(response)
return
}
completion(CreateTaskIntentResponse(code: .ready, userActivity: nil))
}
Handle — виконання:
func handle(intent: CreateTaskIntent,
completion: @escaping (CreateTaskIntentResponse) -> Void) {
let store = TaskStore(appGroup: "group.com.yourapp")
let task = store.create(
name: intent.taskName!,
projectId: intent.project?.identifier,
dueDate: intent.dueDate?.dateComponents
)
let response = CreateTaskIntentResponse(code: .success, userActivity: nil)
response.task = TaskObject(identifier: task.id, display: task.name)
completion(response)
}
Response-шаблони задаються в .intentdefinition: Siri вимовить «Завдання "$(taskName)" створено в проекті "$(project)"» з підстановкою реальних значень.
Інтеграція з Shortcuts app
INShortcut + INVoiceShortcutCenter дозволяють додати shortcut прямо з програми без переходу в Settings:
func addToSiri(intent: CreateTaskIntent) {
let shortcut = INShortcut(intent: intent)
let vc = INUIAddVoiceShortcutViewController(shortcut: shortcut)
vc.delegate = self
present(vc, animated: true)
}
Кнопка «Додати в Siri» з нативним view — стандартний паттерн для onboarding.
Інтеграція з Widgets через AppIntents (iOS 16+)
З iOS 16 кастомні Intents мігрували на новий фреймворк AppIntents. Старий SiriKit Intents (.intentdefinition) продовжує працювати, але для віджетів конфігурованих через Shortcuts потрібен AppIntent:
struct CreateTaskAppIntent: AppIntent {
static var title: LocalizedStringResource = "Створити завдання"
@Parameter(title: "Назва завдання")
var taskName: String
@Parameter(title: "Проект")
var project: ProjectEntity?
func perform() async throws -> some IntentResult & ProvidesDialog {
let task = try await TaskService().create(name: taskName, project: project?.id)
return .result(dialog: "Створено завдання \(task.name)")
}
}
AppIntents — це майбутнє SiriKit на iOS 16+. Менше boilerplate, підтримка Swift concurrency, працює в віджетах, Spotlight, Shortcuts app.
Типові проблеми
Extension не викликається — найчастіша проблема. Причини: Intent не додан до NSExtension.NSExtensionAttributes.IntentsSupported в Info.plist extension; або Intent додан в основний таргет, але не в extension-таргет.
App Group не пробрасується — Extension та основна програма працюють у різних sandbox. Ділимось даними через App Group: FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.yourapp") або UserDefaults(suiteName: "group.com.yourapp").
Siri не розуміє специфичні терміни — INVocabulary.shared().setVocabularyStrings(projectNames, of: .organizationName) та глобальний AppIntentVocabulary.plist для статичних термінів.
Ориентири по срокам
| Завдання | Срок |
|---|---|
| 1 кастомний Intent (прості параметри) | 1–2 дні |
| Intent з кастомними INObject + resolve діалогом | 2–3 дні |
| Миграція на AppIntents (iOS 16+) + widget конфігурація | 3–5 днів |







