App Schemas: Make Your App Available to Siri

At WWDC 2026, an Apple engineer took a SwiftUI calendar app that only responded to taps and made Siri search its events, answer questions about them by name and note content, create and update them by voice, and render a custom result card, writing three structs and filling out a handful of code snippets to get there.1 The mechanism behind that shift is App Schemas: a way to describe an app’s content and actions in terms Siri already understands, with no training phrases and no natural-language processing on the developer’s end.1 The session is a code-along built around a sample project called CometCal, and the lesson underneath the cosmic theme is structural. You do not teach Siri your vocabulary. You declare your data and your actions against a shape Siri already knows, and the rest follows.

This post walks the three pillars that carry that result: the schema-domain model, semantic donation to Spotlight through IndexedEntity, and onscreen awareness plus the valueState distinction that makes voice-driven updates safe. Everything below comes from the session directly. The subject differs from background execution in App Intents, which covers running work without launching the UI; the focus here is how Siri reasons over your content and acts on it.

TL;DR

  • App Schemas describe an app’s entities, action parameters, and outputs in terms Siri already understands, organized into App Schema Domains; the calendar domain covers events, calendars, attendees, and the actions on them, with no training phrases and no NLP on the developer’s side.1
  • Schematized entities come from Xcode code snippets: type a domain prefix like calendar_ and pick a snippet (for example calendar_calendar), which scaffolds the entity with its macro, properties, display representation, and query stubs.1
  • Conforming an entity to IndexedEntity and donating it to a CSSearchableIndex via indexAppEntities (and removing it via deleteAppEntities) lets Siri resolve it by name, by property, or by context without a custom property query.1
  • Two view modifiers, .appEntityIdentifier on a list and .userActivity on a detail view (each carrying an EntityIdentifier), give Siri onscreen awareness so a request like “email the people in this event” resolves without naming the event.1
  • Update intents expose IntentParameter.valueState, where .set with a value, .set with nil, and .unset distinguish a new value, an explicit clear, and an absent parameter, so Siri-driven edits stay unambiguous.1

What App Schemas actually are (Session 344)

Watch on Apple Developer ↗

Justin from the Swift Intelligence Frameworks team explains App Schemas before opening Xcode, starting at 3:12.

Siri reaches an app through the App Intents framework, and Apple Intelligence powers the reasoning on top.1 The starting problem in CometCal is plain: “Right now, Siri has no idea what a calendar or an event means inside CometCal.”1 App Schemas close that gap. As the session puts it, they “describe my app’s content and actions in terms Siri can already understand. They define the structure of my entities, the parameters of my actions, and the outputs. No training phrases, no natural language processing on my end.”1

The organizing unit is the App Schema Domain. The calendar domain “covers everything related to scheduling: events, calendars, attendees, and the actions that operate on them.”1 Because the shapes are predefined, the editor does the cataloging. The engineer creates a CalendarEntity file, imports AppIntents, types calendar_, and “Xcode offers every schema in the Calendar domain, right in autocomplete.”1 Selecting calendar_calendar fills in the structure: the entity macro, properties, a display representation, and query stubs, producing what the session calls “a schematized entity, a type Siri can reason over.”1

The naming convention deserves a careful note. The schema snippet names appear in the editor as lowercase, underscore-prefixed identifiers (calendar_calendar, calendar_attendee, calendar_event, calendar_createEvent, calendar_updateEvent, plus enum snippets like calendar_attendeeStatus and calendar_attendeeType), and the spoken transcript renders them that way. The Swift types they scaffold (an @AppEntity macro, a DisplayRepresentation, query protocol conformances) follow ordinary Swift casing. Confirm the exact spelling and casing of each symbol against Apple’s App Intents documentation and the downloadable CometCal sample project before you build against them, since a spoken code-along is not a precise reference for capitalization.

The payoff of the schema model is reach for very little code. The session frames the entire content layer as “three structs and filling out a few code snippets.”1 CometCal builds three entities of increasing richness: a calendar, an attendee, and an event that pulls the other two together. The event “composes with the other entities built earlier”: its calendar is a CalendarEntity, and its attendees are an array of AttendeeEntity, and “Siri understands these relationships with App Schemas.”1 The schema also decides what is required versus optional. Essentials like title or start date wire up directly, optional schema properties an app does not use (the session names travel time and virtual location) can stay unset, and a property that lives on the data model but not the schema, like isFavorite, can still be added to the entity.1

Two further schema mechanics show up on the event. Union values let one property hold one of several types: the location can be “either a PlaceDescriptor from the GeoToolbox framework, or a String,” and an alarm can be either a Duration or a Date.1 The recurrence property uses Foundation’s Calendar.RecurrenceRule and converts to and from CometCal’s own frequency enum for daily, weekly, monthly, and yearly cases.1 Schematized enums (the session points to an event status enum it calls EventEntityStatus, and the attendee enums above) arrive complete from the snippet, and the app adopts the cases that apply; if an app uses different terminology, you map the existing model onto the schema’s cases “so Siri can recognize the shape.”1

Semantic donation through IndexedEntity

The schema gives Siri a vocabulary. Donation gives Siri the actual data to reason over. The two are separate steps, and the session is explicit that missing the second is easy: “IndexedEntity defines the shape of my indexed content, but entities still need to be donated.”1

Conforming an entity to the IndexedEntity protocol is what enables matching by meaning rather than by text alone.1 The reason is the search index. Conforming “allows my app to donate entities using the Spotlight index to get the benefits of semantic understanding,” and once an entity is donated, “Siri can resolve it by name, by property, or by context, without requiring a custom property query.”1 That last clause is the whole point. You write no bespoke matcher for “the crew lunch” or “events that mention oxygen.” Siri searches the donated titles and note content directly, and “answers every question using the app’s content. No need for custom natural language… just entities and schemas.”1

Donation runs through CSSearchableIndex. CometCal holds a CSSearchableIndex instance, created in its CalendarManager initializer under a name unique to the app.1 The rule the session states is that “anytime calendars, or any indexed entity for that matter, are changed, the index needs to be updated.”1 So the data layer donates on write: the create path calls indexAppEntities with the searchable index before returning, the update path re-indexes the changed entity, and the delete path calls deleteAppEntities, “passing in the entity’s id and type.”1 After wiring the calendar entity, the engineer creates a calendar named “Lunar Orbit Log,” swipes to search, and finds it with its icon and title, the proof that donation took.1

Not every entity should be indexed, and the attendee is the counterexample that teaches the rule. AttendeeEntity conforms to TransientAppEntity instead of IndexedEntity, “a temporary entity that doesn’t require a unique identifier and isn’t meant to be queried.”1 The reasoning is modeling discipline: in CometCal an attendee represents “a person’s participation in a specific event, not the person themselves,” the same person can attend many events, and “indexing each attendance separately would create duplicative results in Spotlight.”1 Since attendees are always reached through their event, there is no independent lookup path to maintain, and TransientAppEntity “makes that explicit… no query to write, no index to maintain.”1 The attendee also introduces IntentPerson, “the system’s standard way to represent a person with a name and contact information,” useful for handing an attendee’s email to Mail to draft a message.1

The indexed entities still need their query plumbing. The query holds the data layer through the @Dependency property wrapper, “how App Intents injects shared resources into intents and queries,” so the query uses the one registered CalendarManager rather than a fresh instance, and the query is marked main-actor because the manager is.1 The required EntityQuery method fetches by ID for cases where the system already knows it, and conforming to EnumerableEntityQuery with an allEntities method lets the system list available calendars later when Siri needs to offer them as options while creating an event.1 A DisplayRepresentation (title plus a system calendar image) tells Siri and Spotlight how to render the entity.1

There is a navigation seam worth naming, because donation alone lands the user on the app’s main screen. An OpenEventIntent that conforms to the system.open schema, takes an EventEntity as its target, and tells the navigation layer to route to it, closes that gap: the system invokes it “whenever someone taps an event result in Spotlight or Siri, or asks Siri to open one,” so a tapped result opens straight to the event’s detail view.1

Onscreen awareness and the valueState distinction

The first two pillars let Siri find content by name. The third lets Siri use what is already in front of the user, and then act on it without ambiguity.

Onscreen awareness costs “just two view modifiers.”1 In the list view, .appEntityIdentifier attaches to the list, “passing in an EntityIdentifier for each of the event entities,” which “connects the list to its entities, so when someone is browsing the list, the system knows which events are on screen.”1 In the detail view, .userActivity carries an EntityIdentifier for the single event in focus, telling the system “that one specific event is front and center so Siri can resolve this event to exactly the one being viewed.”1 With both in place, a user on an event’s detail view can say “email the people in this event and ask someone to bring chocolate and marshmallows,” and Siri uses its understanding of the onscreen event to find the attendees and hand them to Mail, no title required.1

Acting on content is the same pattern as reading it, run in reverse. Intents come from snippets too. The calendar_createEvent snippet scaffolds the intent with its macro, the schema, the parameters the schema requires, and a perform stub.1 The perform logic is a three-step shape the session states plainly: “resolve the intent’s parameters into something the data layer understands, perform the action, and return the result as an entity.”1 For create, that means extracting the location from its union value, converting recurrence if provided, calling the manager’s create method, and returning an EventEntity.1 Because the intent conforms to a schema, “Siri can handle all the heavy lifting. Interpreting language, asking for clarification, and confirming details,” so the developer never writes the conversation.1

Updates surface the subtlety that makes voice-driven edits trustworthy. Most parameters on calendar_updateEvent are optional, because a user usually changes one or two things, and “the event parameter is what Siri resolves; everything else is optional.”1 A plain nil check cannot answer the real question. As the session frames it, “when recurrence is nil, does that mean ‘don’t change it’ or ‘remove it’? A simple nil check doesn’t tell me which case I’m dealing with.”1 The answer is IntentParameter.valueState, exposed because the intent macro wraps each property in an IntentParameter. The three states carry distinct meaning: “.set with an actual value means a new value is provided. .set with a nil value means it’s explicitly cleared. .unset means the parameter isn’t part of the request.”1 The distinction “applies to any optional parameter where clearing the value is a meaningful action,” which is why “do not repeat this event” reliably clears recurrence rather than leaving it untouched.1

Two finishing touches round out the action layer. A custom result card replaces Siri’s default display-representation card: adding ShowsSnippetView to the perform method’s return type and passing a prepared SwiftUI view (the session’s takes an EventEntity) renders the app’s own styling inside Siri, an approach that “works for any other intent that returns a result.”1 And DeleteEventIntent, “the simplest of the three,” takes just the event and an optional span for recurring events; Siri “automatically handles the confirmation dialog before anything is removed” and disambiguates when more than one event matches.1

Key Takeaways

For iOS developers adopting App Intents:

  • Reach for the schema first. Type a domain prefix like calendar_ in Xcode and let autocomplete list the available snippets; the snippet scaffolds the macro, properties, display representation, and query stubs so you fill in types and mapping rather than inventing structure.1
  • Decide per entity whether it earns an index. Conform durable, queryable content to IndexedEntity and donate it; use TransientAppEntity for participation-style records (CometCal’s attendee) that are always reached through a parent and would only pollute Spotlight if indexed.1
  • Verify exact symbol spelling and casing against Apple’s App Intents docs and the CometCal sample before building, since the code-along names come from a spoken transcript.

For teams designing voice and Apple Intelligence flows:

  • Treat donation as a write-path responsibility. Call indexAppEntities on create and update and deleteAppEntities on delete, keyed by the entity’s id and type, so Siri’s index never drifts from the data.1
  • Add onscreen awareness early: .appEntityIdentifier on lists and .userActivity on detail views (each carrying an EntityIdentifier) let users say “this event” instead of its title.1
  • Handle valueState explicitly in update intents. Branch on .set-with-value, .set-with-nil, and .unset so an explicit clear never reads as “leave unchanged.”1

FAQ

What are App Schemas in App Intents?

App Schemas describe an app’s content and actions in terms Siri already understands: they define the structure of an app’s entities, the parameters of its actions, and the outputs, with no training phrases and no natural-language processing on the developer’s side. They are organized into App Schema Domains; the calendar domain covers events, calendars, attendees, and the actions on them. In Xcode you adopt a schema by typing a domain prefix like calendar_ and choosing a code snippet such as calendar_calendar, which scaffolds the entity.1

How does Siri resolve my app’s content by name or context?

Conform the entity to the IndexedEntity protocol and donate it to a CSSearchableIndex (the Spotlight index) by calling indexAppEntities on create and update, and deleteAppEntities with the entity’s id and type on delete. Donation gives Siri “semantic understanding,” letting it resolve an entity by name, by property, or by context without a custom property query, including searching note content for questions like “what events mention oxygen?”1

When should I use TransientAppEntity instead of IndexedEntity?

Use TransientAppEntity for a temporary entity that needs no unique identifier and is not meant to be queried. CometCal’s attendee fits because an attendee represents a person’s participation in a specific event, not the person; the same person attends many events, and indexing each attendance separately would create duplicative Spotlight results. Since attendees are reached only through their event, there is no independent lookup path, so the transient entity needs no query and no index.1

What is valueState and why does it matter for update intents?

In an update intent, the App Intents macro wraps each property in an IntentParameter that exposes a valueState. It distinguishes three cases a nil check cannot: .set with a value means a new value, .set with nil means the value is explicitly cleared, and .unset means the parameter was not part of the request. The distinction lets Siri-driven edits clear a property (for example, “do not repeat this event”) without that being confused for “leave it unchanged.”1

How do I give Siri onscreen awareness of my app?

Add two view modifiers. Put .appEntityIdentifier on the list view, passing an EntityIdentifier for each event entity, so the system knows which events are on screen while browsing. Put .userActivity with an EntityIdentifier on the detail view so the system knows one specific event is in focus. Together they let a user say “email the people in this event” and have Siri resolve “this event” to exactly the one being viewed.1


This post sits in a cluster on Apple’s intelligence frameworks. For the framework that App Schemas build on, start with App Intents: Apple’s new API to your app. For running intent work without launching the UI, which is a separate concern from the content reasoning covered here, read background execution in App Intents. For the broader Spotlight donation story behind semantic resolution, see on-device AI and Spotlight media indexing. The full series hub is the Apple Ecosystem Series.

References


  1. Apple, WWDC 2026 session 344, Code-along: Make your app available to Siri. Source for App Schemas and App Schema Domains (the calendar domain; no training phrases, no NLP); schematized entities via Xcode snippets (calendar_calendar, calendar_attendee, calendar_event, calendar_createEvent, calendar_updateEvent, calendar_attendeeStatus, calendar_attendeeType); IndexedEntity and Spotlight donation through CSSearchableIndex via indexAppEntities / deleteAppEntities; resolution by name, property, or context; TransientAppEntity and the attendee modeling rationale; IntentPerson; union values (PlaceDescriptor from GeoToolbox, String; Duration or Date alarms) and Calendar.RecurrenceRule; the @Dependency wrapper, EntityQuery, EnumerableEntityQuery, and DisplayRepresentation; the system.open OpenEventIntent; onscreen awareness via .appEntityIdentifier and .userActivity carrying an EntityIdentifier; IntentParameter.valueState (.set/.unset); the ShowsSnippetView custom result card; DeleteEventIntent confirmation and disambiguation; and the AppIntentsTesting framework referenced for automated tests. 

Artículos relacionados

App Intents Are Apple's New API to Your App

I shipped an App Intent in Water on Feb 8, 2026. Here's what Apple Intelligence wants from third-party apps in iOS 26, a…

18 min de lectura

Meet Music Understanding: On-Device Audio Analysis

Music Understanding is Apple's on-device framework for analyzing a song's key, rhythm, structure, pace, instrument activ…

12 min de lectura

Your Agent Has Two Untrusted Inputs

AI agents have two untrusted inputs: code the model writes and tool output it reads. One now has a real WASM sandbox; th…

12 min de lectura