App Schemas: deixe seu app disponível para a Siri
Na WWDC 2026, um engenheiro da Apple pegou um app de calendário em SwiftUI que só respondia a toques e fez a Siri pesquisar seus eventos, responder perguntas sobre eles por nome e por conteúdo de notas, criá-los e atualizá-los por voz, e renderizar um cartão de resultado personalizado, escrevendo três structs e preenchendo um punhado de snippets de código para chegar lá.1 O mecanismo por trás dessa mudança é o App Schemas: uma forma de descrever o conteúdo e as ações de um app em termos que a Siri já entende, sem frases de treinamento e sem processamento de linguagem natural do lado do desenvolvedor.1 A sessão é um code-along construído em torno de um projeto de exemplo chamado CometCal, e a lição por baixo do tema cósmico é estrutural. Você não ensina seu vocabulário à Siri. Você declara seus dados e suas ações contra um formato que a Siri já conhece, e o resto vem em seguida.
Este post percorre os três pilares que sustentam esse resultado: o modelo de schema-domínio, a doação semântica para o Spotlight por meio de IndexedEntity e a consciência de tela mais a distinção de valueState que torna seguras as atualizações guiadas por voz. Tudo o que segue vem diretamente da sessão. O assunto difere da execução em segundo plano em App Intents, que trata de executar trabalho sem abrir a UI; o foco aqui é como a Siri raciocina sobre o seu conteúdo e age sobre ele.
TL;DR
- Os
App Schemasdescrevem as entidades, os parâmetros de ação e as saídas de um app em termos que a Siri já entende, organizados em App Schema Domains; o domínio de calendário cobre eventos, calendários, participantes e as ações sobre eles, sem frases de treinamento e sem NLP do lado do desenvolvedor.1 - Entidades esquematizadas vêm de snippets de código do Xcode: digite um prefixo de domínio como
calendar_e escolha um snippet (por exemplo,calendar_calendar), que estrutura a entidade com sua macro, propriedades, display representation e stubs de query.1 - Fazer uma entidade conformar a
IndexedEntitye doá-la a umCSSearchableIndexviaindexAppEntities(e removê-la viadeleteAppEntities) permite que a Siri a resolva por nome, por propriedade ou por contexto, sem uma query de propriedade personalizada.1 - Dois view modifiers,
.appEntityIdentifierem uma lista e.userActivityem uma tela de detalhe (cada um carregando umEntityIdentifier), dão à Siri consciência de tela, de modo que uma solicitação como “envie um e-mail para as pessoas deste evento” se resolva sem nomear o evento.1 - Os intents de atualização expõem
IntentParameter.valueState, em que.setcom um valor,.setcom nil e.unsetdistinguem um valor novo, uma limpeza explícita e um parâmetro ausente, mantendo inequívocas as edições guiadas pela Siri.1
O que os App Schemas realmente são (Session 344)
Justin, da equipe de Swift Intelligence Frameworks, explica os App Schemas antes de abrir o Xcode, a partir de 3:12.
A Siri alcança um app por meio do framework App Intents, e o Apple Intelligence dá força ao raciocínio que vem por cima.1 O problema inicial no CometCal é simples: “Neste momento, a Siri não faz ideia do que significam um calendário ou um evento dentro do CometCal.”1 Os App Schemas fecham essa lacuna. Como a sessão coloca, eles “descrevem o conteúdo e as ações do meu app em termos que a Siri já consegue entender. Eles definem a estrutura das minhas entidades, os parâmetros das minhas ações e as saídas. Sem frases de treinamento, sem processamento de linguagem natural do meu lado.”1
A unidade organizadora é o App Schema Domain. O domínio de calendário “cobre tudo o que se refere a agendamento: eventos, calendários, participantes e as ações que operam sobre eles.”1 Como os formatos são predefinidos, o editor faz a catalogação. O engenheiro cria um arquivo CalendarEntity, importa AppIntents, digita calendar_ e “o Xcode oferece todos os schemas do domínio Calendar, ali mesmo no autocompletar.”1 Selecionar calendar_calendar preenche a estrutura: a macro da entidade, propriedades, uma display representation e stubs de query, produzindo o que a sessão chama de “uma entidade esquematizada, um tipo sobre o qual a Siri consegue raciocinar.”1
A convenção de nomenclatura merece uma observação cuidadosa. Os nomes dos snippets de schema aparecem no editor como identificadores em minúsculas e prefixados por sublinhado (calendar_calendar, calendar_attendee, calendar_event, calendar_createEvent, calendar_updateEvent, além de snippets de enum como calendar_attendeeStatus e calendar_attendeeType), e a transcrição falada os apresenta assim. Os tipos Swift que eles estruturam (uma macro @AppEntity, uma DisplayRepresentation, conformidades a protocolos de query) seguem a capitalização comum do Swift. Confirme a grafia e a capitalização exatas de cada símbolo na documentação de App Intents da Apple e no projeto de exemplo CometCal disponível para download antes de programar contra eles, já que um code-along falado não é uma referência precisa para capitalização.
O retorno do modelo de schema é alcance com muito pouco código. A sessão resume toda a camada de conteúdo como “três structs e o preenchimento de alguns snippets de código.”1 O CometCal constrói três entidades de riqueza crescente: um calendário, um participante e um evento que reúne os outros dois. O evento “se compõe com as outras entidades construídas antes”: seu calendário é uma CalendarEntity, e seus participantes são um array de AttendeeEntity, e “a Siri entende essas relações com os App Schemas.”1 O schema também decide o que é obrigatório versus opcional. Itens essenciais como título ou data de início se conectam diretamente; propriedades de schema opcionais que um app não usa (a sessão cita o tempo de deslocamento e a localização virtual) podem ficar sem definição; e uma propriedade que existe no modelo de dados, mas não no schema, como isFavorite, ainda pode ser adicionada à entidade.1
Outras duas mecânicas de schema aparecem no evento. Valores de união permitem que uma propriedade contenha um de vários tipos: a localização pode ser “ou um PlaceDescriptor do framework GeoToolbox, ou uma String”, e um alarme pode ser ou uma Duration ou uma Date.1 A propriedade de recorrência usa o Calendar.RecurrenceRule do Foundation e converte de e para o próprio enum de frequência do CometCal para os casos diário, semanal, mensal e anual.1 Enums esquematizados (a sessão aponta para um enum de status de evento que ela chama de EventEntityStatus, e os enums de participante acima) chegam completos a partir do snippet, e o app adota os casos que se aplicam; se um app usa terminologia diferente, você mapeia o modelo existente para os casos do schema “para que a Siri reconheça o formato.”1
Doação semântica por meio de IndexedEntity
O schema dá à Siri um vocabulário. A doação dá à Siri os dados de verdade sobre os quais raciocinar. Os dois são passos separados, e a sessão é explícita ao dizer que é fácil esquecer o segundo: “IndexedEntity define o formato do meu conteúdo indexado, mas as entidades ainda precisam ser doadas.”1
Fazer uma entidade conformar ao protocolo IndexedEntity é o que habilita a correspondência por significado, e não apenas por texto.1 A razão é o índice de busca. A conformidade “permite que meu app doe entidades usando o índice do Spotlight para obter os benefícios do entendimento semântico”, e, uma vez que uma entidade é doada, “a Siri consegue resolvê-la por nome, por propriedade ou por contexto, sem exigir uma query de propriedade personalizada.”1 Essa última frase é o ponto inteiro. Você não escreve nenhum matcher sob medida para “o almoço da tripulação” ou “eventos que mencionam oxigênio”. A Siri pesquisa diretamente os títulos e o conteúdo de notas doados, e “responde a cada pergunta usando o conteúdo do app. Sem necessidade de linguagem natural personalizada… apenas entidades e schemas.”1
A doação corre por meio do CSSearchableIndex. O CometCal mantém uma instância de CSSearchableIndex, criada no inicializador de seu CalendarManager sob um nome exclusivo do app.1 A regra que a sessão estabelece é que “sempre que calendários, ou qualquer entidade indexada por sinal, mudam, o índice precisa ser atualizado.”1 Então a camada de dados doa na escrita: o caminho de criação chama indexAppEntities com o índice pesquisável antes de retornar, o caminho de atualização reindexa a entidade alterada, e o caminho de exclusão chama deleteAppEntities, “passando o id e o tipo da entidade.”1 Depois de conectar a entidade de calendário, o engenheiro cria um calendário chamado “Lunar Orbit Log”, desliza até a busca e o encontra com seu ícone e título, a prova de que a doação pegou.1
Nem toda entidade deveria ser indexada, e o participante é o contraexemplo que ensina a regra. AttendeeEntity conforma a TransientAppEntity em vez de IndexedEntity, “uma entidade temporária que não exige um identificador único e não se destina a ser consultada.”1 O raciocínio é disciplina de modelagem: no CometCal, um participante representa “a participação de uma pessoa em um evento específico, não a pessoa em si”, a mesma pessoa pode comparecer a muitos eventos, e “indexar cada participação separadamente criaria resultados duplicados no Spotlight.”1 Como os participantes são sempre alcançados por meio de seu evento, não há um caminho de busca independente a manter, e TransientAppEntity “deixa isso explícito… nenhuma query a escrever, nenhum índice a manter.”1 O participante também introduz IntentPerson, “a forma padrão do sistema de representar uma pessoa com nome e informações de contato”, útil para entregar ao Mail o e-mail de um participante e redigir uma mensagem.1
As entidades indexadas ainda precisam de seu encanamento de query. A query mantém a camada de dados por meio do property wrapper @Dependency, “como o App Intents injeta recursos compartilhados em intents e queries”, de modo que a query usa o único CalendarManager registrado em vez de uma instância nova, e a query é marcada como main-actor porque o manager também é.1 O método EntityQuery obrigatório busca por ID nos casos em que o sistema já a conhece, e conformar a EnumerableEntityQuery com um método allEntities permite que o sistema liste mais tarde os calendários disponíveis quando a Siri precisar oferecê-los como opções ao criar um evento.1 Uma DisplayRepresentation (título mais uma imagem de calendário do sistema) diz à Siri e ao Spotlight como renderizar a entidade.1
Há uma costura de navegação que vale a pena nomear, porque a doação sozinha leva o usuário à tela principal do app. Um OpenEventIntent que conforma ao schema system.open, recebe uma EventEntity como seu alvo e diz à camada de navegação para rotear até ele fecha essa lacuna: o sistema o invoca “sempre que alguém toca em um resultado de evento no Spotlight ou na Siri, ou pede à Siri para abrir um”, de modo que um resultado tocado abre direto na tela de detalhe do evento.1
Consciência de tela e a distinção de valueState
Os dois primeiros pilares deixam a Siri encontrar conteúdo por nome. O terceiro deixa a Siri usar o que já está diante do usuário e, então, agir sobre isso sem ambiguidade.
A consciência de tela custa “apenas dois view modifiers.”1 Na tela de lista, .appEntityIdentifier se anexa à lista, “passando um EntityIdentifier para cada uma das entidades de evento”, o que “conecta a lista às suas entidades, de modo que, quando alguém está navegando pela lista, o sistema sabe quais eventos estão na tela.”1 Na tela de detalhe, .userActivity carrega um EntityIdentifier para o único evento em foco, dizendo ao sistema “que aquele evento específico está em primeiro plano, para que a Siri consiga resolver este evento para exatamente aquele que está sendo visto.”1 Com ambos no lugar, um usuário na tela de detalhe de um evento pode dizer “envie um e-mail para as pessoas deste evento e peça que alguém traga chocolate e marshmallows”, e a Siri usa seu entendimento do evento na tela para encontrar os participantes e entregá-los ao Mail, sem exigir título.1
Agir sobre o conteúdo é o mesmo padrão de lê-lo, executado ao contrário. Os intents também vêm de snippets. O snippet calendar_createEvent estrutura o intent com sua macro, o schema, os parâmetros que o schema exige e um stub de perform.1 A lógica de perform é um formato de três passos que a sessão enuncia sem rodeios: “resolver os parâmetros do intent em algo que a camada de dados entenda, executar a ação e retornar o resultado como uma entidade.”1 Para criar, isso significa extrair a localização de seu valor de união, converter a recorrência se fornecida, chamar o método de criação do manager e retornar uma EventEntity.1 Como o intent conforma a um schema, “a Siri consegue cuidar de todo o trabalho pesado. Interpretar a linguagem, pedir esclarecimentos e confirmar detalhes”, de modo que o desenvolvedor nunca escreve a conversa.1
As atualizações revelam a sutileza que torna confiáveis as edições guiadas por voz. A maioria dos parâmetros de calendar_updateEvent é opcional, porque um usuário em geral muda uma ou duas coisas, e “o parâmetro de evento é o que a Siri resolve; todo o resto é opcional.”1 Uma simples verificação de nil não consegue responder à pergunta real. Como a sessão coloca, “quando a recorrência é nil, isso significa ‘não mude’ ou ‘remova’? Uma simples verificação de nil não me diz com qual caso estou lidando.”1 A resposta é IntentParameter.valueState, exposto porque a macro do intent envolve cada propriedade em um IntentParameter. Os três estados carregam significados distintos: “.set com um valor de verdade significa que um novo valor foi fornecido. .set com um valor nil significa que ele foi explicitamente limpo. .unset significa que o parâmetro não faz parte da solicitação.”1 A distinção “se aplica a qualquer parâmetro opcional em que limpar o valor é uma ação significativa”, e é por isso que “não repita este evento” limpa a recorrência de forma confiável, em vez de deixá-la intocada.1
Dois acabamentos arrematam a camada de ação. Um cartão de resultado personalizado substitui o cartão padrão de display representation da Siri: adicionar ShowsSnippetView ao tipo de retorno do método de perform e passar uma view SwiftUI já preparada (a da sessão recebe uma EventEntity) renderiza o estilo do próprio app dentro da Siri, uma abordagem que “funciona para qualquer outro intent que retorne um resultado.”1 E DeleteEventIntent, “o mais simples dos três”, recebe apenas o evento e um span opcional para eventos recorrentes; a Siri “cuida automaticamente do diálogo de confirmação antes que qualquer coisa seja removida” e desambigua quando mais de um evento corresponde.1
Principais conclusões
Para desenvolvedores iOS adotando App Intents:
- Recorra primeiro ao schema. Digite um prefixo de domínio como
calendar_no Xcode e deixe o autocompletar listar os snippets disponíveis; o snippet estrutura a macro, as propriedades, a display representation e os stubs de query, de modo que você preenche tipos e mapeamento em vez de inventar estrutura.1 - Decida, por entidade, se ela merece um índice. Faça conteúdo durável e consultável conformar a
IndexedEntitye doe-o; useTransientAppEntitypara registros do tipo participação (o participante do CometCal) que são sempre alcançados por meio de um pai e só poluiriam o Spotlight se indexados.1 - Verifique a grafia e a capitalização exatas dos símbolos na documentação de App Intents da Apple e no exemplo CometCal antes de programar, já que os nomes do code-along vêm de uma transcrição falada.
Para equipes projetando fluxos de voz e Apple Intelligence:
- Trate a doação como uma responsabilidade do caminho de escrita. Chame
indexAppEntitiesna criação e na atualização edeleteAppEntitiesna exclusão, indexados pelo id e pelo tipo da entidade, para que o índice da Siri nunca se desvie dos dados.1 - Adicione consciência de tela cedo:
.appEntityIdentifierem listas e.userActivityem telas de detalhe (cada um carregando umEntityIdentifier) deixam os usuários dizerem “este evento” em vez do título.1 - Trate
valueStateexplicitamente nos intents de atualização. Ramifique em.set-com-valor,.set-com-nil e.unset, para que uma limpeza explícita nunca seja lida como “deixar inalterado.”1
FAQ
O que são os App Schemas em App Intents?
Os App Schemas descrevem o conteúdo e as ações de um app em termos que a Siri já entende: eles definem a estrutura das entidades de um app, os parâmetros de suas ações e as saídas, sem frases de treinamento e sem processamento de linguagem natural do lado do desenvolvedor. Eles são organizados em App Schema Domains; o domínio de calendário cobre eventos, calendários, participantes e as ações sobre eles. No Xcode, você adota um schema digitando um prefixo de domínio como calendar_ e escolhendo um snippet de código como calendar_calendar, que estrutura a entidade.1
Como a Siri resolve o conteúdo do meu app por nome ou contexto?
Faça a entidade conformar ao protocolo IndexedEntity e doe-a a um CSSearchableIndex (o índice do Spotlight) chamando indexAppEntities na criação e na atualização, e deleteAppEntities com o id e o tipo da entidade na exclusão. A doação dá à Siri “entendimento semântico”, deixando-a resolver uma entidade por nome, por propriedade ou por contexto sem uma query de propriedade personalizada, inclusive pesquisando o conteúdo de notas para perguntas como “quais eventos mencionam oxigênio?”1
Quando devo usar TransientAppEntity em vez de IndexedEntity?
Use TransientAppEntity para uma entidade temporária que não precisa de identificador único e não se destina a ser consultada. O participante do CometCal se encaixa porque um participante representa a participação de uma pessoa em um evento específico, não a pessoa; a mesma pessoa comparece a muitos eventos, e indexar cada participação separadamente criaria resultados duplicados no Spotlight. Como os participantes são alcançados apenas por meio de seu evento, não há um caminho de busca independente, então a entidade transitória não precisa de query nem de índice.1
O que é valueState e por que ele importa para os intents de atualização?
Em um intent de atualização, a macro do App Intents envolve cada propriedade em um IntentParameter que expõe um valueState. Ele distingue três casos que uma verificação de nil não consegue: .set com um valor significa um valor novo, .set com nil significa que o valor foi explicitamente limpo, e .unset significa que o parâmetro não fez parte da solicitação. A distinção deixa as edições guiadas pela Siri limparem uma propriedade (por exemplo, “não repita este evento”) sem que isso seja confundido com “deixar inalterado.”1
Como dou à Siri consciência de tela do meu app?
Adicione dois view modifiers. Coloque .appEntityIdentifier na tela de lista, passando um EntityIdentifier para cada entidade de evento, para que o sistema saiba quais eventos estão na tela enquanto se navega. Coloque .userActivity com um EntityIdentifier na tela de detalhe, para que o sistema saiba que um evento específico está em foco. Juntos, eles deixam um usuário dizer “envie um e-mail para as pessoas deste evento” e fazem a Siri resolver “este evento” para exatamente aquele que está sendo visto.1
Este post integra um cluster sobre os frameworks de inteligência da Apple. Para o framework sobre o qual os App Schemas se apoiam, comece por App Intents: a nova API da Apple para o seu app. Para executar trabalho de intent sem abrir a UI, o que é uma preocupação separada do raciocínio sobre conteúdo tratado aqui, leia execução em segundo plano em App Intents. Para a história mais ampla de doação ao Spotlight por trás da resolução semântica, veja IA no dispositivo e indexação de mídia no Spotlight. O hub completo da série é a Apple Ecosystem Series.
Referências
-
Apple, WWDC 2026 session 344, Code-along: Make your app available to Siri. Fonte para App Schemas e App Schema Domains (o domínio de calendário; sem frases de treinamento, sem NLP); entidades esquematizadas via snippets do Xcode (
calendar_calendar,calendar_attendee,calendar_event,calendar_createEvent,calendar_updateEvent,calendar_attendeeStatus,calendar_attendeeType);IndexedEntitye doação ao Spotlight por meio deCSSearchableIndexviaindexAppEntities/deleteAppEntities; resolução por nome, propriedade ou contexto;TransientAppEntitye a justificativa de modelagem do participante;IntentPerson; valores de união (PlaceDescriptordoGeoToolbox, String; alarmesDurationouDate) eCalendar.RecurrenceRule; o wrapper@Dependency,EntityQuery,EnumerableEntityQueryeDisplayRepresentation; oOpenEventIntentdesystem.open; consciência de tela via.appEntityIdentifiere.userActivitycarregando umEntityIdentifier;IntentParameter.valueState(.set/.unset); o cartão de resultado personalizadoShowsSnippetView; a confirmação e a desambiguação deDeleteEventIntent; e o frameworkAppIntentsTestingreferenciado para testes automatizados. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩