Accelerating app interactions with App Intents
At a glance
| Item | Summary |
|---|---|
| Purpose | Demonstrate App Intents, entities, queries, shortcuts, Spotlight, foreground navigation, and workout controls. |
| Architecture | A SwiftUI trail app exposes thin intent/entity adapters over observable managers and value models. |
| Targets | The Xcode project contains host-app and watch-app targets; shared source uses platform conditionals for watchOS workout behavior. |
| Main pattern | The app registers long-lived managers once; @Dependency gives intents and entity queries the same behavior owners. |
Project structure
AppIntentsSampleApp/
├── AppIntentsSampleApp.swift
├── App Intents/
│ ├── Entities/
│ └── Workout Intents/
├── Model/ # trail, account, navigation, activity managers
├── Views/
└── Utililty/
The app and watch targets share source membership; there is no separate extension target or library package.
Overall architecture
flowchart LR
Root["AppIntentsSampleApp"] --> TrailManager["TrailDataManager"]
Root --> Account["AccountManager"]
Root --> Activity["ActivityTracker"]
Root --> Navigation["NavigationModel"]
Root --> Views["SwiftUI environment"]
Root --> DI["AppDependencyManager"]
DI --> Intents["App Intents"]
DI --> Queries["Entity queries"]
Queries --> Entity["TrailEntity"]
Intents --> Navigation
Reference code
AppIntentsSampleApp/AppIntentsSampleApp.swift:19 — composition happens at the earliest non-UI-dependent launch path (abridged).
let trailDataManager = TrailDataManager.shared
let navigationModel = NavigationModel(
selectedCollection: trailDataManager.nearMeCollection
)
let activityTracker = ActivityTracker.shared
AppDependencyManager.shared.add(dependency: trailDataManager)
AppDependencyManager.shared.add(dependency: navigationModel)
AppDependencyManager.shared.add(dependency: activityTracker)Ownership and state
classDiagram
AppIntentsSampleApp o-- TrailDataManager : shared
AppIntentsSampleApp *-- NavigationModel : scene state
AppIntentsSampleApp o-- AccountManager : shared
AppIntentsSampleApp o-- ActivityTracker : shared
ActivityTracker *-- Activity : history
ActivityTracker *-- LiveWorkoutManager : watchOS only
OpenFavorites --> NavigationModel : injected
TrailEntityQuery --> TrailDataManager : injected
Ownership evidence
AppIntentsSampleApp/Model/ActivityTracker.swift:14 — one manager owns activity history and conditionally owns the watch implementation.
@Observable class ActivityTracker: ActivityTrackingActions, @unchecked Sendable {
static let shared = ActivityTracker()
private(set) var activityHistory: [Activity]
private(set) var activityInProgress: Activity?
private var liveWorkoutManager: ActivityTrackingActions?
}Managers are the mutation authorities. Views receive them through SwiftUI environment; App Intents receive the same instances through App Intents dependency injection.
Class and protocol design
TrailEntity: AppEntity is deliberately separate from Trail, exposing only system-relevant data (AppIntentsSampleApp/App Intents/Entities/TrailEntity.swift:11). Intent structs adopt specialized framework protocols such as OpenIntent and StartWorkoutIntent. The private ActivityTrackingActions protocol at AppIntentsSampleApp/Model/ActivityTracker.swift:257 lets the cross-platform tracker substitute a watchOS HealthKit implementation without exposing that abstraction app-wide.
Access control
| Symbol | Access | Rationale |
|---|---|---|
| Root manager properties | private |
Only app composition injects them (AppIntentsSampleApp/AppIntentsSampleApp.swift:14). |
| Intent dependencies | private where callers need no access |
Keeps framework-populated collaborators implementation-only (AppIntentsSampleApp/App Intents/OpenFavorites.swift:46). |
| Activity history setters | private(set) |
Views/intents may read; only tracker transitions state. |
LiveWorkoutManager and strategy protocol |
private |
They are watchOS implementation details in one file. |
| Public API | none | Both targets compile app-owned source, not a distributable library. |
Logic ownership and placement
| Logic | Owner | Placement reason |
|---|---|---|
| Trail loading/querying and Spotlight | TrailDataManager + extension |
All entity/query paths use one catalog (AppIntentsSampleApp/Model/TrailDataManager.swift:90). |
| Foreground selection | NavigationModel |
UI state remains main-actor isolated. |
| Workout lifecycle/history | ActivityTracker |
Keeps platform-neutral history and watch-specific delegation together. |
| Entity translation | TrailEntity initializer/query |
System representation remains outside the domain value. |
| Intent orchestration | Individual intent perform() |
Thin adapters resolve input and call owners (AppIntentsSampleApp/App Intents/OpenFavorites.swift:33). |
Design patterns
| Pattern | Evidence | Purpose |
|---|---|---|
| Composition root / DI | App registers four managers | Supports background intent execution without requiring a scene. |
| Entity adapter | TrailEntity omits heavy images |
Keeps system-facing values stable and minimal. |
| Repository | TrailDataManager query API |
One trail source for UI, Spotlight, and intents. |
| Router | NavigationModel |
Intent-driven foreground navigation changes observable state. |
| Strategy protocol | ActivityTrackingActions |
Adds HealthKit only on watchOS. |
Naming conventions
- System entry points use verb-first names (
OpenFavorites,SuggestTrails,StartTrailActivity). - Framework roles use suffixes (
Entity,EntityQuery,OptionsProvider,Manager,Tracker). - Domain values remain singular (
Trail,Activity); owning collections/managers use broader nouns.
Architecture takeaways
- Register intent dependencies before any code assumes a visible scene.
- Keep entity schemas separate when the app model contains expensive or irrelevant fields.
- Use one mutation owner for UI and system entry points.
- Treat the watch target as a platform specialization of shared architecture, not an extension process.
Source map
| Source | Role |
|---|---|
AppIntentsSampleApp/AppIntentsSampleApp.swift:51 |
SwiftUI environment composition |
AppIntentsSampleApp/Model/NavigationModel.swift:13 |
Main-actor UI state |
AppIntentsSampleApp/Model/TrailDataManager.swift:12 |
Trail repository |
AppIntentsSampleApp/App Intents/Entities/TrailEntity.swift:19 |
System entity adapter |
AppIntentsSampleApp/App Intents/Workout Intents/StartTrailActivity.swift:16 |
Watch workout intent |