App Intents 2.0 no iOS 26: Guia Completo de Interactive Snippets e Visual Intelligence

O guia completo de App Intents 2.0 no iOS 26: Interactive Snippets em SwiftUI, View Annotations, Deferred Properties e o novo framework de testes com Swift 6.2.

App Intents 2 iOS 26: Guia Completo 2026

Atualizado: 24 de junho de 2026

O App Intents 2.0, lançado com o iOS 26, é a evolução do framework que expõe ações e conteúdo do seu app para Siri, Spotlight, Visual Intelligence e Apple Intelligence. Desta vez vem com Interactive Snippets em SwiftUI, propriedades adiadas, anotações de entidades em telas e um framework de testes dedicado. Na prática, ele substitui a herança do SiriKit (que ainda carrega vestígios da era Objective-C) por uma API Swift-nativa que funciona como o contrato entre o seu app e toda a camada de IA da Apple.

  • O App Intents 2.0 introduz Interactive Snippets: views SwiftUI retornadas como resultado de uma intent, com botões e ações dentro do Spotlight, Siri e Visual Intelligence.
  • As novas View Annotations permitem mapear views da sua UI para AppEntitys, dando ao sistema consciência do que está visível na tela.
  • Deferred Properties deixam você carregar propriedades pesadas de entidades sob demanda, evitando o custo de hidratar tudo no construtor.
  • O App Intents Testing framework valida Siri, Shortcuts e Spotlight via caminhos reais do sistema, sem precisar de UI automation.
  • Apple Intelligence requer iPhone 15 Pro ou superior; intents continuam funcionando em hardware mais antigo, mas perdem o tratamento conversacional.
  • Adotar schemas de intent semânticos é o que coloca seu conteúdo no Spotlight semantic index e o torna referenciável por linguagem natural.

O que é o App Intents 2.0 no iOS 26?

O App Intents 2.0 é a versão do framework que acompanha o iOS 26, iPadOS 26, macOS 26 e watchOS 26. Tecnicamente, ele continua sendo uma API Swift puramente declarativa em que cada ação do seu app é uma struct que conforma AppIntent. Só que a superfície cresceu o suficiente para merecer a versão maior. Em vez de só "rodar um botão por voz", agora a intent é o ponto de integração entre o seu app e quatro sistemas diferentes da Apple: Siri (com Apple Intelligence), Shortcuts, Spotlight semantic index e Visual Intelligence.

Lembro de quando ainda escrevíamos extensions INExtension em Objective-C para responder a domínios fechados do SiriKit: lista de tarefas, mensagens, treinos. Cada novo verbo exigia um schema da Apple. O App Intents quebrou essa rigidez ainda em 2022. O que mudou agora em 2026 é a profundidade. O que você expõe como intent não é mais "uma ação para o usuário invocar de propósito", é parte do contexto que o modelo de linguagem on-device usa para responder perguntas e antecipar próximos passos.

As três adições centrais do iOS 26 que você precisa internalizar são: Interactive Snippets (resultado da intent renderizado como uma view SwiftUI viva), View Annotations (mapeamento entre views da sua UI e entidades para que o sistema entenda o que está na tela) e Deferred Properties em AppEntity (carregamento sob demanda de campos caros). Há também um novo framework de testes chamado AppIntentsTesting, que finalmente permite escrever testes de integração reais.

Como criar seu primeiro App Intent em Swift 6.2

Vamos começar pelo essencial. Um App Intent é uma struct com um título, uma descrição e um método perform() assíncrono. Em 2026, com Swift 6.2 e concorrência aproximável, o boilerplate caiu bastante. Honestamente, é uma diferença grande em relação ao que escrevíamos só dois anos atrás. Veja uma intent que marca um livro como lido em um app de leitura:

import AppIntents

struct MarkBookAsReadIntent: AppIntent {
    static let title: LocalizedStringResource = "Marcar livro como lido"
    static let description = IntentDescription(
        "Marca o livro especificado como concluído e atualiza o progresso de leitura."
    )

    @Parameter(title: "Livro")
    var book: BookEntity

    @MainActor
    func perform() async throws -> some IntentResult & ProvidesDialog {
        let library = LibraryStore.shared
        try await library.markRead(book.id)
        return .result(dialog: "Pronto, \(book.title) foi marcado como lido.")
    }
}

Três pontos que costumo enfatizar em revisão de código:

  • Nomes semânticos. book e não p1. O modelo de linguagem do Apple Intelligence usa o nome do parâmetro como pista para casar a frase do usuário com a intent. Nomes ruins viram intents que nunca disparam.
  • IntentDescription é parte da API. Não é documentação interna, é metadata indexada. Descreva o efeito colateral, não o nome do método.
  • Tipo de retorno composto. some IntentResult & ProvidesDialog permite que a Siri leia uma resposta natural sem você precisar abrir o app.

Para expor a intent à Spotlight e ao Shortcuts, você só precisa adicionar uma AppShortcutsProvider. Não há mais arquivos .intentdefinition, não há mais Intent Extensions separados para a maioria dos casos; tudo vive no target principal.

struct ReaderShortcuts: AppShortcutsProvider {
    static var appShortcuts: [AppShortcut] {
        AppShortcut(
            intent: MarkBookAsReadIntent(),
            phrases: [
                "Marcar livro como lido no \(.applicationName)",
                "Concluir leitura no \(.applicationName)"
            ],
            shortTitle: "Marcar como lido",
            systemImageName: "checkmark.circle.fill"
        )
    }
}

Compile, rode no iOS 26 e a intent já aparece em Atalhos, Spotlight (digite "marcar livro") e Siri. Se você precisa de uma base sólida em concorrência antes de mexer com @MainActor, vale a leitura do nosso guia de concorrência acessível no Swift 6.2.

Interactive Snippets: respostas SwiftUI dentro do sistema

Esta é a novidade mais visível do App Intents 2.0. Antes, o resultado de uma intent era um diálogo de texto ou, no máximo, uma snippet view estática. No iOS 26, você pode retornar uma view SwiftUI interativa (com botões, sliders, animações) que vive dentro da bolha do Spotlight, do painel da Siri ou da overlay de Visual Intelligence. O usuário interage com a sua UI sem abrir o app.

Para construir uma Interactive Snippet, você cria uma struct que conforma SnippetView e a retorna do perform() via ShowsSnippetView:

struct BookProgressSnippet: SnippetView {
    let book: BookEntity

    var body: some View {
        HStack(spacing: 12) {
            AsyncImage(url: book.coverURL) { $0.resizable() } placeholder: { Color.gray }
                .frame(width: 56, height: 80)
                .clipShape(.rect(cornerRadius: 6))

            VStack(alignment: .leading, spacing: 6) {
                Text(book.title).font(.headline)
                ProgressView(value: book.progress)
                Button(intent: MarkBookAsReadIntent(book: book)) {
                    Label("Marcar como lido", systemImage: "checkmark")
                }
                .buttonStyle(.borderedProminent)
            }
        }
        .padding()
    }
}

struct ShowBookProgressIntent: AppIntent {
    static let title: LocalizedStringResource = "Mostrar progresso do livro"

    @Parameter(title: "Livro") var book: BookEntity

    func perform() async throws -> some IntentResult & ShowsSnippetView {
        .result(view: BookProgressSnippet(book: book))
    }
}

Repare em duas coisas. Primeiro, Button(intent:) dispara outra intent diretamente da snippet, então você consegue encadear fluxos inteiros sem nunca trazer o app para foreground. Segundo, a view roda em um processo do sistema, então recursos como @AppStorage e SwiftData precisam estar acessíveis via App Group ou via uma intent intermediária.

Visual Intelligence e a View Annotations API

A Visual Intelligence é a feature que liga o que está visível na tela (ou na câmera) ao Apple Intelligence. Em apps de terceiros, ela vive atrás da nova View Annotations API: você anota as views da sua UI declarando qual AppEntity cada uma representa, e o sistema passa a entender "este card aqui é o livro Mil Brilhantes Sóis".

struct BookCardView: View {
    let book: BookEntity

    var body: some View {
        VStack(alignment: .leading) {
            Text(book.title).font(.title3.bold())
            Text(book.author).font(.subheadline).foregroundStyle(.secondary)
        }
        .padding()
        .background(.thinMaterial, in: .rect(cornerRadius: 12))
        .appEntityAnnotation(book)
    }
}

Com essa simples linha (.appEntityAnnotation(book)), o usuário pode dizer "lê em voz alta este aqui" ou "adiciona este à minha lista de desejos" enquanto a célula está visível, e a Siri resolve "este aqui" para a entidade específica. Internamente, isso usa o mesmo grafo semântico que alimenta o Spotlight: entidades que você anota viram contexto para o modelo on-device.

Se você está construindo experiências mais ricas com IA local, vale combinar isso com o nosso guia de Foundation Models no iOS 26, que cobre como rodar o modelo da Apple direto no dispositivo. E se a sua UI ainda está no design pré-iOS 26, dê uma passada antes pelo guia de Liquid Glass no SwiftUI, porque as anotações ficam mais coerentes em views que já usam o novo material.

AppEntity, Deferred Properties e Spotlight Semantic Index

Uma AppEntity é a representação canônica de um pedaço de conteúdo do seu app: um livro, uma nota, um treino. No App Intents 2.0, entidades ganham dois recursos importantes: view annotations (acima) e deferred properties, que carregam campos caros sob demanda.

struct BookEntity: AppEntity {
    static var typeDisplayRepresentation: TypeDisplayRepresentation = "Livro"
    static var defaultQuery = BookQuery()

    var id: UUID
    var title: String
    var author: String

    @DeferredProperty
    var fullSynopsis: String

    @DeferredProperty
    var coverImage: IntentFile

    var displayRepresentation: DisplayRepresentation {
        DisplayRepresentation(title: "\(title)", subtitle: "\(author)")
    }
}

O sistema só hidrata fullSynopsis e coverImage quando alguém explicitamente as pede, como uma snippet que mostra a capa, por exemplo. Isso resolve um problema clássico: até o iOS 25, hidratar 200 livros para indexação no Spotlight significava carregar 200 capas. Agora a query inicial é leve, e o detalhamento é pago caso a caso.

O outro lado da moeda é o Spotlight semantic index. Toda AppEntity que você expõe via EntityQuery entra em um índice on-device de embeddings. Isso significa que buscas como "aquele livro sobre o cara que perde a memória" passam a casar com a sua entidade mesmo sem nenhuma palavra-chave exata. É a parte da Apple Intelligence que mais surpreende usuários, e a que mais exige cuidado com displayRepresentation, porque o que você escreve ali vira o "rótulo" do embedding.

Como testar App Intents no Xcode 26

Por anos, testar App Intents foi um dos pontos mais dolorosos da plataforma. Você acabava simulando invocações via Shortcuts e validando logs. O Xcode 26 traz o framework AppIntentsTesting, que finalmente permite testes de integração reais. Ele se integra naturalmente ao Swift Testing:

import Testing
import AppIntentsTesting
@testable import Reader

@Test func marcarLivroComoLidoAtualizaProgresso() async throws {
    let store = LibraryStore.inMemory(seed: [.milBrilhantes])
    let book = BookEntity(id: .milBrilhantes, title: "Mil Brilhantes Sóis", author: "Hosseini")

    let intent = MarkBookAsReadIntent()
    intent.book = book

    let result = try await intent.runAsTest(in: .init(store: store))

    #expect(result.dialog?.localizedString.contains("Mil Brilhantes") == true)
    #expect(store.book(.milBrilhantes)?.progress == 1.0)
}

O método runAsTest(in:) exercita a intent pelos mesmos caminhos que o sistema usa em produção: resolução de parâmetros, autorização, ciclo de vida da snippet. Isso pega regressões que testes unitários puros sobre perform() simplesmente não pegavam, particularmente em entidades com EntityQuery assíncrona.

Recomendo configurar a injeção de dependências do app de forma que perform() nunca leia singletons diretamente. Use um @Dependency ou um environment value, e injete o store na fixture de teste. É a mesma disciplina que recomendo para views observadas com @Observable no SwiftUI. Testabilidade nasce na fronteira.

Migrando de SiriKit e do App Intents 1.x

Se você ainda tem código de SiriKit (aqueles INIntent e INExtension herdados do Objective-C), 2026 é o ano de encerrar. A Apple não removeu o SiriKit, mas Apple Intelligence não conversa com ele. Apps presos em SiriKit ficam fora do Spotlight semântico, da Visual Intelligence e dos Interactive Snippets. O caminho de migração é razoavelmente mecânico:

  1. Identifique cada INIntent antigo e crie um AppIntent equivalente com parâmetros tipados.
  2. Converta INObjects em AppEntitys, levando suas propriedades pesadas para @DeferredProperty.
  3. Remova o Intent Extension. A maioria das intents agora roda direto no app process; só feature-specific extensions (Widgets, Live Activities) precisam continuar separadas.
  4. Adote schemas semânticos do App Intents. Categorias como MailIntent, FitnessIntent ou JournalingIntent dão ao Apple Intelligence o vocabulário que ele já conhece, reduzindo a quantidade de frases de exemplo que você precisa fornecer.
  5. Para apps que já estavam em App Intents 1.x, o trabalho principal é adotar SnippetView, @DeferredProperty e anotar views relevantes, sem grandes quebras de API.

Para um plano formal, vale ler o WWDC26 Apple Intelligence Guide, que organiza a migração por tipo de app. Tive um caso recente em que migramos um app de finanças com sete INIntents antigos em pouco mais de uma sprint, e o ganho não foi só técnico: o app começou a aparecer em buscas semânticas no Spotlight que antes nem encostavam nele.

Erros comuns e boas práticas para 2026

Depois de revisar várias migrações esse semestre, alguns padrões se repetem.

1. Parametrizar demais

Intents com seis parâmetros opcionais quase sempre são duas intents disfarçadas. O Apple Intelligence não consegue desambiguar bem, e quando consegue, o diálogo de resolução fica insuportável. Quebre por verbo.

2. Esquecer de openAppWhenRun

O default mudou: em 2026, intents não abrem o app a menos que você diga explicitamente. Para fluxos que precisam de UI completa, defina static var openAppWhenRun = true. Para fluxos conversacionais, deixe o default.

3. Ignorar a localização

Use LocalizedStringResource em todos os title, description e phrases. Apple Intelligence faz disambiguation por idioma; uma string hard-coded em inglês não dispara em pt-BR.

4. Snippet views pesadas

Tratei disso no callout acima, mas vale repetir: nada de List com fetch síncrono em snippet view. Carregue na intent, passe para a view já hidratado.

5. Não escrever testes de integração

Antes do AppIntentsTesting, dava para se desculpar. Agora não dá mais. Cada intent que vai para produção merece pelo menos um runAsTest.

Perguntas frequentes

Qual a diferença entre App Intents e SiriKit?

SiriKit usava domínios fechados (mensagens, treinos, listas) com schemas controlados pela Apple. App Intents é uma API Swift aberta em que qualquer ação do seu app pode ser exposta. Em 2026, SiriKit ainda existe mas não participa do Apple Intelligence; todo app novo deve nascer com App Intents 2.0.

Preciso de um Intent Extension separado no iOS 26?

Na maioria dos casos, não. App Intents rodam diretamente no processo do app principal. Você só precisa de extensions separadas para casos específicos como Widgets, Live Activities ou Control Center controls, e mesmo assim a intent em si pode viver no target principal e ser invocada por essas extensions.

Interactive Snippets funcionam offline?

Sim. A snippet é uma view SwiftUI executada localmente; ela não depende de servidor. O que depende de Apple Intelligence (a parte conversacional que invoca a intent por voz natural) precisa do modelo on-device, que roda offline em iPhone 15 Pro e superiores.

Como o Spotlight semantic index escolhe qual entidade mostrar?

Ele indexa o displayRepresentation de cada AppEntity retornada pela sua EntityQuery, gerando embeddings on-device. Buscas são casadas por similaridade semântica, não por palavra-chave. Por isso vale capricho no title e subtitle, eles são o "texto" que o modelo vê.

Posso testar App Intents sem rodar em um dispositivo físico?

Sim. O framework AppIntentsTesting roda no simulador e exercita a intent pelos mesmos caminhos que o sistema usa. Para validar a parte conversacional (Apple Intelligence + Siri), aí sim você precisa de um dispositivo compatível, mas para resolução de parâmetros, lógica do perform() e snippets, o simulador basta.

Lukas Müller
Sobre o Autor Lukas Müller

iOS developer and Swift author since the Objective-C days. Spends his evenings on side projects and his mornings on SwiftUI internals.