App Intents 2.0 e Siri Shortcuts: guia de automação iOS 27

Guia completo de App Intents 2.0 e Siri Shortcuts para iOS 27. Criar streaming responses, conversas multi-turno, View Annotations e integrar com Foundation Models.

App Intents e Siri Shortcuts para automação iOS avançada com Swift e Apple Intelligence

App Intents 2.0 e Siri Shortcuts representam o único caminho para apps de terceiros se integrarem com a Siri no iOS 27. Após a WWDC 2026, a Apple depreciou o SiriKit e posicionou App Intents como o framework obrigatório para interação por voz, descoberta no Spotlight e fluxos de automação.

O que este artigo cobre

Este artigo apresenta a criação completa de App Intents e Siri Shortcuts para iOS 27, desde os conceitos fundamentais até streaming responses, conversas multi-turno e a API View Annotations.

Compreender o framework App Intents 2.0

O framework App Intents, introduzido com iOS 16 e expandido para a versão 2.0 na WWDC 2026, é um framework Swift-nativo e declarativo para construir ações detectáveis pelo sistema. O iOS 27 traz quatro novidades principais: streaming responses para operações de longa duração, follow-ups conversacionais multi-turno, View Annotations para referenciar elementos na tela, e App Schemas para compreensão semântica sem frases de treinamento.

Na WWDC 2026, a Apple depreciou formalmente o SiriKit e estabeleceu App Intents como a única forma pela qual a Siri pode interagir com apps de terceiros. O código SiriKit existente continua compilando com avisos de depreciação, mas a Apple sinalizou uma janela de suporte de dois a três anos antes da remoção.

TaskIntent.swiftswift
import AppIntents

// An AppIntent represents an action users can perform
struct CreateTaskIntent: AppIntent {
    // Title displayed in Shortcuts and Siri
    static var title: LocalizedStringResource = "Create a task"

    // Description for accessibility and suggestions
    static var description = IntentDescription(
        "Creates a new task in the application."
    )

    // Parameter with automatic validation
    @Parameter(title: "Task title")
    var taskTitle: String

    // Optional parameter with default value
    @Parameter(title: "Priority", default: .medium)
    var priority: TaskPriority

    // Action execution with async/await
    func perform() async throws -> some IntentResult & ReturnsValue<TaskEntity> {
        // Create task via service
        let task = TaskService.shared.createTask(
            title: taskTitle,
            priority: priority
        )

        // Return created entity for chaining
        return .result(value: TaskEntity(task: task))
    }
}

O intent declara seus parâmetros via property wrapper @Parameter, permitindo que a Siri solicite valores ausentes conversacionalmente. O método perform() executa a lógica de negócio e retorna um resultado tipado. O sistema de tipos serve como o schema que a Siri AI descobre dinamicamente.

Streaming Responses para ações de longa duração

App Intents 2.0 introduz streaming responses, permitindo que intents reportem progresso durante a execução em vez de bloquear até a conclusão. Essa capacidade aborda cenários como upload de arquivos, sincronização de dados ou cálculos complexos onde o usuário precisa de feedback.

StreamingExportIntent.swiftswift
import AppIntents

struct ExportDataIntent: AppIntent {
    static var title: LocalizedStringResource = "Export data"

    @Parameter(title: "Format")
    var format: ExportFormat

    // Streaming response for progress reporting
    func perform() async throws -> some IntentResult & ProvidesDialog {
        let totalItems = DataService.shared.itemCount
        var processed = 0

        // Stream progress updates to Siri
        for item in DataService.shared.allItems {
            await exportItem(item, format: format)
            processed += 1

            // Report progress at intervals
            if processed % 100 == 0 {
                await reportProgress(
                    "Exported (processed) of (totalItems) items..."
                )
            }
        }

        return .result(
            dialog: "Export complete. (totalItems) items saved as (format.rawValue)."
        )
    }
}

Streaming responses permitem feedback natural durante operações que levam mais de alguns segundos, mantendo o engajamento do usuário em vez de deixá-lo esperando em silêncio.

Follow-ups conversacionais multi-turno

Conversas multi-turno permitem que a Siri faça perguntas clarificadoras e continue a interação dentro de uma única sessão. Essa característica transforma App Intents de comandos de disparo único em fluxos de trabalho conversacionais.

ConversationalTaskIntent.swiftswift
import AppIntents

struct SmartTaskIntent: AppIntent {
    static var title: LocalizedStringResource = "Create smart task"

    @Parameter(title: "Task title")
    var taskTitle: String?

    @Parameter(title: "Priority")
    var priority: TaskPriority?

    @Parameter(title: "Due date")
    var dueDate: Date?

    // Multi-turn conversation flow
    func perform() async throws -> some IntentResult & ProvidesDialog {
        // Request missing parameters conversationally
        let title = try await taskTitle ?? requestValue(
            for: .$taskTitle,
            dialog: "What should the task be called?"
        )

        let taskPriority = try await priority ?? requestValue(
            for: .$priority,
            dialog: "What priority level?"
        )

        // Optional follow-up
        let date: Date?
        if try await requestConfirmation(
            result: .result(dialog: "Should this task have a due date?")
        ) {
            date = try await requestValue(
                for: .$dueDate,
                dialog: "When is it due?"
            )
        } else {
            date = nil
        }

        let task = TaskService.shared.createTask(
            title: title,
            priority: taskPriority,
            dueDate: date
        )

        return .result(
            dialog: "Created '(task.title)' with (taskPriority.rawValue) priority."
        )
    }
}

O método requestValue(for:dialog:) pausa a execução até que a Siri receba a resposta do usuário, criando uma troca natural de ida e volta em vez de exigir todos os parâmetros antecipadamente.

API View Annotations para referências na tela

A API View Annotations, nova no iOS 27, permite que usuários referenciem elementos de UI diretamente em comandos da Siri usando frases como "esta foto", "o terceiro" ou "aquela mensagem". Essa capacidade requer anotar views SwiftUI com informação semântica.

AnnotatedPhotoView.swiftswift
import SwiftUI
import AppIntents

struct PhotoGridView: View {
    let photos: [Photo]

    var body: some View {
        LazyVGrid(columns: [GridItem(.adaptive(minimum: 100))]) {
            ForEach(photos) { photo in
                PhotoThumbnail(photo: photo)
                    // Annotate view for Siri reference
                    .appIntentAnnotation(
                        entity: PhotoEntity(photo: photo),
                        label: photo.title
                    )
            }
        }
    }
}

// Intent that accepts view-referenced entities
struct SharePhotoIntent: AppIntent {
    static var title: LocalizedStringResource = "Share photo"

    // Parameter resolved from on-screen annotation
    @Parameter(title: "Photo", supportsViewAnnotation: true)
    var photo: PhotoEntity

    func perform() async throws -> some IntentResult & ProvidesDialog {
        await ShareService.share(photo.id)
        return .result(dialog: "Photo shared.")
    }
}

Quando um usuário diz "compartilhe esta foto" enquanto visualiza a grade, a Siri resolve "esta" para a PhotoEntity atualmente focada ou tocada por último através da anotação. Isso elimina a fricção de descrever itens verbalmente.

Compatibilidade de dispositivos

View Annotations requer iOS 27, mas a camada App Intents funciona em todos os dispositivos iOS 27 incluindo iPhone 11 e posteriores. Recursos Apple Intelligence como consciência na tela precisam de iPhone 15 Pro ou posterior.

Definir App Entities para os dados

As App Entities representam os "substantivos" do aplicativo: os objetos sobre os quais os intents operam. Permitem que a Siri compreenda, busque e manipule os dados do app. Construir entidades efetivas é essencial para padrões de gerenciamento de estado em SwiftUI em apps orientados a intents.

TaskEntity.swiftswift
import AppIntents

// Internal data model
struct Task: Identifiable, Codable {
    let id: UUID
    var title: String
    var priority: TaskPriority
    var isCompleted: Bool
    var dueDate: Date?
}

// Entity exposed to the system
struct TaskEntity: AppEntity {
    // Required unique identifier
    var id: UUID

    // Displayable properties
    var title: String
    var priority: TaskPriority
    var isCompleted: Bool

    // Display configuration in the system
    static var typeDisplayRepresentation: TypeDisplayRepresentation = "Task"

    // Visual representation of the instance
    var displayRepresentation: DisplayRepresentation {
        DisplayRepresentation(
            title: "(title)",
            subtitle: "(priority.rawValue)",
            image: .init(systemName: isCompleted ? "checkmark.circle.fill" : "circle")
        )
    }

    // Default query for searching entities
    static var defaultQuery = TaskEntityQuery()

    // Initializer from internal model
    init(task: Task) {
        self.id = task.id
        self.title = task.title
        self.priority = task.priority
        self.isCompleted = task.isCompleted
    }
}

// Optimized entity query with search
struct TaskEntityQuery: EntityStringQuery {
    // Text search with service-side filtering
    func entities(matching string: String) async throws -> [TaskEntity] {
        TaskService.shared.search(query: string, limit: 10)
            .map { TaskEntity(task: $0) }
    }

    // Search by identifiers
    func entities(for identifiers: [UUID]) async throws -> [TaskEntity] {
        TaskService.shared.fetchTasks()
            .filter { identifiers.contains($0.id) }
            .map { TaskEntity(task: $0) }
    }

    // Limited suggestions for performance
    func suggestedEntities() async throws -> [TaskEntity] {
        TaskService.shared.fetchRecentTasks(limit: 5)
            .map { TaskEntity(task: $0) }
    }
}

O protocolo EntityStringQuery adiciona capacidade de busca textual, permitindo que a Siri encontre entidades por nome. O método suggestedEntities() alimenta as interfaces da Siri e Atalhos com opções relevantes.

Usar AppEnum para valores fixos

Usar AppEnum para tipos com um conjunto fixo de valores (prioridade, status), e AppEntity para tipos dinâmicos criados pelo usuário (tarefas, notas, contatos). Misturá-los causa complexidade desnecessária.

Criar App Enums para valores fixos

Os App Enums expõem tipos enumerados ao sistema, permitindo que a Siri ofereça opções contextuais com representações visuais.

TaskPriority.swiftswift
import AppIntents

// Enum exposed to the system
enum TaskPriority: String, AppEnum, Codable {
    case low
    case medium
    case high

    // Type name displayed
    static var typeDisplayRepresentation: TypeDisplayRepresentation = "Priority"

    // Representation of each case
    static var caseDisplayRepresentations: [TaskPriority: DisplayRepresentation] = [
        .low: DisplayRepresentation(
            title: "Low",
            image: .init(systemName: "arrow.down.circle")
        ),
        .medium: DisplayRepresentation(
            title: "Medium",
            image: .init(systemName: "minus.circle")
        ),
        .high: DisplayRepresentation(
            title: "High",
            image: .init(systemName: "exclamationmark.circle")
        )
    ]
}

Ícones SF Symbols enriquecem a exibição em Atalhos e nas sugestões da Siri, tornando a seleção mais rápida do que descrições verbais.

Pronto para mandar bem nas entrevistas de iOS?

Pratique com nossos simuladores interativos, flashcards e testes tecnicos.

Implementar AppShortcutsProvider

O AppShortcutsProvider expõe App Shortcuts ao sistema, tornando-os imediatamente disponíveis sem configuração do usuário. Esses atalhos aparecem no Spotlight, Siri, Action Button e nos novos fluxos agênticos da Siri AI.

ShortcutsProvider.swiftswift
import AppIntents

// Provider declaring all app shortcuts
struct TaskAppShortcutsProvider: AppShortcutsProvider {
    // Maximum 10 shortcuts per application
    @AppShortcutsBuilder
    static var appShortcuts: [AppShortcut] {
        // Shortcut to create a task
        AppShortcut(
            intent: CreateTaskIntent(),
            phrases: [
                // The .applicationName placeholder is REQUIRED
                "Create a task with (.applicationName)",
                "New task in (.applicationName)",
                "Add a task to (.applicationName)"
            ],
            shortTitle: "Create Task",
            systemImageName: "plus.circle"
        )

        // Shortcut with dynamic parameter
        AppShortcut(
            intent: CompleteTaskIntent(),
            phrases: [
                "Complete (.$taskName) in (.applicationName)",
                "Mark (.$taskName) as done with (.applicationName)"
            ],
            shortTitle: "Complete Task",
            systemImageName: "checkmark.circle"
        )

        // Shortcut for smart conversational intent
        AppShortcut(
            intent: SmartTaskIntent(),
            phrases: [
                "Quick task (.applicationName)",
                "Remind me (.applicationName)"
            ],
            shortTitle: "Smart Task",
            systemImageName: "brain"
        )
    }
}

As frases de voz devem incluir o placeholder (.applicationName) para que a Siri identifique o app destino. Parâmetros dinâmicos como (.$taskName) permitem comandos contextuais.

Intents em widgets interativos

Os App Intents se integram com WidgetKit para criar widgets interativos. Compreender os padrões de navegação em SwiftUI ajuda ao construir transições de widget para app.

TaskWidgetIntents.swiftswift
import AppIntents
import WidgetKit

// Widget-optimized intent (fast execution)
struct ToggleTaskFromWidgetIntent: AppIntent {
    static var title: LocalizedStringResource = "Toggle task"

    @Parameter(title: "Task ID")
    var taskID: String

    init() {}

    init(taskID: UUID) {
        self.taskID = taskID.uuidString
    }

    // No dialog for widgets
    func perform() async throws -> some IntentResult {
        guard let uuid = UUID(uuidString: taskID) else {
            return .result()
        }

        TaskService.shared.toggleCompletion(taskId: uuid)

        // Immediate widget refresh
        WidgetCenter.shared.reloadTimelines(ofKind: "TaskWidget")

        return .result()
    }
}

// Widget view with interactive button
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.

Integração com Foundation Models Framework

O iOS 27 introduz o Foundation Models framework, habilitando inferência LLM no dispositivo. Embora Foundation Models não tenha integração direta com a Siri, apps podem usá-lo dentro de App Intents para processar linguagem natural ou gerar respostas.

AIAssistedIntent.swiftswift
import AppIntents
import FoundationModels

struct SummarizeNotesIntent: AppIntent {
    static var title: LocalizedStringResource = "Summarize notes"

    @Parameter(title: "Topic")
    var topic: String

    func perform() async throws -> some IntentResult & ProvidesDialog {
        let notes = NoteService.shared.fetchNotes(matching: topic)
        let content = notes.map(.content).joined(separator: "

")

        // Use Foundation Models for on-device summarization
        let model = LanguageModel.default
        let summary = try await model.generate(
            prompt: "Summarize these notes in 2 sentences: (content)",
            maxTokens: 100
        )

        return .result(dialog: IntentDialog(summary))
    }
}

Foundation Models executa aproximadamente 30 tokens por segundo no dispositivo sem custo por requisição. O framework requer iPhone 15 Pro ou iPhone 16 e posterior para processamento no dispositivo.

Migrar de SiriKit para App Intents

Para apps que ainda usam SiriKit, a migração segue um processo estruturado. O Xcode 27 fornece uma ferramenta "Convert to App Intent" para configurações de Widget, mas handlers personalizados requerem reescrita manual.

swift
// Before: SiriKit Intent Handler
class CreateTaskIntentHandler: NSObject, CreateTaskIntentHandling {
    func handle(intent: CreateTaskIntent) async -> CreateTaskIntentResponse {
        guard let title = intent.taskTitle else {
            return CreateTaskIntentResponse(code: .failure, userActivity: nil)
        }

        let task = TaskService.shared.createTask(title: title)
        return CreateTaskIntentResponse.success(task: task)
    }
}

// After: App Intent (iOS 27)
struct CreateTaskIntent: AppIntent {
    static var title: LocalizedStringResource = "Create a task"

    @Parameter(title: "Task title")
    var taskTitle: String

    func perform() async throws -> some IntentResult & ReturnsValue<TaskEntity> {
        let task = TaskService.shared.createTask(title: taskTitle)
        return .result(value: TaskEntity(task: task))
    }
}

A migração elimina arquivos .intentdefinition e targets Intent Extension. Toda a lógica de intents move-se para o target principal do app como structs Swift.

Testar os App Intents

O framework AppIntentsTesting valida intents através da infraestrutura real de Siri, Atalhos e Spotlight sem mocks.

TaskIntentTests.swiftswift
import XCTest
import AppIntentsTesting
@testable import TaskApp

final class TaskIntentTests: XCTestCase {

    override func setUp() {
        super.setUp()
        TaskService.shared.reset()
    }

    func testCreateTaskIntent() async throws {
        // Given
        var intent = CreateTaskIntent()
        intent.taskTitle = "Test Task"
        intent.priority = .high

        // When
        let result = try await intent.perform()

        // Then
        let tasks = TaskService.shared.fetchTasks()
        XCTAssertEqual(tasks.count, 1)
        XCTAssertEqual(tasks.first?.title, "Test Task")
        XCTAssertEqual(tasks.first?.priority, .high)
    }

    func testEntityQuery() async throws {
        // Given
        let task1 = TaskService.shared.createTask(title: "Task 1", priority: .low)
        let task2 = TaskService.shared.createTask(title: "Task 2", priority: .high)

        let query = TaskEntityQuery()

        // When
        let entities = try await query.entities(for: [task1.id, task2.id])

        // Then
        XCTAssertEqual(entities.count, 2)
    }

    func testStreamingIntent() async throws {
        // Given
        var intent = ExportDataIntent()
        intent.format = .json

        // When
        let result = try await intent.perform()

        // Then: verify completion dialog
        XCTAssertNotNil(result)
    }
}

Os testes verificam o comportamento dos intents independentemente da interface do sistema, detectando regressões antes do deploy.

Sources

Checklist de implementação App Intents iOS 27

Para apps que visam iOS 27, App Intents não são mais opcionais. A Siri AI compõe ações de múltiplas etapas através de apps, e apps sem intents publicados ficam excluídos desses fluxos agênticos.

  • 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
  • Adicionar View Annotations às views SwiftUI para referências na tela
  • Implementar streaming responses para operações de longa duração
  • Suportar conversas multi-turno para fluxos de trabalho complexos
  • Migrar do SiriKit antes do fim da janela de depreciação
  • Testar intents com o framework AppIntentsTesting
  • Localizar títulos e descrições

Comece a praticar!

Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.

Desafio do dia

Você saberia encontrar o bug em iOS?

Um trecho real, um bug escondido, uma tentativa por dia. Sem conta para testar.

Anthony Fillion-Maillet

Escrito por

Anthony Fillion-Maillet

Fundador da SharpSkill

Desenvolvedor fullstack há mais de 10 anos. Dirige a SharpSkill e responde por tudo o que é publicado aqui.

Atualizado em 20 de agosto de 2026

Tags

#app-intents
#siri-shortcuts
#ios
#swift
#apple-intelligence

Compartilhar

Artigos relacionados