An app intent exposes a meaningful action with parameters and a result. Start from the user action and entity identity, then validate the declarations against the installed SDK.

User request → Intent parameters → Domain operation → Result and presentation
  1. 1User request
  2. 2Intent parameters
  3. 3Domain operation
  4. 4Result and presentation

Work through the example

Keep the domain operation testable outside the intent. Test missing entities and permission denial rather than assuming invocation succeeds.

Start with a disposable branch and synthetic data. Write the expected outcome before changing the implementation, then keep the first failing result. This prevents a later repair from quietly redefining the task. The procedure below is grounded in the repository reference; its examples must still be checked against your project and installed toolchain.

Implementation reference

The following focused section is adapted from the maintained project guide. It preserves the source’s examples and limitations.

AppIntent Protocol and perform()

Every App Intent conforms to the AppIntent protocol and implements a perform() method that returns an IntentResult.

import AppIntents

struct OpenArticleIntent: AppIntent {
    // Title shown in Shortcuts and Siri
    static var title: LocalizedStringResource = "Open Article"
    static var description: IntentDescription = "Opens a specific article in the app."

    // The system can open your app when this intent runs
    static var openAppWhenRun: Bool = true

    @Parameter(title: "Article Name")
    var articleName: String

    @MainActor
    func perform() async throws -> some IntentResult & ProvidesDialog {
        // Look up the article and navigate to it
        guard let article = ArticleStore.shared.find(byName: articleName) else {
            throw ArticleError.notFound(articleName)
        }

        NavigationManager.shared.navigate(to: article)

        return .result(dialog: "Opening \"\(article.title)\"")
    }
}

enum ArticleError: Error, CustomLocalizedStringResourceConvertible {
    case notFound(String)

    var localizedStringResource: LocalizedStringResource {
        switch self {
        case .notFound(let name):
            return "Could not find article \"\(name)\""
        }
    }
}

Acceptance and failure review

Checkpoint What to inspect If it does not match
User request Confirm the input and environment Preserve the failure and return to this step
Intent parameters Inspect the intermediate artifact Preserve the failure and return to this step
Domain operation Run the focused check Preserve the failure and return to this step
Result and presentation Record the observed result Preserve the failure and return to this step

Ask the agent to explain the smallest change that resolves the observed mismatch. Keep unrelated refactors out of the repair. A change that makes a warning disappear is not enough if the behavior or ownership contract has changed. Re-run the same acceptance check so the before and after results are comparable.

Evidence and limits

Do not infer Siri behavior from a static review or claim new SDK deprecations without checking Apple documentation.

This is an educational guide. Its presence in the series does not certify a completed client-specific lab. The series evidence record separates executed checks from exercises and blocked environments.

Inspect the source used in this lesson.

What to do next

Next: On-device AI: Foundation Models readiness and fallback design