app-intents
SKILL.md
AppIntents — Siri, Shortcuts & Spotlight
Critical Constraints
- ❌ DO NOT use old SiriKit
INIntent→ ✅ UseAppIntentprotocol - ❌ DO NOT use
NSUserActivityalone for Spotlight → ✅ UseIndexedEntity+CSSearchableIndex.default().indexAppEntities() - ❌ DO NOT hardcode foreground-only intents → ✅ Use
supportedModesfor flexible execution
Basic App Intent
import AppIntents
struct FindNearestLandmarkIntent: AppIntent {
static var title: LocalizedStringResource = "Find Nearest Landmark"
@Parameter(title: "Category")
var category: String?
func perform() async throws -> some IntentResult {
let landmark = await findNearestLandmark(category: category)
return .result(value: landmark)
}
}
Intent Modes (Background/Foreground Control)
struct GetCrowdStatusIntent: AppIntent {
static let supportedModes: IntentModes = [.background, .foreground(.dynamic)]
func perform() async throws -> some ReturnsValue<Int> & ProvidesDialog {
guard await modelData.isOpen(landmark) else {
return .result(value: 0, dialog: "Currently closed.")
}
if systemContext.currentMode.canContinueInForeground {
do {
try await continueInForeground(alwaysConfirm: false)
await navigator.navigateToCrowdStatus(landmark)
} catch { }
}
let status = await modelData.getCrowdStatus(landmark)
return .result(value: status, dialog: "Crowd level: \(status)")
}
}
Mode combinations:
[.background, .foreground]— foreground default, background fallback[.background, .foreground(.dynamic)]— background default, can request foreground[.background, .foreground(.deferred)]— background first, guaranteed foreground later
Property Macros
struct LandmarkEntity: IndexedEntity {
// Computed — reads from source of truth
@ComputedProperty
var isFavorite: Bool { UserDefaults.standard.favorites.contains(id) }
// Deferred — expensive, fetched only when requested
@DeferredProperty
var crowdStatus: Int {
get async throws { await modelData.getCrowdStatus(self) }
}
}
Spotlight Integration
struct LandmarkEntity: AppEntity, IndexedEntity {
static var typeDisplayRepresentation = TypeDisplayRepresentation(
name: "Landmark", systemImage: "mountain.2"
)
var id: String
var name: String
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(title: "\(name)", image: .init(systemName: "mountain.2"))
}
var searchableAttributes: CSSearchableItemAttributeSet {
let attrs = CSSearchableItemAttributeSet()
attrs.title = name
return attrs
}
}
// Index entities
try await CSSearchableIndex.default().indexAppEntities(landmarks, priority: .normal)
// Remove from index
try await CSSearchableIndex.default().deleteAppEntities(identifiedBy: [id], ofType: LandmarkEntity.self)
Interactive Snippets
struct LandmarkSnippetIntent: SnippetIntent {
@Parameter var landmark: LandmarkEntity
var snippet: some View {
VStack {
Text(landmark.name).font(.headline)
HStack {
Button("Add to Favorites") { }
Button("Search Tickets") { }
}
}.padding()
}
}
App Shortcuts
struct AppShortcuts: AppShortcutsProvider {
static var appShortcuts: [AppShortcut] {
AppShortcut(
intent: FindNearestLandmarkIntent(),
phrases: ["Find the closest landmark with \(.applicationName)"],
systemImageName: "location"
)
}
}
Swift Package Support
// In framework
public struct LandmarksKitPackage: AppIntentsPackage { }
// In app target
struct LandmarksPackage: AppIntentsPackage {
static var includedPackages: [any AppIntentsPackage.Type] {
[LandmarksKitPackage.self]
}
}
References
Weekly Installs
4
Repository
makgunay/claude…t-skillsFirst Seen
Feb 14, 2026
Security Audits
Installed on
opencode4
claude-code4
codex4
cursor4
gemini-cli3
github-copilot3