WWDC Quick Look đź’“ By SwiftGGTeam
Develop for Shortcuts and Spotlight with App Intents

Develop for Shortcuts and Spotlight with App Intents

Watch original video

Highlight

Three sections: Use Model action, Spotlight on Mac, Automations on Mac.

Core Content

The most annoying thing about writing a Shortcut is that the formats do not line up. The model returns a paragraph of text, but the next step needs a Boolean, so you bolt on a parsing step. To pull amounts and dates out of a PDF, you write regex. Paste ChatGPT’s rich text into a notes app like Bear and you lose the bold and the tables. These “glue steps” make up most of a shortcut, and they are the common reason users give up.

WWDC25 answers with a new Use Model action (01:20). The action lets the user call an Apple Intelligence model from a Shortcut — Private Cloud Compute server model, on-device model, or ChatGPT. What really shapes the developer experience is how it hands off to the next action. When the next action expects a Boolean, the runtime turns the model output into yes/no. When it expects structured fields, the model returns a Dictionary. When it expects an app entity, the model picks one out of the entity list you pass in and returns an App Entity. The developer writes no extra code, but the entity has to expose enough information — because what the model sees is the entity’s JSON form.

Beyond the model action, the session covers two Mac-only topics: Spotlight can now run an App Intent directly (10:43) instead of just searching, and personal automations come to the Mac (17:18) with new triggers like folder and external drive. Both rest on the same precondition: your App Intent must be designed cleanly — full parameter summary, entity exposing a Find action, rich-text parameters using AttributedString.

Details

How the model sees an App Entity (06:51). When the user passes an entity list to the model, the system serializes each entity to JSON and feeds it in. The JSON has three parts:

  1. Every entity property exposed to Shortcuts, all converted to strings.
  2. The name from Type Display Representation, telling the model what kind of thing this is (e.g. “Calendar Event”).
  3. The title and subtitle from Display Representation.

Whatever the entity shows in Shortcuts is what the model sees. If you only expose id and title, the model cannot filter events by “start time”.

Two ways to implement Find action (08:06). For the model to reach an entity, you need a Find action. Two paths:

  • Hand-written query: implement EnumerableEntityQuery and EntityPropertyQuery.
  • Through Spotlight indexing: have the entity adopt IndexedEntity, then use the new API to map entity properties to Spotlight attribute keys, and the system generates the Find action for you.

The second path has new API this year: indexingKey and customIndexingKey (09:05). The first maps a property to a Spotlight key the system already knows (event title to eventTitle); the second is for properties without a built-in key (a custom notes field).

struct EventEntity: IndexedEntity {
    @Property(indexingKey: \.title)
    var eventTitle: String

    @Property(customIndexingKey: "notes")
    var notes: String

    static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "Event")

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

Key points:

  • The IndexedEntity protocol puts the entity into both the Core Spotlight index and the Shortcuts Find action.
  • indexingKey: \.title ties eventTitle to Spotlight’s existing title key, so the system can generate a “search by title” Find action.
  • customIndexingKey: "notes" uses a string for a custom key — good for fields without a built-in Spotlight key.
  • The name on typeDisplayRepresentation (“Event”) goes into the JSON fed to the model as a type hint.
  • The title and subtitle on displayRepresentation also go into the JSON. What the model can read is exactly the fields shown here.

Two hard requirements for an Intent to show up in Spotlight (12:34):

  1. The parameter summary must include every required parameter without a default. Miss one, and the intent disappears from Spotlight.
  2. The intent must not be hidden: isDiscoverable = false, assistantOnly = true, or intents used only as widget configuration (no perform method) will not show up.

If you add a required notes parameter but forget to write it into the summary, the intent vanishes. Three fixes: put it in the summary, make it optional, or give it a default (like an empty string).

Foreground and background split (16:20). Spotlight users sometimes want to “get in and out” (create the event in the background); other times they want to see the result (jump into the app and look at the new event). Apple suggests splitting into two intents: the background intent (like CreateEventIntent) ties to a foreground intent (like OpenEventIntent) through Opens Intent. The same action can run in the background and bring the app forward when needed.

Follow Up (09:50). The Use Model action has a Follow Up toggle. Turn it on and the user can type a follow-up after the initial result to adjust the output (the demo says “double the recipe”). For the developer, this means your intent may receive input that is richer and less tidy than a one-shot request.

Automations on Mac (17:18). Two new Mac-only triggers — folder and external drive — on top of Time of Day, Bluetooth, and the rest carried over from iOS. The rule is simple: any intent that runs on macOS can hook into a Mac automation, including iOS apps that install on the Mac.

Core Takeaways

  • Design entities by working backward from “what the model can read”: every @Property you expose, the title/subtitle on displayRepresentation, the name on typeDisplayRepresentation — all of it gets serialized into JSON and fed to the model. Why it matters: the model reasons over the JSON, and a field you do not expose may as well not exist. Where to start: take one core entity, print its current JSON form, and ask yourself “from this JSON alone, can I filter or sort it?” Fill in whatever is missing.

  • Switch every rich-text parameter to AttributedString: the model often outputs bold, lists, and tables. Why it matters: receiving with String silently drops the format the model produced, and the user experience suddenly gets worse. Where to start: search every text parameter on every intent. Wherever the app already supports rich text inside (notes, mail, documents), change the type from String to AttributedString and pair it with the new API in What’s New in Foundation.

  • Audit parameter summaries so intents make it into Spotlight: Spotlight on Mac is a free entry point, but messy intents do not get in. Why it matters: Mac users hit Cmd+Space dozens of times a day, and running your action right there is enormous traffic. Where to start: list every intent and check whether the parameter summary covers every required parameter without a default. For each gap: write it into the summary, make it optional, or add a default.

  • Split into a background and a foreground intent, chained with Opens Intent: a single intent either always opens the app (annoying) or never opens it (you cannot see the result). Where to start: pick a “create” action (create event, add task, log an expense). Add an OpenXxxIntent. Have the original intent’s perform return an OpensIntent tied to it. Let the user’s entry point decide which path runs.

  • Add Suggestions to every parameter: tapping a parameter field in Spotlight and seeing no suggestions is a turn-off. Where to start: for large lists (events, mail, documents), implement suggestedEntities on EntityQuery — for example, “calendar events in the next day”. For finite sets (time zones, currencies, languages), implement allEntities on EnumerableEntityQuery. For dynamic search, add EntityStringQuery, or hand it off to the system through IndexedEntity.

Comments

GitHub Issues · utterances