app-intents
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseApp Intents (iOS 26+)
App Intents (iOS 26+)
Implement, review, and extend App Intents to expose app functionality to Siri,
Shortcuts, Spotlight, widgets, Control Center, and Apple Intelligence.
实现、审查并扩展App Intents,将应用功能开放给Siri、Shortcuts、Spotlight、小组件、控制中心和Apple Intelligence调用。
Triage Workflow
处理工作流
Step 1: Identify the integration surface
步骤1:确定集成场景
Determine which system feature the intent targets:
| Surface | Protocol | Since |
|---|---|---|
| Siri / Shortcuts | | iOS 16 |
| Configurable widget | | iOS 17 |
| Control Center | | iOS 18 |
| Spotlight search | | iOS 18 |
| Apple Intelligence | | iOS 18 |
| Interactive snippets | | iOS 26 |
| Visual Intelligence | | iOS 26 |
确定该Intent适配的系统功能:
| 适配场景 | 协议 | 支持版本 |
|---|---|---|
| Siri / Shortcuts | | iOS 16 |
| 可配置小组件 | | iOS 17 |
| 控制中心 | | iOS 18 |
| Spotlight 搜索 | | iOS 18 |
| Apple Intelligence | | iOS 18 |
| 交互片段 | | iOS 26 |
| 视觉智能 | | iOS 26 |
Step 2: Define the data model
步骤2:定义数据模型
- Create shadow models (do NOT conform core data models directly).
AppEntity - Create types for fixed parameter choices.
AppEnum - Choose the right variant for resolution.
EntityQuery - Mark searchable entities with and
IndexedEntity.@Property(indexingKey:)
- 创建影子模型(不要直接让核心数据模型遵守该协议)。
AppEntity - 为固定参数选项创建类型。
AppEnum - 选择合适的变体来做数据解析。
EntityQuery - 用和
IndexedEntity标记可搜索的实体。@Property(indexingKey:)
Step 3: Implement the intent
步骤3:实现Intent
- Conform to (or a specialized sub-protocol).
AppIntent - Declare properties for all user-facing inputs.
@Parameter - Implement .
perform() async throws -> some IntentResult - Add for Shortcuts UI.
parameterSummary - Register phrases via .
AppShortcutsProvider
- 遵守协议(或对应的专用子协议)。
AppIntent - 为所有面向用户的输入声明属性。
@Parameter - 实现方法。
perform() async throws -> some IntentResult - 为Shortcuts UI添加。
parameterSummary - 通过注册触发短语。
AppShortcutsProvider
Step 4: Verify
步骤4:验证
- Build and run in Shortcuts app to confirm parameter resolution.
- Test Siri phrases with the intent preview in Xcode.
- Confirm Spotlight results for types.
IndexedEntity - Check widget configuration for intents.
WidgetConfigurationIntent
- 在Shortcuts应用中构建并运行,确认参数解析正常。
- 使用Xcode中的Intent预览测试Siri触发短语。
- 确认类型的Spotlight搜索结果正常。
IndexedEntity - 检查类型Intent的小组件配置功能正常。
WidgetConfigurationIntent
AppIntent Protocol
AppIntent 协议
The system instantiates the struct via , sets parameters, then calls
. Declare a and for Shortcuts UI.
init()perform()titleparameterSummaryswift
struct OrderSoupIntent: AppIntent {
static var title: LocalizedStringResource = "Order Soup"
static var description = IntentDescription("Place a soup order.")
@Parameter(title: "Soup") var soup: SoupEntity
@Parameter(title: "Quantity", default: 1) var quantity: Int
static var parameterSummary: some ParameterSummary {
Summary("Order \(\.$soup)") { \.$quantity }
}
func perform() async throws -> some IntentResult {
try await OrderService.shared.place(soup: soup.id, quantity: quantity)
return .result(dialog: "Ordered \(quantity) \(soup.name).")
}
}Optional members: (), (),
(), ().
descriptionIntentDescriptionopenAppWhenRunBoolisDiscoverableBoolauthenticationPolicyIntentAuthenticationPolicy系统会通过实例化结构体,设置参数,之后调用方法。需要声明和供Shortcuts UI展示。
init()perform()titleparameterSummaryswift
struct OrderSoupIntent: AppIntent {
static var title: LocalizedStringResource = "Order Soup"
static var description = IntentDescription("Place a soup order.")
@Parameter(title: "Soup") var soup: SoupEntity
@Parameter(title: "Quantity", default: 1) var quantity: Int
static var parameterSummary: some ParameterSummary {
Summary("Order \(\.$soup)") { \.$quantity }
}
func perform() async throws -> some IntentResult {
try await OrderService.shared.place(soup: soup.id, quantity: quantity)
return .result(dialog: "Ordered \(quantity) \(soup.name).")
}
}可选成员: ()、 ()、 ()、 ()。
descriptionIntentDescriptionopenAppWhenRunBoolisDiscoverableBoolauthenticationPolicyIntentAuthenticationPolicy@Parameter
@Parameter
Declare each user-facing input with . Optional parameters are not
required; non-optional parameters with a are pre-filled.
@Parameterdefaultswift
// WRONG: Non-optional parameter without default -- system cannot preview
@Parameter(title: "Count")
var count: Int
// CORRECT: Provide a default or make optional
@Parameter(title: "Count", default: 1)
var count: Int
@Parameter(title: "Count")
var count: Int?用声明每个面向用户的输入。可选参数不是必填项;带默认值的非可选参数会被自动预填。
@Parameterdefaultswift
// WRONG: Non-optional parameter without default -- system cannot preview
@Parameter(title: "Count")
var count: Int
// CORRECT: Provide a default or make optional
@Parameter(title: "Count", default: 1)
var count: Int
@Parameter(title: "Count")
var count: Int?Supported value types
支持的值类型
Primitives: , , , , , , .
Framework: , , . Measurements: ,
, and others. Custom: any or .
IntDoubleBoolStringURLDateDateComponentsCurrencyPersonIntentFileMeasurement<UnitLength>Measurement<UnitTemperature>AppEntityAppEnum基础类型:、、、、、、。
框架类型:、、。计量单位:、等其他类型。自定义类型:任意遵守或协议的类型。
IntDoubleBoolStringURLDateDateComponentsCurrencyPersonIntentFileMeasurement<UnitLength>Measurement<UnitTemperature>AppEntityAppEnumCommon initializer patterns
常见初始化模式
swift
// Basic
@Parameter(title: "Name")
var name: String
// With default
@Parameter(title: "Count", default: 5)
var count: Int
// Numeric slider
@Parameter(title: "Volume", controlStyle: .slider, inclusiveRange: (0, 100))
var volume: Int
// Options provider (dynamic list)
@Parameter(title: "Category", optionsProvider: CategoryOptionsProvider())
var category: Category
// File with content types
@Parameter(title: "Document", supportedContentTypes: [.pdf, .plainText])
var document: IntentFile
// Measurement with unit
@Parameter(title: "Distance", defaultUnit: .miles, supportsNegativeNumbers: false)
var distance: Measurement<UnitLength>See for all initializer variants.
references/appintents-advanced.mdswift
// Basic
@Parameter(title: "Name")
var name: String
// With default
@Parameter(title: "Count", default: 5)
var count: Int
// Numeric slider
@Parameter(title: "Volume", controlStyle: .slider, inclusiveRange: (0, 100))
var volume: Int
// Options provider (dynamic list)
@Parameter(title: "Category", optionsProvider: CategoryOptionsProvider())
var category: Category
// File with content types
@Parameter(title: "Document", supportedContentTypes: [.pdf, .plainText])
var document: IntentFile
// Measurement with unit
@Parameter(title: "Distance", defaultUnit: .miles, supportsNegativeNumbers: false)
var distance: Measurement<UnitLength>查看了解所有初始化变体。
references/appintents-advanced.mdAppEntity
AppEntity
Create shadow models that mirror app data -- never conform core data model
types directly.
swift
struct SoupEntity: AppEntity {
static let defaultQuery = SoupEntityQuery()
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Soup"
var id: String
@Property(title: "Name") var name: String
@Property(title: "Price") var price: Double
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(title: "\(name)", subtitle: "$\(String(format: "%.2f", price))")
}
init(from soup: Soup) {
self.id = soup.id; self.name = soup.name; self.price = soup.price
}
}Required: , (static), ,
(static). Mark properties with
to expose for filtering/sorting. Properties without remain internal.
iddefaultQuerydisplayRepresentationtypeDisplayRepresentation@Property(title:)@Property创建与应用数据映射的影子模型 -- 永远不要直接让核心数据模型类型遵守该协议。
swift
struct SoupEntity: AppEntity {
static let defaultQuery = SoupEntityQuery()
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Soup"
var id: String
@Property(title: "Name") var name: String
@Property(title: "Price") var price: Double
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(title: "\(name)", subtitle: "$\(String(format: "%.2f", price))")
}
init(from soup: Soup) {
self.id = soup.id; self.name = soup.name; self.price = soup.price
}
}必填项:、静态属性、、静态属性。用标记属性,即可开放给过滤/排序功能使用。没有加的属性会保持私有。
iddefaultQuerydisplayRepresentationtypeDisplayRepresentation@Property(title:)@PropertyEntityQuery (4 Variants)
EntityQuery (4种变体)
1. EntityQuery (base -- resolve by ID)
1. EntityQuery (基础版 -- 通过ID解析)
swift
struct SoupEntityQuery: EntityQuery {
func entities(for identifiers: [String]) async throws -> [SoupEntity] {
SoupStore.shared.soups.filter { identifiers.contains($0.id) }.map { SoupEntity(from: $0) }
}
func suggestedEntities() async throws -> [SoupEntity] {
SoupStore.shared.featured.map { SoupEntity(from: $0) }
}
}swift
struct SoupEntityQuery: EntityQuery {
func entities(for identifiers: [String]) async throws -> [SoupEntity] {
SoupStore.shared.soups.filter { identifiers.contains($0.id) }.map { SoupEntity(from: $0) }
}
func suggestedEntities() async throws -> [SoupEntity] {
SoupStore.shared.featured.map { SoupEntity(from: $0) }
}
}2. EntityStringQuery (free-text search)
2. EntityStringQuery (自由文本搜索)
swift
struct SoupStringQuery: EntityStringQuery {
func entities(matching string: String) async throws -> [SoupEntity] {
SoupStore.shared.search(string).map { SoupEntity(from: $0) }
}
func entities(for identifiers: [String]) async throws -> [SoupEntity] {
SoupStore.shared.soups.filter { identifiers.contains($0.id) }.map { SoupEntity(from: $0) }
}
}swift
struct SoupStringQuery: EntityStringQuery {
func entities(matching string: String) async throws -> [SoupEntity] {
SoupStore.shared.search(string).map { SoupEntity(from: $0) }
}
func entities(for identifiers: [String]) async throws -> [SoupEntity] {
SoupStore.shared.soups.filter { identifiers.contains($0.id) }.map { SoupEntity(from: $0) }
}
}3. EnumerableEntityQuery (finite set)
3. EnumerableEntityQuery (有限集合)
swift
struct AllSoupsQuery: EnumerableEntityQuery {
func allEntities() async throws -> [SoupEntity] {
SoupStore.shared.allSoups.map { SoupEntity(from: $0) }
}
func entities(for identifiers: [String]) async throws -> [SoupEntity] {
SoupStore.shared.soups.filter { identifiers.contains($0.id) }.map { SoupEntity(from: $0) }
}
}swift
struct AllSoupsQuery: EnumerableEntityQuery {
func allEntities() async throws -> [SoupEntity] {
SoupStore.shared.allSoups.map { SoupEntity(from: $0) }
}
func entities(for identifiers: [String]) async throws -> [SoupEntity] {
SoupStore.shared.soups.filter { identifiers.contains($0.id) }.map { SoupEntity(from: $0) }
}
}4. UniqueAppEntityQuery (singleton, iOS 18+)
4. UniqueAppEntityQuery (单例,iOS 18+)
Use for single-instance entities like app settings.
swift
struct AppSettingsEntity: UniqueAppEntity {
static let defaultQuery = AppSettingsQuery()
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Settings"
var displayRepresentation: DisplayRepresentation { "App Settings" }
var id: String { "app-settings" }
}
struct AppSettingsQuery: UniqueAppEntityQuery {
func entity() async throws -> AppSettingsEntity {
AppSettingsEntity()
}
}See for with
filter/sort support.
references/appintents-advanced.mdEntityPropertyQuery用于应用设置等单实例实体。
swift
struct AppSettingsEntity: UniqueAppEntity {
static let defaultQuery = AppSettingsQuery()
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Settings"
var displayRepresentation: DisplayRepresentation { "App Settings" }
var id: String { "app-settings" }
}
struct AppSettingsQuery: UniqueAppEntityQuery {
func entity() async throws -> AppSettingsEntity {
AppSettingsEntity()
}
}查看了解支持过滤/排序的。
references/appintents-advanced.mdEntityPropertyQueryAppEnum
AppEnum
Define fixed sets of selectable values. Must be backed by a
raw value (use ).
LosslessStringConvertibleStringswift
enum SoupSize: String, AppEnum {
case small, medium, large
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Size"
static var caseDisplayRepresentations: [SoupSize: DisplayRepresentation] = [
.small: "Small",
.medium: "Medium",
.large: "Large"
]
}swift
// WRONG: Using Int raw value
enum Priority: Int, AppEnum { // Compiler error -- Int is not LosslessStringConvertible
case low = 1, medium = 2, high = 3
}
// CORRECT: Use String raw value
enum Priority: String, AppEnum {
case low, medium, high
// ...
}定义固定的可选值集合。必须使用遵循协议的原始值(推荐用)。
LosslessStringConvertibleStringswift
enum SoupSize: String, AppEnum {
case small, medium, large
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Size"
static var caseDisplayRepresentations: [SoupSize: DisplayRepresentation] = [
.small: "Small",
.medium: "Medium",
.large: "Large"
]
}swift
// WRONG: Using Int raw value
enum Priority: Int, AppEnum { // Compiler error -- Int is not LosslessStringConvertible
case low = 1, medium = 2, high = 3
}
// CORRECT: Use String raw value
enum Priority: String, AppEnum {
case low, medium, high
// ...
}AppShortcutsProvider
AppShortcutsProvider
Register pre-built shortcuts that appear in Siri and the Shortcuts app without
user configuration.
swift
struct MyAppShortcuts: AppShortcutsProvider {
static var appShortcuts: [AppShortcut] {
AppShortcut(
intent: OrderSoupIntent(),
phrases: [
"Order \(\.$soup) in \(.applicationName)",
"Get soup from \(.applicationName)"
],
shortTitle: "Order Soup",
systemImageName: "cup.and.saucer"
)
}
static var shortcutTileColor: ShortcutTileColor = .navy
}注册预构建的快捷指令,无需用户配置即可出现在Siri和Shortcuts应用中。
swift
struct MyAppShortcuts: AppShortcutsProvider {
static var appShortcuts: [AppShortcut] {
AppShortcut(
intent: OrderSoupIntent(),
phrases: [
"Order \(\.$soup) in \(.applicationName)",
"Get soup from \(.applicationName)"
],
shortTitle: "Order Soup",
systemImageName: "cup.and.saucer"
)
}
static var shortcutTileColor: ShortcutTileColor = .navy
}Phrase rules
短语规则
- Every phrase MUST include .
\(.applicationName) - Phrases can reference parameters: .
\(\.$soup) - Call when dynamic option values change.
updateAppShortcutParameters() - Use to prevent false Siri activations.
negativePhrases
- 每个短语必须包含。
\(.applicationName) - 短语可以引用参数:。
\(\.$soup) - 动态选项值变更时调用。
updateAppShortcutParameters() - 使用避免Siri误触发。
negativePhrases
Siri Integration
Siri 集成
Donating intents
捐赠Intent
Donate intents so the system learns user patterns and suggests them in Spotlight:
swift
let intent = OrderSoupIntent()
intent.soup = favoriteSoupEntity
try await intent.donate()捐赠Intent以便系统学习用户使用习惯,并在Spotlight中给出相关建议:
swift
let intent = OrderSoupIntent()
intent.soup = favoriteSoupEntity
try await intent.donate()Predictable intents
可预测Intent
Conform to for Siri prediction of upcoming actions.
PredictableIntent遵守协议即可让Siri预测用户即将执行的操作。
PredictableIntentInteractive Widget Intents
交互式小组件 Intent
Use with / in widgets. Use
for configurable widget parameters.
AppIntentButtonToggleWidgetConfigurationIntentswift
struct ToggleFavoriteIntent: AppIntent {
static var title: LocalizedStringResource = "Toggle Favorite"
@Parameter(title: "Item ID") var itemID: String
func perform() async throws -> some IntentResult {
FavoriteStore.shared.toggle(itemID)
return .result()
}
}
// In widget view:
Button(intent: ToggleFavoriteIntent(itemID: entry.id)) {
Image(systemName: entry.isFavorite ? "heart.fill" : "heart")
}在小组件的/中使用。使用定义可配置的小组件参数。
ButtonToggleAppIntentWidgetConfigurationIntentswift
struct ToggleFavoriteIntent: AppIntent {
static var title: LocalizedStringResource = "Toggle Favorite"
@Parameter(title: "Item ID") var itemID: String
func perform() async throws -> some IntentResult {
FavoriteStore.shared.toggle(itemID)
return .result()
}
}
// 在小组件视图中:
Button(intent: ToggleFavoriteIntent(itemID: entry.id)) {
Image(systemName: entry.isFavorite ? "heart.fill" : "heart")
}WidgetConfigurationIntent
WidgetConfigurationIntent
swift
struct BookWidgetConfig: WidgetConfigurationIntent {
static var title: LocalizedStringResource = "Favorite Book"
@Parameter(title: "Book", default: "The Swift Programming Language") var bookTitle: String
}
// Connect to WidgetKit:
struct MyWidget: Widget {
var body: some WidgetConfiguration {
AppIntentConfiguration(kind: "FavoriteBook", intent: BookWidgetConfig.self, provider: MyTimelineProvider()) { entry in
BookWidgetView(entry: entry)
}
}
}swift
struct BookWidgetConfig: WidgetConfigurationIntent {
static var title: LocalizedStringResource = "Favorite Book"
@Parameter(title: "Book", default: "The Swift Programming Language") var bookTitle: String
}
// 连接到WidgetKit:
struct MyWidget: Widget {
var body: some WidgetConfiguration {
AppIntentConfiguration(kind: "FavoriteBook", intent: BookWidgetConfig.self, provider: MyTimelineProvider()) { entry in
BookWidgetView(entry: entry)
}
}
}Control Center Widgets (iOS 18+)
控制中心小组件 (iOS 18+)
Expose controls in Control Center and Lock Screen with
and .
ControlConfigurationIntentControlWidgetswift
struct LightControlConfig: ControlConfigurationIntent {
static var title: LocalizedStringResource = "Light Control"
@Parameter(title: "Light", default: .livingRoom) var light: LightEntity
}
struct ToggleLightIntent: AppIntent {
static var title: LocalizedStringResource = "Toggle Light"
@Parameter(title: "Light") var light: LightEntity
func perform() async throws -> some IntentResult {
try await LightService.shared.toggle(light.id)
return .result()
}
}
struct LightControl: ControlWidget {
var body: some ControlWidgetConfiguration {
AppIntentControlConfiguration(kind: "LightControl", intent: LightControlConfig.self) { config in
ControlWidgetToggle(config.light.name, isOn: config.light.isOn, action: ToggleLightIntent(light: config.light))
}
}
}通过和将控制功能开放到控制中心和锁屏。
ControlConfigurationIntentControlWidgetswift
struct LightControlConfig: ControlConfigurationIntent {
static var title: LocalizedStringResource = "Light Control"
@Parameter(title: "Light", default: .livingRoom) var light: LightEntity
}
struct ToggleLightIntent: AppIntent {
static var title: LocalizedStringResource = "Toggle Light"
@Parameter(title: "Light") var light: LightEntity
func perform() async throws -> some IntentResult {
try await LightService.shared.toggle(light.id)
return .result()
}
}
struct LightControl: ControlWidget {
var body: some ControlWidgetConfiguration {
AppIntentControlConfiguration(kind: "LightControl", intent: LightControlConfig.self) { config in
ControlWidgetToggle(config.light.name, isOn: config.light.isOn, action: ToggleLightIntent(light: config.light))
}
}
}Spotlight and IndexedEntity (iOS 18+)
Spotlight 和 IndexedEntity (iOS 18+)
Conform to for Spotlight search. On iOS 26+, use
for structured metadata:
IndexedEntityindexingKeyswift
struct RecipeEntity: IndexedEntity {
static let defaultQuery = RecipeQuery()
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Recipe"
var id: String
@Property(title: "Name", indexingKey: .title) var name: String // iOS 26+
@ComputedProperty(indexingKey: .description) // iOS 26+
var summary: String { "\(name) -- a delicious recipe" }
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(title: "\(name)")
}
}遵守协议即可支持Spotlight搜索。在iOS 26+中,使用定义结构化元数据:
IndexedEntityindexingKeyswift
struct RecipeEntity: IndexedEntity {
static let defaultQuery = RecipeQuery()
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Recipe"
var id: String
@Property(title: "Name", indexingKey: .title) var name: String // iOS 26+
@ComputedProperty(indexingKey: .description) // iOS 26+
var summary: String { "\(name) -- a delicious recipe" }
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(title: "\(name)")
}
}iOS 26 Additions
iOS 26 新增功能
SnippetIntent
SnippetIntent
Display interactive snippets in system UI:
swift
struct OrderStatusSnippet: SnippetIntent {
static var title: LocalizedStringResource = "Order Status"
func perform() async throws -> some IntentResult & ShowsSnippetView {
let status = await OrderTracker.currentStatus()
return .result(view: OrderStatusSnippetView(status: status))
}
static func reload() { /* notify system to refresh */ }
}
// A calling intent can display this snippet via:
// return .result(snippetIntent: OrderStatusSnippet())在系统UI中展示交互式片段:
swift
struct OrderStatusSnippet: SnippetIntent {
static var title: LocalizedStringResource = "Order Status"
func perform() async throws -> some IntentResult & ShowsSnippetView {
let status = await OrderTracker.currentStatus()
return .result(view: OrderStatusSnippetView(status: status))
}
static func reload() { /* notify system to refresh */ }
}
// 调用方Intent可以通过以下代码展示该片段:
// return .result(snippetIntent: OrderStatusSnippet())IntentValueQuery (Visual Intelligence)
IntentValueQuery (视觉智能)
swift
struct ProductValueQuery: IntentValueQuery {
typealias Input = String
typealias Result = ProductEntity
func values(for input: String) async throws -> [ProductEntity] {
ProductStore.shared.search(input).map { ProductEntity(from: $0) }
}
}swift
struct ProductValueQuery: IntentValueQuery {
typealias Input = String
typealias Result = ProductEntity
func values(for input: String) async throws -> [ProductEntity] {
ProductStore.shared.search(input).map { ProductEntity(from: $0) }
}
}Common Mistakes
常见错误
-
Conforming core data models to AppEntity. Create dedicated shadow models instead. Core models carry persistence logic that conflicts with intent lifecycle.
-
Missingin phrases. Every
\(.applicationName)phrase MUST include the application name token. Siri uses it for disambiguation.AppShortcut -
Non-optional @Parameter without default. The system cannot preview or pre-fill such parameters. Make non-optional parameters have a, or mark them optional.
defaultswift// WRONG @Parameter(title: "Count") var count: Int // CORRECT @Parameter(title: "Count", default: 1) var count: Int -
Using Int raw value for AppEnum.requires
AppEnumwhereRawRepresentable. UseRawValue: LosslessStringConvertible.String -
Forgetting. Without it, the Shortcuts picker shows no default options. Implement it on every
suggestedEntities().EntityQuery -
Throwing for missing entities in. Omit missing entities from the returned array instead of throwing an error.
entities(for:) -
Stale Spotlight index. Callwhen entity data changes. For
updateAppShortcutParameters(), re-donate or update the entity.IndexedEntity -
Missingon AppEntity or AppEnum. Both protocols require a static
typeDisplayRepresentation. Omitting it causes a compiler error that can be confusing.typeDisplayRepresentation -
Using deprecated. Use
@AssistantIntent(schema:)instead. The@AppIntent(schema:)macro was deprecated in iOS 18.4.@AssistantIntent -
Blocking perform() with synchronous work.is async -- use
perform()for I/O. Never block the thread with synchronous network calls.await
-
让核心数据模型直接遵守AppEntity协议。 请改为创建专用的影子模型。核心数据模型携带的持久化逻辑会与Intent生命周期冲突。
-
短语中缺少。 每个
\(.applicationName)短语都必须包含应用名称标记,Siri会用它来做指令消歧。AppShortcut -
非可选@Parameter没有默认值。 系统无法为这类参数生成预览或预填内容。请为非可选参数设置默认值,或将其标记为可选。
defaultswift// 错误写法 @Parameter(title: "Count") var count: Int // 正确写法 @Parameter(title: "Count", default: 1) var count: Int -
AppEnum使用Int类型作为原始值。要求
AppEnum的RawRepresentable遵循RawValue协议,请使用LosslessStringConvertible类型。String -
忘记实现。 没有该方法的话,Shortcuts选择器不会展示任何默认选项。请为每个
suggestedEntities()实现该方法。EntityQuery -
在中为缺失的实体抛出错误。 请直接从返回数组中省略缺失的实体,不要抛出错误。
entities(for:) -
Spotlight索引过期。 实体数据变更时请调用。对于
updateAppShortcutParameters(),请重新捐赠或更新实体。IndexedEntity -
AppEntity或AppEnum缺少。 这两个协议都要求实现静态的
typeDisplayRepresentation属性,省略会导致难以排查的编译错误。typeDisplayRepresentation -
使用已废弃的。 请改用
@AssistantIntent(schema:),@AppIntent(schema:)宏在iOS 18.4中已被废弃。@AssistantIntent -
在中执行同步阻塞任务。
perform()是异步方法,I/O操作请使用perform(),永远不要用同步网络请求阻塞线程。await
Review Checklist
审查清单
- Every has a descriptive
AppIntent(verb + noun, title case)title - types are optional or have defaults for system preview
@Parameter - types are shadow models, not core data model conformances
AppEntity - has
AppEntityanddisplayRepresentationtypeDisplayRepresentation - omits missing IDs (does not throw)
EntityQuery.entities(for:) - implemented on all entity queries
suggestedEntities() - uses
AppEnumraw value withStringcaseDisplayRepresentations - phrases include
AppShortcutsProvider\(.applicationName) - defined for Shortcuts UI readability
parameterSummary - properties use
IndexedEntityon iOS 26+@Property(indexingKey:) - Control Center intents conform to
ControlConfigurationIntent - Widget intents conform to
WidgetConfigurationIntent - No deprecated /
@AssistantIntentmacros@AssistantEntity - uses async/await, no synchronous blocking
perform() - runs in expected isolation context; intent parameter types are
perform()Sendable
- 每个都有描述清晰的
AppIntent(动词+名词,标题大小写)title - 类型要么是可选,要么带有默认值支持系统预览
@Parameter - 类型是影子模型,不是直接遵守协议的核心数据模型
AppEntity - 实现了
AppEntity和displayRepresentationtypeDisplayRepresentation - 会省略缺失的ID(不会抛出错误)
EntityQuery.entities(for:) - 所有实体查询都实现了
suggestedEntities() - 使用
AppEnum作为原始值并实现了StringcaseDisplayRepresentations - 的短语包含
AppShortcutsProvider\(.applicationName) - 实现了保证Shortcuts UI的可读性
parameterSummary - iOS 26+上的属性使用了
IndexedEntity@Property(indexingKey:) - 控制中心Intent遵守协议
ControlConfigurationIntent - 小组件Intent遵守协议
WidgetConfigurationIntent - 没有使用已废弃的/
@AssistantIntent宏@AssistantEntity - 使用async/await,没有同步阻塞逻辑
perform() - 运行在预期的隔离上下文中;Intent参数类型是
perform()Sendable
Reference Material
参考材料
- See for @Parameter variants, EntityPropertyQuery, assistant schemas, focus filters, SiriKit migration, error handling, confirmation flows, authentication, URL-representable types, and Spotlight indexing details.
references/appintents-advanced.md
- 查看了解@Parameter变体、EntityPropertyQuery、助手schema、专注过滤器、SiriKit迁移、错误处理、确认流程、鉴权、URL可表示类型以及Spotlight索引的详细信息。
references/appintents-advanced.md