app-intents
4
总安装量
3
周安装量
#49217
全站排名
安装命令
npx skills add https://github.com/makgunay/claude-swift-skills --skill app-intents
Agent 安装分布
opencode
3
claude-code
3
codex
3
cursor
3
gemini-cli
2
github-copilot
2
Skill 文档
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]
}
}