# App Intents e Siri Shortcuts: automação avançada de iOS 2026 > Guia completo de App Intents e Siri Shortcuts para iOS 18+. Criar ações personalizadas para Siri, integrar Apple Intelligence e automatizar o app Swift em 2026. - Published: 2026-03-08 - Updated: 2026-04-29 - Author: SharpSkill - Tags: app-intents, siri-shortcuts, ios, swift, apple-intelligence - Reading time: 15 min --- Em 2026, com Apple Intelligence e o framework App Intents, as aplicações iOS entram numa era em que a intenção do usuário importa mais do que a interface gráfica. Apps que não expõem intents tornam-se invisíveis em um sistema operacional orientado a IA. App Intents é a fundação que permite a Siri, Spotlight, widgets e Action Button interagirem com as funcionalidades do aplicativo. > **O que este artigo cobre** > > Este artigo apresenta a criação completa de App Intents e Siri Shortcuts para iOS 18+, dos conceitos fundamentais até a integração com Apple Intelligence e os App Intent Domains. ## Compreender o framework App Intents O framework App Intents, introduzido com iOS 16, moderniza a construção de intents em Swift, substituindo o antigo SiriKit Intents framework. Essa arquitetura declarativa permite criar ações detectáveis pelo sistema: Spotlight, app Atalhos, Siri e Action Button. ```swift // TaskIntent.swift import AppIntents // Um AppIntent representa uma ação que o usuário pode executar struct CreateTaskIntent: AppIntent { // Título exibido em Atalhos e Siri static var title: LocalizedStringResource = "Criar uma tarefa" // Descrição para acessibilidade e sugestões static var description = IntentDescription( "Cria uma nova tarefa no aplicativo." ) // Parâmetro com validação automática @Parameter(title: "Título da tarefa") var taskTitle: String // Parâmetro opcional com valor padrão @Parameter(title: "Prioridade", default: .medium) var priority: TaskPriority // Execução da ação func perform() async throws -> some IntentResult & ReturnsValue { // Cria a tarefa via serviço let task = TaskService.shared.createTask( title: taskTitle, priority: priority ) // Retorna a entidade criada para encadeamento return .result(value: TaskEntity(task: task)) } } ``` O intent declara seus parâmetros via property wrapper `@Parameter`, permitindo a Siri solicitar valores ausentes. O método `perform()` executa a lógica de negócio e retorna um resultado tipado. ## Definir App Entities para os dados As App Entities representam os "substantivos" do aplicativo: os objetos sobre os quais os intents operam. Permitem a Siri compreender e manipular os dados do app. ```swift // TaskEntity.swift import AppIntents // Modelo de dados interno struct Task: Identifiable, Codable { let id: UUID var title: String var priority: TaskPriority var isCompleted: Bool var dueDate: Date? } // Entidade exposta ao sistema struct TaskEntity: AppEntity { // Identificador único obrigatório var id: UUID // Propriedades exibíveis var title: String var priority: TaskPriority var isCompleted: Bool // Configuração de exibição no sistema static var typeDisplayRepresentation: TypeDisplayRepresentation = "Tarefa" // Representação visual da instância var displayRepresentation: DisplayRepresentation { DisplayRepresentation( title: "\(title)", subtitle: "\(priority.rawValue)", image: .init(systemName: isCompleted ? "checkmark.circle.fill" : "circle") ) } // Query padrão para buscar entidades static var defaultQuery = TaskEntityQuery() // Inicializador a partir do modelo interno init(task: Task) { self.id = task.id self.title = task.title self.priority = task.priority self.isCompleted = task.isCompleted } } // Query para buscar e filtrar entidades struct TaskEntityQuery: EntityQuery { // Busca por identificadores func entities(for identifiers: [UUID]) async throws -> [TaskEntity] { TaskService.shared.fetchTasks() .filter { identifiers.contains($0.id) } .map { TaskEntity(task: $0) } } // Sugestões exibidas na interface func suggestedEntities() async throws -> [TaskEntity] { TaskService.shared.fetchTasks() .filter { !$0.isCompleted } .prefix(5) .map { TaskEntity(task: $0) } } } ``` A `EntityQuery` define como o sistema busca e sugere entidades. Os métodos `entities(for:)` e `suggestedEntities()` alimentam as interfaces do Siri e dos Atalhos. > **App Enums vs App Entities** > > Use `AppEnum` para tipos com um conjunto fixo de valores (prioridade, status), e `AppEntity` para tipos dinâmicos criados pelo usuário (tarefas, notas, contatos). ## Criar App Enums para valores fixos Os App Enums expõem tipos enumerados ao sistema, permitindo que Siri ofereça opções contextuais. ```swift // TaskPriority.swift import AppIntents // Enum exposto ao sistema enum TaskPriority: String, AppEnum, Codable { case low case medium case high // Nome do tipo exibido static var typeDisplayRepresentation: TypeDisplayRepresentation = "Prioridade" // Representação de cada caso static var caseDisplayRepresentations: [TaskPriority: DisplayRepresentation] = [ .low: DisplayRepresentation( title: "Baixa", image: .init(systemName: "arrow.down.circle") ), .medium: DisplayRepresentation( title: "Média", image: .init(systemName: "minus.circle") ), .high: DisplayRepresentation( title: "Alta", image: .init(systemName: "exclamationmark.circle") ) ] } // Enum para o status da tarefa enum TaskStatus: String, AppEnum, Codable { case pending case inProgress case completed static var typeDisplayRepresentation: TypeDisplayRepresentation = "Status" static var caseDisplayRepresentations: [TaskStatus: DisplayRepresentation] = [ .pending: "Pendente", .inProgress: "Em andamento", .completed: "Concluída" ] } ``` As representações visuais (ícones SF Symbols) enriquecem a exibição em Atalhos e nas sugestões da Siri. ## Implementar AppShortcutsProvider O `AppShortcutsProvider` expõe os App Shortcuts ao sistema, deixando-os disponíveis imediatamente sem configuração do usuário. Esses atalhos aparecem em Spotlight, Siri e Action Button. ```swift // ShortcutsProvider.swift import AppIntents // Provider que declara todos os atalhos do app struct TaskAppShortcutsProvider: AppShortcutsProvider { // Máximo de 10 atalhos por aplicativo @AppShortcutsBuilder static var appShortcuts: [AppShortcut] { // Atalho para criar uma tarefa AppShortcut( intent: CreateTaskIntent(), phrases: [ // O placeholder .applicationName é OBRIGATÓRIO "Criar uma tarefa com \(.applicationName)", "Nova tarefa em \(.applicationName)", "Adicionar uma tarefa ao \(.applicationName)" ], shortTitle: "Criar tarefa", systemImageName: "plus.circle" ) // Atalho para listar tarefas AppShortcut( intent: ListTasksIntent(), phrases: [ "Mostrar minhas tarefas em \(.applicationName)", "Exibir tarefas do \(.applicationName)", "Quais são minhas tarefas \(.applicationName)" ], shortTitle: "Minhas tarefas", systemImageName: "list.bullet" ) // Atalho com parâmetro dinâmico AppShortcut( intent: CompleteTaskIntent(), phrases: [ "Concluir \(\.$taskName) em \(.applicationName)", "Marcar \(\.$taskName) como feita com \(.applicationName)" ], shortTitle: "Concluir tarefa", systemImageName: "checkmark.circle" ) } } ``` As frases de voz devem incluir o placeholder `\(.applicationName)` para que Siri identifique o app de destino. Parâmetros dinâmicos como `\(\.$taskName)` permitem comandos contextuais. > **Limite de 10 Shortcuts** > > Um aplicativo pode declarar no máximo 10 App Shortcuts. Convém priorizar as ações mais frequentes e úteis para os usuários. ## Intents com parâmetros complexos Intents podem aceitar parâmetros complexos, incluindo entidades e opções de configuração. ```swift // CompleteTaskIntent.swift import AppIntents struct CompleteTaskIntent: AppIntent { static var title: LocalizedStringResource = "Concluir uma tarefa" static var description = IntentDescription( "Marca uma tarefa como concluída." ) // Parâmetro tipo entidade com busca automática @Parameter(title: "Tarefa") var task: TaskEntity // Parâmetro opcional com data @Parameter(title: "Data de conclusão") var completionDate: Date? // Configuração do diálogo Siri static var parameterSummary: some ParameterSummary { Summary("Concluir \(\.$task)") { \.$completionDate } } func perform() async throws -> some IntentResult & ProvidesDialog { // Atualiza a tarefa TaskService.shared.completeTask( id: task.id, completionDate: completionDate ?? Date() ) // Feedback de voz para Siri return .result( dialog: "A tarefa \(task.title) foi marcada como concluída." ) } } // Intent retornando uma lista de entidades struct ListTasksIntent: AppIntent { static var title: LocalizedStringResource = "Listar tarefas" // Filtro opcional @Parameter(title: "Status", default: nil) var statusFilter: TaskStatus? @Parameter(title: "Prioridade", default: nil) var priorityFilter: TaskPriority? func perform() async throws -> some IntentResult & ReturnsValue<[TaskEntity]> { var tasks = TaskService.shared.fetchTasks() // Aplicar filtros if let status = statusFilter { tasks = tasks.filter { switch status { case .completed: return $0.isCompleted case .pending, .inProgress: return !$0.isCompleted } } } if let priority = priorityFilter { tasks = tasks.filter { $0.priority == priority } } let entities = tasks.map { TaskEntity(task: $0) } return .result(value: entities) } } ``` O `ParameterSummary` define como Siri apresenta o intent durante a execução por voz, fornecendo um feedback natural. ## Integração com Apple Intelligence iOS 18 introduz os App Intent Domains, coleções de APIs projetadas para funcionalidades específicas. Esses domínios permitem a Apple Intelligence compreender e executar ações com maior precisão. ```swift // BookmarkDomainIntents.swift import AppIntents // Conformidade ao domínio Bookmarks para integração Apple Intelligence struct SaveBookmarkIntent: AppIntent { static var title: LocalizedStringResource = "Salvar um favorito" // Parâmetro URL com validação automática @Parameter(title: "URL") var url: URL @Parameter(title: "Título", default: nil) var title: String? @Parameter(title: "Pasta", default: nil) var folder: BookmarkFolderEntity? // Abrir o app se necessário static var openAppWhenRun: Bool = false func perform() async throws -> some IntentResult & ProvidesDialog { let bookmark = BookmarkService.shared.save( url: url, title: title, folder: folder?.id ) return .result( dialog: "Favorito salvo: \(bookmark.title)" ) } } // Intent com consciência do conteúdo da tela (iOS 18.4+) struct AnalyzeScreenContentIntent: AppIntent { static var title: LocalizedStringResource = "Analisar conteúdo" // Acesso ao contexto da tela via Apple Intelligence @Parameter(title: "Contexto") var screenContext: String? func perform() async throws -> some IntentResult & ProvidesDialog { guard let context = screenContext else { return .result(dialog: "Nenhum conteúdo para analisar.") } // Processar conteúdo extraído pela Apple Intelligence let analysis = ContentAnalyzer.analyze(context) return .result(dialog: analysis.summary) } } ``` Os domínios predefinidos (Books, Camera, Spreadsheets) permitem a Siri responder às solicitações com precisão graças a modelos treinados nessas tarefas específicas. ## Ações Siri com confirmação do usuário Para ações sensíveis, o sistema pode solicitar confirmação antes da execução. ```swift // DeleteTaskIntent.swift import AppIntents struct DeleteTaskIntent: AppIntent { static var title: LocalizedStringResource = "Excluir uma tarefa" @Parameter(title: "Tarefa") var task: TaskEntity // Requer confirmação do usuário static var isDiscoverable: Bool = true func perform() async throws -> some IntentResult & ProvidesDialog { // Solicita confirmação via diálogo try await requestConfirmation( result: .result( dialog: "Tem certeza que deseja excluir \(task.title)?" ) ) // Exclui após confirmação TaskService.shared.deleteTask(id: task.id) return .result( dialog: "A tarefa \(task.title) foi excluída." ) } } // Intent com várias etapas de diálogo struct ScheduleTaskIntent: AppIntent { static var title: LocalizedStringResource = "Agendar uma tarefa" @Parameter(title: "Tarefa") var task: TaskEntity @Parameter(title: "Data") var scheduledDate: Date @Parameter(title: "Lembrete", default: true) var setReminder: Bool static var parameterSummary: some ParameterSummary { When(\.$setReminder, .equalTo, true) { Summary("Agendar \(\.$task) para \(\.$scheduledDate) com lembrete") } otherwise: { Summary("Agendar \(\.$task) para \(\.$scheduledDate)") } } func perform() async throws -> some IntentResult & ProvidesDialog { TaskService.shared.schedule( taskId: task.id, date: scheduledDate, reminder: setReminder ) let dateFormatter = DateFormatter() dateFormatter.dateStyle = .medium dateFormatter.timeStyle = .short let formattedDate = dateFormatter.string(from: scheduledDate) return .result( dialog: "Tarefa agendada para \(formattedDate)." ) } } ``` O método `requestConfirmation` pausa a execução até a validação do usuário, protegendo contra ações acidentais. ## Intents em widgets interativos Os App Intents se integram naturalmente com WidgetKit para criar widgets interativos no iOS 17+. ```swift // TaskWidgetIntents.swift import AppIntents import WidgetKit // Intent otimizado para widget (execução rápida) struct ToggleTaskFromWidgetIntent: AppIntent { static var title: LocalizedStringResource = "Alternar tarefa" @Parameter(title: "ID da tarefa") var taskID: String init() {} init(taskID: UUID) { self.taskID = taskID.uuidString } // Sem diálogo para widgets func perform() async throws -> some IntentResult { guard let uuid = UUID(uuidString: taskID) else { return .result() } TaskService.shared.toggleCompletion(taskId: uuid) // Recarga imediata do widget WidgetCenter.shared.reloadTimelines(ofKind: "TaskWidget") return .result() } } // View do widget com botão interativo import SwiftUI struct TaskWidgetView: View { let task: Task var body: some View { Button(intent: ToggleTaskFromWidgetIntent(taskID: task.id)) { HStack { Image(systemName: task.isCompleted ? "checkmark.circle.fill" : "circle") .foregroundStyle(task.isCompleted ? .green : .secondary) Text(task.title) .strikethrough(task.isCompleted) } .padding() } .buttonStyle(.plain) } } ``` Os widgets usam a sintaxe `Button(intent:)` para conectar a interação diretamente ao App Intent, sem abrir o aplicativo. ## Configuração do Action Button O Action Button do iPhone 15 Pro e posteriores pode disparar App Shortcuts diretamente. ```swift // ActionButtonIntent.swift import AppIntents // Intent otimizado para Action Button struct QuickCaptureIntent: AppIntent { static var title: LocalizedStringResource = "Captura rápida" static var description = IntentDescription( "Cria rapidamente uma tarefa com um título." ) // Abre o app para entrada static var openAppWhenRun: Bool = true func perform() async throws -> some IntentResult & OpensIntent { // Notificação para abrir a tela de captura rápida NotificationCenter.default.post( name: .quickCaptureTriggered, object: nil ) return .result(opensIntent: ShowQuickCaptureViewIntent()) } } // Declarar em AppShortcutsProvider para Action Button extension TaskAppShortcutsProvider { @AppShortcutsBuilder static var appShortcuts: [AppShortcut] { // ... outros atalhos AppShortcut( intent: QuickCaptureIntent(), phrases: [ "Captura rápida \(.applicationName)", "Nota rápida \(.applicationName)" ], shortTitle: "Capturar", systemImageName: "bolt.circle" ) } } ``` Os usuários podem configurar o Action Button para disparar esse atalho via Ajustes > Botão de Ação. > **Macros iOS 18+** > > iOS 18 introduz os macros `@DeferredProperty` e `@ComputedProperty` para reduzir o boilerplate. App Intents também podem residir em Swift Packages para reuso multiplataforma. ## Intents em Swift Packages Os App Intents podem ser definidos em Swift Packages para compartilhamento entre iOS, macOS e watchOS. ```swift // Package.swift // swift-tools-version: 5.9 import PackageDescription let package = Package( name: "TaskIntents", platforms: [ .iOS(.v17), .macOS(.v14), .watchOS(.v10) ], products: [ .library(name: "TaskIntents", targets: ["TaskIntents"]) ], dependencies: [], targets: [ .target( name: "TaskIntents", dependencies: [] ) ] ) // Sources/TaskIntents/SharedIntents.swift import AppIntents // Intent compartilhado multiplataforma public struct SharedCreateTaskIntent: AppIntent { public static var title: LocalizedStringResource = "Criar uma tarefa" @Parameter(title: "Título") public var taskTitle: String public init() {} public func perform() async throws -> some IntentResult { // Lógica compartilhada await TaskRepository.shared.create(title: taskTitle) return .result() } } // Extensão específica de plataforma no app #if os(iOS) extension SharedCreateTaskIntent { // Comportamento específico iOS static var openAppWhenRun: Bool = false } #endif ``` Essa arquitetura mantém um único código para os intents, adaptando o comportamento por plataforma. ## Testar os App Intents Os App Intents podem ser testados de forma unitária como qualquer código Swift. ```swift // TaskIntentTests.swift import XCTest import AppIntents @testable import TaskApp final class TaskIntentTests: XCTestCase { override func setUp() { super.setUp() // Resetar serviço para testes isolados TaskService.shared.reset() } func testCreateTaskIntent() async throws { // Given var intent = CreateTaskIntent() intent.taskTitle = "Tarefa de teste" intent.priority = .high // When let result = try await intent.perform() // Then let tasks = TaskService.shared.fetchTasks() XCTAssertEqual(tasks.count, 1) XCTAssertEqual(tasks.first?.title, "Tarefa de teste") XCTAssertEqual(tasks.first?.priority, .high) } func testCompleteTaskIntent() async throws { // Given let task = TaskService.shared.createTask( title: "Tarefa para concluir", priority: .medium ) var intent = CompleteTaskIntent() intent.task = TaskEntity(task: task) // When _ = try await intent.perform() // Then let updatedTask = TaskService.shared.fetchTask(id: task.id) XCTAssertTrue(updatedTask?.isCompleted ?? false) } func testEntityQuery() async throws { // Given let task1 = TaskService.shared.createTask(title: "Tarefa 1", priority: .low) let task2 = TaskService.shared.createTask(title: "Tarefa 2", priority: .high) let query = TaskEntityQuery() // When let entities = try await query.entities(for: [task1.id, task2.id]) // Then XCTAssertEqual(entities.count, 2) } } ``` Os testes verificam o comportamento dos intents independentemente da interface do sistema. ## Boas práticas e otimizações Vários padrões garantem App Intents performáticos e uma experiência ótima para o usuário. ```swift // BestPractices.swift import AppIntents // 1. Frases localizadas struct LocalizedTaskIntent: AppIntent { static var title: LocalizedStringResource = "task.create.title" static var description = IntentDescription( "task.create.description", categoryName: "task.category" ) @Parameter(title: "task.parameter.title") var taskTitle: String func perform() async throws -> some IntentResult { // ... return .result() } } // 2. Tratamento adequado de erros enum TaskIntentError: Error, CustomLocalizedStringResourceConvertible { case taskNotFound case invalidInput case serviceUnavailable var localizedStringResource: LocalizedStringResource { switch self { case .taskNotFound: return "Tarefa não encontrada." case .invalidInput: return "Dados inválidos." case .serviceUnavailable: return "Serviço temporariamente indisponível." } } } struct RobustTaskIntent: AppIntent { static var title: LocalizedStringResource = "Intent robusto" @Parameter(title: "ID") var taskId: String func perform() async throws -> some IntentResult & ProvidesDialog { guard let uuid = UUID(uuidString: taskId) else { throw TaskIntentError.invalidInput } guard let task = TaskService.shared.fetchTask(id: uuid) else { throw TaskIntentError.taskNotFound } return .result(dialog: "Tarefa encontrada: \(task.title)") } } // 3. Otimização de EntityQuery struct OptimizedTaskQuery: EntityStringQuery { // Busca textual otimizada func entities(matching string: String) async throws -> [TaskEntity] { // Busca no serviço com limite TaskService.shared.search(query: string, limit: 10) .map { TaskEntity(task: $0) } } // Sugestões limitadas para performance func suggestedEntities() async throws -> [TaskEntity] { TaskService.shared.fetchRecentTasks(limit: 5) .map { TaskEntity(task: $0) } } } // 4. Focus Filter para os modos de Foco struct TaskFocusFilter: SetFocusFilterIntent { static var title: LocalizedStringResource = "Filtrar tarefas" @Parameter(title: "Mostrar apenas prioridade alta") var showHighPriorityOnly: Bool func perform() async throws -> some IntentResult { TaskService.shared.setFocusFilter(highPriorityOnly: showHighPriorityOnly) return .result() } } ``` Esses padrões garantem uma integração fluida com o sistema preservando uma performance ótima. ## Conclusão App Intents e Siri Shortcuts transformam a forma como os usuários interagem com aplicações iOS. Em 2026, com Apple Intelligence, expor intents deixou de ser opcional e tornou-se essencial para entregar uma experiência moderna e intuitiva. ### Checklist App Intents iOS 18+ - ✅ Criar AppIntents para as ações principais do app - ✅ Definir AppEntities para os dados manipuláveis - ✅ Usar AppEnum para os tipos enumerados - ✅ Implementar AppShortcutsProvider com frases de voz - ✅ Respeitar o limite máximo de 10 App Shortcuts - ✅ Incluir `\(.applicationName)` em todas as frases - ✅ Configurar ParameterSummary para feedback Siri - ✅ Integrar domínios Apple Intelligence quando aplicável - ✅ Testar os intents com testes unitários - ✅ Localizar títulos e descrições --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/pt/blog/ios/app-intents-siri-shortcuts-advanced-ios-automation