ANTIGRAVITY LABJP
Articles/App Development
App Development/2026-04-20Intermediate

Building App Intents with Antigravity: Siri and Shortcuts Integration in Swift

A practical guide to implementing App Intents in iOS apps using Antigravity. Learn how to integrate Siri and the Shortcuts app, plus the pitfalls to watch for when working with Antigravity-generated Swift code.

antigravity435ios36swift9app-intents3siri3shortcuts

When I first tried to add Siri integration to one of my iOS apps, the old SiriKit approach stopped me cold. Between the Intent Definition file, the generated code Xcode would overwrite on rebuild, and the separate App Extension target, the boilerplate alone took half a day before I'd written a single line of my own logic.

App Intents — introduced in iOS 16 and significantly enhanced in iOS 17 — changed everything. And combined with Antigravity, it's become one of the smoothest parts of my iOS development workflow. This guide walks through everything I've learned implementing App Intents with AI assistance, including the specific failure modes to watch for in Antigravity-generated Swift code.

Why App Intents Works So Well with Antigravity

The reason App Intents pairs nicely with Antigravity comes down to structure. SiriKit required you to manage three things at once: an .intentdefinition GUI file, Xcode-generated code you couldn't directly edit, and a separate App Extension. When you asked Antigravity to modify that setup, the generated files and hand-written code would frequently conflict.

App Intents eliminates all of that. Everything is pure Swift code in a single file, in a single target. When you tell Antigravity "add an intent to create a task," it produces a complete, self-contained implementation with no hidden dependencies to manage.

The difference in practical terms:

  • SiriKit (old): Intent Definition file + generated classes + App Extension target + rebuild cycle
  • App Intents (current): One Swift file, one target, zero code generation, editable directly

This structural simplicity is why Antigravity handles App Intents well. The entire intent is visible as code, so Antigravity can reason about it holistically rather than reconstructing intent from scattered files.

Setting Up Your First App Intent with Antigravity

Open your project in Antigravity and send this prompt. Specifying the iOS target version matters — the API differs meaningfully between iOS 16 and 17, and leaving it unspecified often leads Antigravity to use the older patterns:

Add App Intents support to this iOS app.
Target is iOS 17 and above. Create a single basic App Intent
to add a task. No EntityQuery needed for now.

Antigravity will inspect your project structure and generate something like this:

import AppIntents
 
struct AddTaskIntent: AppIntent {
    static var title: LocalizedStringResource = "Add Task"
    static var description = IntentDescription("Adds a new task to your app")
 
    @Parameter(title: "Task Name")
    var taskName: String
 
    @Parameter(title: "Due Date", default: nil)
    var dueDate: Date?
 
    func perform() async throws -> some IntentResult & ProvidesDialog {
        // Replace TaskRepository with your app's actual data layer
        let task = await TaskRepository.shared.add(name: taskName, dueDate: dueDate)
        return .result(dialog: "Added \(task.name) to your tasks")
    }
}

Build and run this on a device, open the Shortcuts app, and "Add Task" will appear immediately. No extension target, no rebuild dance, no .intentdefinition file to hunt down. The first time I saw this work I genuinely checked twice to make sure I hadn't missed a step.

Adding Enums for Option Pickers

The type safety of App Intents parameters is where the framework really shines. Using AppEnum, you can give Siri and the Shortcuts app a proper picker interface instead of free-form text input. This is especially useful for parameters with a fixed set of valid values like status, category, or priority.

Tell Antigravity: "Add a priority parameter with three options: high, medium, and low."

enum TaskPriority: String, AppEnum {
    case high   = "high"
    case medium = "medium"
    case low    = "low"
 
    static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "Priority")
    static var caseDisplayRepresentations: [TaskPriority: DisplayRepresentation] = [
        .high:   "High",
        .medium: "Medium",
        .low:    "Low",
    ]
}
 
struct AddTaskIntent: AppIntent {
    static var title: LocalizedStringResource = "Add Task"
    static var description = IntentDescription("Adds a new task to your app")
 
    @Parameter(title: "Task Name")
    var taskName: String
 
    @Parameter(title: "Priority", default: .medium)
    var priority: TaskPriority
 
    func perform() async throws -> some IntentResult & ProvidesDialog {
        let task = await TaskRepository.shared.add(
            name: taskName,
            priority: priority.rawValue
        )
        return .result(dialog: "Added \(task.name) with \(priority) priority")
    }
}

Say "Add report deadline with high priority" to Siri and the parameters map automatically. No manual string parsing, no regex matching — just type-safe intent parameters. When you have five or six intents in your app, this kind of structure makes maintenance dramatically more predictable.

Registering Siri Phrases with AppShortcutsProvider

Creating an AppIntent struct only makes your action visible as an action in the Shortcuts app. To let Siri invoke it by voice — without requiring the user to manually configure a shortcut first — you need AppShortcutsProvider:

struct TaskAppShortcuts: AppShortcutsProvider {
    static var appShortcuts: [AppShortcut] {
        AppShortcut(
            intent: AddTaskIntent(),
            phrases: [
                // \(.applicationName) is automatically replaced with your app's display name
                "Add a task in \(.applicationName)",
                "Add \(.taskName) in \(.applicationName)"
            ],
            shortTitle: "Add Task",
            systemImageName: "plus.circle"
        )
    }
}

No @main annotation needed. No registration call. Just include this struct in your build target and it registers automatically at app launch. This is one of the pieces Antigravity occasionally omits from generated code — worth checking your output explicitly.

One thing to keep in mind: Apple limits the number of AppShortcut entries per app, and the phrases need to contain your app name token. If Antigravity generates a phrase without \(.applicationName), Siri won't accept it.

Three Pitfalls in Antigravity-Generated App Intent Code

These are the issues I've hit consistently when using Antigravity to write App Intents. All three are things Antigravity gets wrong not from lack of knowledge, but from not having enough context about where the code runs.

1. UI updates inside perform() without MainActor

perform() runs off the main thread. Antigravity sometimes generates code that calls ViewModel or UIKit update methods directly inside perform(), which crashes at runtime with a threading violation.

// ❌ What Antigravity sometimes generates
func perform() async throws -> some IntentResult {
    viewModel.refreshTaskList()   // 💥 will crash
    return .result()
}
 
// ✅ Wrap UI work in MainActor.run
func perform() async throws -> some IntentResult {
    await MainActor.run {
        viewModel.refreshTaskList()
    }
    return .result()
}

The fix is straightforward once you know what to look for. I now add "wrap any ViewModel or UI calls in MainActor.run" to the end of any App Intents prompt I give Antigravity.

2. Older API usage when the iOS target isn't specified

If you don't mention your iOS target, Antigravity may default to iOS 16-compatible API patterns. The IntentResult protocol's associated type requirements differ between iOS 16 and 17, and some of the more expressive return type combinations only work on 17+. Specifying "iOS 17 and above" in the prompt consistently produces current API code.

3. Optional @Parameter behavior in Shortcuts

Declaring @Parameter(title: "Due Date", default: nil) doesn't always make the parameter optional in the Shortcuts app UI the way you'd intuit. In some configurations the Shortcuts app still prompts the user to fill it in. If a parameter should genuinely be skippable — meaning the user can run the intent without specifying it — test that explicitly in the Shortcuts app rather than trusting the generated code's comments.

What Antigravity Is Particularly Good at with App Intents

Beyond the initial scaffolding, there are a few App Intents tasks where Antigravity adds real value:

Expanding intents incrementally. Once you have a working intent, asking Antigravity to "add an intent to complete a task" or "add an intent to list today's tasks" generates consistent, correctly-structured code that fits your existing pattern. The first intent acts as implicit context.

Writing unit tests. Antigravity generates XCTestCase subclasses for App Intents that properly handle the async throws semantics of perform(). Testing intents is often overlooked, and having Antigravity scaffold these tests makes it easier to add coverage.

Localization. LocalizedStringResource in App Intents integrates with your .xcstrings file. Antigravity can generate the full set of localized strings for an intent when you provide the target locales.

Testing on Device vs. Simulator

Testing Siri integration is one area where the simulator falls meaningfully short. Voice recognition accuracy, phrase matching, and the visual flow of Siri's response dialog all behave differently on a real device. The basic testing flow:

  1. Build and install on device
  2. Settings → Siri & Search → find your app → enable "Ask Siri"
  3. "Hey Siri, [your registered phrase]"
  4. Test the non-voice path through the Shortcuts app separately

I find it useful to test with the screen locked, since that's often when users actually invoke Siri. App Intents that need to display information on screen require handling the locked-screen state explicitly — another detail Antigravity sometimes overlooks when generating code for intents that return .result(view:).

For combining App Intents with Live Activities and Dynamic Island — a pattern that works well for ongoing task tracking — see the Live Activities and Dynamic Island guide. If you're building a widget alongside your intent, the WidgetKit home widget guide covers the complementary setup in detail.


App Intents has become one of iOS's most quietly capable extension points. Siri, Spotlight search, Focus Filters, and widgets can all share the same intent definitions — and with iOS 26 likely to expand this surface area further, building familiarity with the framework now pays off compoundingly.

The most useful thing you can do with this article is pick one specific action your app already supports and write one intent for it. From that starting point, Antigravity becomes genuinely useful for expanding the intent set incrementally. The first build where Siri responds to a phrase you wrote is a satisfying moment that makes the initial setup feel worth it.

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 $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

App Dev2026-07-04
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.
App Dev2026-07-03
Before You Let Siri Run an Agent-Written App Intent — Classify by Side Effect and Gate the Destructive Ones
Letting Siri or an assistant run an Antigravity-generated App Intent without a gate means a destructive action can fire from a single voice command. Here is how I classify intents by side effect, gate the irreversible ones, and catch missing gates before push.
App Dev2026-06-15
Stop Adding a Ternary Every Time a New iPhone Ships: A Table-Driven Resolution Design
Every time a new resolution arrived—iPhone Air, 17 Pro—I was bolting another screen-size ternary onto a 29-branch pile. Here is how I reshaped that into a table-driven design where adding the next device is a one-line data change, with the actual Swift from my wallpaper apps.
📚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
See all →