◉ANTIGRAVITY LABJP
Articles/App Development
▣ App Development/2026-07-04Advanced

When AppEnum Breaks in App Intents — Designing EntityQuery so Siri Can Pick From a Catalog That Grows Every Day

Writing an App Intents parameter with AppEnum works fine while the options are fixed, but it cannot survive content that grows daily. Here is the AppEntity + EntityQuery design that lets Siri and Shortcuts correctly pick from a dynamic catalog, including identifier stability and Spotlight pitfalls.

antigravity461app-intents3siri3ios38swift9app-entity

✦ Premium Article

When I asked Antigravity to "add an App Intent to my wallpaper app so a collection can be opened by name," the Swift it returned ran cleanly. It listed a few representative collection names in an AppEnum and offered them as parameter options. Testing in the Shortcuts app, the suggestions appeared. Had I been satisfied and shipped it there, it would have quietly broken a few weeks later.

The problem is that the collections in the wallpaper apps I run as an indie developer grow and shrink almost daily. AppEnum is a type that assumes its options are fixed at compile time; it cannot represent a catalog that changes at runtime. The generated code wasn't bad — the prompt simply never carried the fact that this catalog is dynamic. Here I'll walk through the exact conditions under which AppEnum breaks, and the move to AppEntity + EntityQuery, all the way down to identifier stability.

AppEnum Is a Fixed Menu, Not a Catalog

App Intents gives you two main ways to offer parameter options: AppEnum and AppEntity. They produce similar-looking code, but they assume completely different worlds.

AspectAppEnumAppEntity
How options are decidedFixed at compile timeResolved by a query at runtime
Good forFinite settings: sort order, theme, qualityData that grows: articles, collections, products
CountA handful to a dozenHundreds to thousands
SearchNo (present all only)Filter by string match
Spotlight exposureNoYes, via IndexedEntity

So a fixed value like "wallpaper quality (standard / high / max)" is a correct fit for AppEnum, while a growing target like "the user's collections" should use AppEntity. Get this line wrong early and it will pass your tests while the catalog is small, then degrade silently in production. The code I first received was exactly this mix-up.

The deciding question is simple: can these options grow without waiting for the next app update? If yes, it's AppEntity.

AppEntity Is the Data; EntityQuery Is How to Find It

An AppEntity is the type that represents one item you expose to Siri and Shortcuts. One collection in the catalog looks like this.

import AppIntents
 
struct WallpaperCollection: AppEntity {
    // Stable identifier: use the data's persistent ID, not an array index
    let id: String
    let name: String
    let itemCount: Int
 
    static var typeDisplayRepresentation: TypeDisplayRepresentation {
        TypeDisplayRepresentation(name: "Wallpaper Collection")
    }
 
    // How a single item looks in Siri / Shortcuts
    var displayRepresentation: DisplayRepresentation {
        DisplayRepresentation(
            title: "\(name)",
            subtitle: "\(itemCount) images"
        )
    }
 
    static var defaultQuery = WallpaperCollectionQuery()
}

On its own, AppEntity has no idea how to find one item. That job belongs to EntityQuery. This is the biggest difference from AppEnum: most of the implementation weight sits on the query side.

struct WallpaperCollectionQuery: EntityQuery {
    // (1) Rebuild entities from identifiers. Called every time a saved shortcut re-resolves.
    func entities(for identifiers: [String]) async throws -> [WallpaperCollection] {
        let all = await CollectionStore.shared.load()
        let map = Dictionary(uniqueKeysWithValues: all.map { ($0.id, $0) })
        return identifiers.compactMap { map[$0] }
    }
 
    // (2) The first candidates shown in the parameter editor
    func suggestedEntities() async throws -> [WallpaperCollection] {
        let all = await CollectionStore.shared.load()
        // Don't return everything — just the most recently used
        return Array(all.sorted { $0.lastUsedAt > $1.lastUsedAt }.prefix(10))
    }
}

Do not treat entities(for:) lightly. It runs every time a user later executes a shortcut they saved earlier. If you can't rebuild the entity from its identifier here, the saved shortcut breaks with "not found." AppEnum has no concept of this rebuild, which is precisely why using it for dynamic data undermines the durability of saved shortcuts.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦Understand exactly when AppEnum fails to represent a dynamic catalog, so you can decide with confidence when to move to AppEntity
✦Implement EntityQuery's entities(for:) / suggestedEntities() / entities(matching:) by role, so both Siri and Shortcuts can search and present your catalog
✦Avoid the silent breakage of using an array index as an identifier, and land on a design with stable IDs and Spotlight integration that actually holds up in production
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $15 for lifetime access
View Membership →

Related Articles

▣ App Dev2026-08-29
A Green Build Is Not Proof That Your pbxproj Edit Was Correct
When an agent edits project.pbxproj, the build stays green while resources and script phases quietly fall out. Four structural checks and an audit script that finishes in 0.1 seconds on a 1,200-file project, drawn from running iOS apps in production.
▣ App Dev2026-05-24
Running iOS Push Notification A/B Tests Weekly With Antigravity Agent — A Self-Improving Loop For Copy, Timing, And Segments
Drawing on years of indie iOS development across wallpaper and wellness apps, this article walks through a weekly Push Notification A/B loop powered by Antigravity Agent — the FCM bridge, BigQuery measurement, segment design, a D7 retention recovery story, and a dependency-free simulation that made me cut the number of arms and add a significance rule before declaring any winner.
▣ App Dev2026-04-19
Building a Fully Automated iOS App Release Pipeline with Antigravity — From Screenshot Generation to App Store Review Management
Use Antigravity, App Store Connect API, and GitHub Actions to automate every step from build to App Store submission. A complete advanced guide covering AI screenshot captions, metadata optimization, and rejection analysis.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links