This documentation is available as Markdown for AI agents and LLMs. See the full Markdown index or append .md to any documentation URL.
This is documentation for the next SDK version.
Expo AppIntents
一个用于从 Expo 应用中公开 Apple App Intents、Siri、Shortcuts、Spotlight 和 Apple Intelligence 入口的库。
重要 此库目前处于 alpha 阶段,并且会频繁发生破坏性变更。
expo-app-intents 从你的应用中公开 Apple App Intents。它将 intent 声明保存在 Swift 内联模块中,以便 Apple 的构建时元数据提取器能够找到它们。此软件包提供 JavaScript API、原生调用队列、实体存储、配置插件,以及用于原生内联模块代码的 app-intents 初始目录。
你无法在运行时通过 JavaScript 动态创建 App Intent 类型。请在 Swift 中声明所需的 AppIntent 类型。带参数的 intent 还需要 AppEntity 和 EntityQuery 类型。如果你的应用公开快捷指令短语,通常还需要声明 AppShortcutsProvider。如果应用不使用快捷指令短语(例如只公开 schema intent),则可以省略它。使用此软件包将调用传递给 JavaScript,并保持动态实体值同步。
Apple Intelligence 支持
使用 expo-app-intents,你可以实现 Apple Intelligence 功能,包括屏幕内容智能。在 iOS 18.4 及更高版本中,你可以标记视图所表示的实体,以便 Siri 根据屏幕上显示的内容执行操作。对 @expo/ui 视图应用 appEntityIdentifier() 修饰符,或使用 AppEntityView 包装 React Native 视图。两者都要求项目使用 Xcode 27 或更高版本编译。在使用更早版本 Xcode 构建的项目中,视图仍会正常渲染,但系统无法将视图与实体关联。
安装
If you are installing this in an existing React Native app, make sure to install expo in your project.
在应用配置中配置
初始化程序会为你写入所需的配置:
Example app.json with config plugin
如果使用 app-intents 以外的目录,请将相同的目录名称传递给配置插件:
Example app.json with config plugin
工作原理
App Intents 与大多数 Expo API 不同,因为 Apple 会在构建时发现它们。将描述 intent、实体、查询和快捷指令短语的 Swift 类型编译到应用目标中。随后,Xcode 的 App Intents 元数据处理器便可以提取这些类型。expo-app-intents 使用 Expo 内联模块来处理这些应用目标 Swift 代码,并提供一个 Expo 软件包,用于 JavaScript API、原生存储和事件传递。
Expo 内联模块会监视生成的 app-intents 目录,其中包含由你的应用管理的 Swift 代码:
- AppShortcuts.swift 会在所选示例使用快捷指令短语时定义具体的
AppShortcutsProvider。快捷指令短语是静态元数据。构建时会将它们编译到应用中,JavaScript 无法动态添加它们。 - AppIntentsSetup.swift 包含所选功能所需的应用目标设置。当应用包含
AppShortcutsProvider时,此文件会将AppShortcuts.updateAppShortcutParameters()连接到expo-app-intents运行时。 - 其余 Swift 文件定义具体的
AppIntent、AppEntity和EntityQuery类型。它们会编译到应用目标中。只有包含 Expo 模块定义的文件才会由 Expo 注册为模块。
intent 运行时,请在其 perform() 方法中完成 Siri 或快捷指令所需的原生操作。然后将 intent 分发给 JavaScript:
await AppIntentDispatcher.shared.dispatch( name: "orderFood", params: ["dishId": .string(dish.id), "dishName": .string(dish.name)] )
AppIntentDispatcher 是一个 Swift actor。它会先将调用记录到原生存储中。如果 JavaScript 运行时已经在监听,接着会发出 onIntent 事件。此过程提供至少一次传递。请通过 id 处理每次调用,并在应用操作后调用 removePendingInvocationAsync(id)。
如果 Siri 或快捷指令在 JavaScript 未运行时执行,调用会留在待处理队列中。应用启动时,useAppIntents() 会使用当前待处理调用调用你的处理程序,并将 newIntent 设为 null。每次实时调用都会使用新的调用再次触发处理程序。你也可以通过 getPendingInvocationsAsync() 读取队列。
带参数的 intent 使用原生实体。例如,餐厅模板在 Swift 中定义 DishEntity 和 DishQuery。JavaScript 使用 setEntityCatalogAsync('dish', dishes) 提供当前菜品列表。原生查询通过 AppIntentEntityStore.shared 读取该目录,因此 Siri 和快捷指令可以按标识符、标题或同义词解析口述值。调用 setEntityCatalogAsync() 会替换目录并请求刷新快捷指令参数。值发生变化时,请再次调用它。
schema intent(例如邮件示例)也在应用目标的 Swift 代码中定义。Apple 将应用 schema 域分为两组。Apple Intelligence 和 Siri 可以发现符合 primary 域(例如 mail、photos 或 files)的类型。_仅限快捷指令_的域(例如 journal、books 或 reader)则只会出现在快捷指令 app 中。
schema intent 不需要快捷指令短语。它符合 schema,因此可以被发现。Xcode 会在构建时提取 App Intents 元数据,因此 Siri 和快捷指令 app 无需在你的 AppShortcutsProvider 中添加条目,就能使用该操作。因此,邮件示例不会添加任何 AppShortcut。如果你选择的示例都没有添加 AppShortcut,初始化程序就不会写入 AppShortcuts.swift。
如果 AppShortcutsProvider 的 appShortcuts 主体不包含任何 AppShortcut,Xcode 会报错:'AppShortcutsProvider' property 'appShortcuts' requires builder syntax。有需要声明的短语时,请添加 AppShortcuts.swift。
仅当你想要编译启动短语(例如“在 MyApp 中点餐”)时,才添加 AppShortcut。你无法有条件地添加短语,因此引用了受可用性限制的 intent 的 provider 必须使用相同的可用性限制。在这种情况下,可以考虑编写一个普通的包装 intent。
系统还必须能够解析 schema 实体。它的默认查询必须是 EntityStringQuery,或者该实体必须已编入索引。Xcode 在提取 App Intents 元数据时会拒绝普通的 EntityQuery。
原生 Swift 概念
初始化程序提供了可运行的 Swift 示例,但 intent 声明属于你的应用。以下各节介绍了你在调整示例或编写自己的 intent 时会用到的主要原生组件。
将调用分发给 JavaScript
intent 的 perform() 方法会在 Swift 中生成 Siri 或快捷指令所需的结果。调用 AppIntentDispatcher.shared.dispatch() 还可以为 JavaScript 记录一次调用:
import AppIntents internal import ExpoAppIntents struct SaveNoteIntent: AppIntent { static let title: LocalizedStringResource = "Save Note" static let openAppWhenRun = true @Parameter(title: "Text") var text: String @MainActor func perform() async throws -> some IntentResult & ProvidesDialog { await AppIntentDispatcher.shared.dispatch( name: "saveNote", params: ["text": .string(text)] ) return .result(dialog: "Your note was saved.") } }
分发是单向的。它会保存调用并立即返回,不会等待 JavaScript,因为 intent 可能会在应用关闭时运行。请从 Swift 返回对话框、值或其他面向系统的结果。应用运行时,使用 JavaScript 处理程序更新应用状态。
params 接受与 JSON 兼容的 AppIntentValue 值:
该值类型也支持 Swift 字面量,因此你可以直接使用 "note"、1、true、数组和字典等值。
在 Swift 中读取动态实体
使用 setEntityCatalogAsync() 从 JavaScript 发布当前实体值。你的 Swift EntityQuery 会通过 AppIntentEntityStore.shared 读取同一目录:
struct NoteQuery: EntityStringQuery { func entities(for identifiers: [String]) async throws -> [NoteEntity] { return try await AppIntentEntityStore.shared .entities(ofKind: "note", matching: identifiers) .map(NoteEntity.init(record:)) } func suggestedEntities() async throws -> [NoteEntity] { return try await AppIntentEntityStore.shared .entities(ofKind: "note") .map(NoteEntity.init(record:)) } }
kind 字符串必须与传递给 setEntityCatalogAsync() 的值一致。每个 AppIntentEntityRecord 都包含 id 和显示用的 title。它还可以包含 subtitle 和 synonyms。请在 init(record:) 等初始化器中将记录转换为具体的 AppEntity。设备会存储目录,因此请保持目录精简,只发布 Siri 和快捷指令所需的值。
按需添加快捷指令短语
如果你想要编译启动短语(例如“在 MyApp 中保存笔记”),请使用 AppShortcutsProvider。
用法
安装软件包后,从项目根目录运行初始化程序:
该命令会添加配置插件,为顶层 app-intents 目录启用内联模块,并写入初始 Swift 文件。
该命令还会询问要包含哪些示例,且默认只选中 minimal。此选项会写入设置模块,但不包含 intent、快捷指令短语或下文中的任何示例。传入 --examples 可在不显示提示的情况下选择示例。此标志也是在非交互模式下选择示例的唯一方式:
--examples 接受 minimal、counter、restaurant、mail 或 all。all 选项会选择本页面介绍的三个示例。当 CI 设置为 1 或 true、EXPO_NONINTERACTIVE 有值,或标准输入未连接到交互式终端时,命令会以非交互模式运行。在这些情况下,命令会在不显示提示的情况下生成 minimal 模板。
init 会保留所选目录中已有的 Swift 文件。如果你再次运行它来添加另一个示例,请在重新构建之前检查命令的警告。部分现有文件可能需要手动进行少量更新。
传入 --dir 可在 app-intents 以外的目录中生成模块。初始化程序会将相同的目录名称写入配置插件和受监视目录:
请将 App Intents 目录保留在项目根目录,就像默认的 app-intents 目录一样。此位置便于查找原生声明,并使其与应用的其余部分分开。
在邮件示例中传入 --visual-intelligence,即可生成其 Spotlight 集成代码。这会添加原生代码,使草稿可供搜索,并允许系统传递和打开草稿。它还会注册 mailDraft 实体 kind,以便 appEntityIdentifier() 可以标识视图所表示的草稿。生成的代码扩展了基础邮件类型,因此无论是否使用此标志,示例都能以相同方式运行。
然后重新构建原生项目:
现在,你的应用已配置为使用 App Intents。根据所选示例,你可以添加处理程序,或通过 JavaScript 填充 App Intents:
将屏幕上的视图连接到实体
在 iOS 18.4 及更高版本中,appEntityIdentifier() 会告知 Siri @expo/ui SwiftUI 视图所表示的实体,而 AppEntityView 则对 React Native 视图执行相同操作。两者都要求项目使用 Xcode 27 或更高版本编译,因为它们使用的 Apple API 在较早版本的 SDK 中不可用。请在原生注册、JavaScript 目录和视图中使用相同的实体 kind 和标识符。
--visual-intelligence 邮件示例会在 AppIntentsSetup.swift 中注册 mailDraft:
OnCreate { if #available(iOS 18.0, *) { AppEntityIdentifierRegistry.shared.registerIndexed( "mailDraft", as: MailDraftEntity.self ) } }
registerIndexed() 会让屏幕内容智能能够访问标识符,并使 Spotlight 与实体目录保持同步。自定义的已编入索引实体必须遵循 IndexedEntity 和 AppIntentEntityRecordConvertible。如果要在不使用 Spotlight 索引的情况下支持屏幕内容智能,请改用 AppEntityIdentifierRegistry.shared.register() 注册实体。
使用相同的 mailDraft kind 从 JavaScript 发布实体:
await AppIntents.setEntityCatalogAsync('mailDraft', [ { id: 'release-notes', title: 'Release notes for review', subtitle: 'The release notes are ready for a final pass.', }, ]);
然后将匹配的 kind 和标识符附加到 @expo/ui 视图:
import { Column, Host, Text } from '@expo/ui'; import * as AppIntents from 'expo-app-intents'; export function MailDraftCard({ draft }: { draft: { id: string; subject: string } }) { return ( <Host matchContents={{ vertical: true }}> <Column modifiers={[AppIntents.appEntityIdentifier('mailDraft', draft.id)]}> <Text>{draft.subject}</Text> </Column> </Host> ); }
要标记 React Native 视图,请使用相同的 kind 和标识符将它们包装在 AppEntityView 中:
import { Text } from 'react-native'; import * as AppIntents from 'expo-app-intents'; export function MailDraftCard({ draft }: { draft: { id: string; subject: string } }) { return ( <AppIntents.AppEntityView entity="mailDraft" entityId={draft.id}> <Text>{draft.subject}</Text> </AppIntents.AppEntityView> ); }
在较早版本的 iOS 中、使用早于 27 的 Xcode 版本编译的项目中,或者 kind 未注册时,这两种视图仍会正常渲染。在这些情况下,系统无法将视图与实体关联,且 expo-app-intents 只会记录一次警告。
处理计数器示例
计数器示例会分发 increaseCounter 调用。请在应用根部附近挂载一次此钩子。这样,它就能处理 JavaScript 未运行时捕获的待处理调用,以及应用运行时收到的实时调用。
传递至少会发生一次,因此同一调用可能会多次到达你的处理程序。只有在 removePendingInvocationAsync(id) 成功完成后,调用才会从待处理队列中移除。每次实时调用都会传递该队列的新快照。
请在 ref 中保留已应用调用的标识符,并跳过这些调用。餐厅和邮件示例使用相同的防重复处理逻辑。ref 只能在单个会话内去重调用。如果 removePendingInvocationAsync() 失败,该调用会在下次启动时仍处于待处理状态,且处理程序会再次应用它。如果无法安全地重复应用某个操作,请持久化已处理标识符。队列最多容纳 100 次调用,超出时会丢弃最早的调用以腾出空间。若应用从不移除调用,最终这些调用会丢失。
import { useRef, useState } from 'react'; import { Text, View } from 'react-native'; import * as AppIntents from 'expo-app-intents'; export function CounterIntentHandler() { const [count, setCount] = useState(0); const [lastIntentId, setLastIntentId] = useState<string | null>(null); const handledIds = useRef(new Set<string>()); AppIntents.useAppIntents(async pendingIntents => { for (const invocation of pendingIntents) { if (invocation.name !== 'increaseCounter' || handledIds.current.has(invocation.id)) { continue; } handledIds.current.add(invocation.id); setCount(value => value + 1); setLastIntentId(invocation.id); await AppIntents.removePendingInvocationAsync(invocation.id); } }); return ( <View> <Text>Counter: {count}</Text> {lastIntentId ? <Text>Last opened by Siri: {lastIntentId}</Text> : null} </View> ); }
处理餐厅示例
餐厅示例使用动态 dish 实体目录。应用启动后,从 JavaScript 填充目录,然后处理 orderFood 调用。调用 setEntityCatalogAsync() 会替换原生目录并刷新快捷指令参数。随后,Siri 和快捷指令可以按标识符、标题或同义词解析菜品。请处理被拒绝的 promise,以便将目录更新失败记录到日志中,而不是产生未处理的拒绝。
import { useEffect, useRef, useState } from 'react'; import { Text, View } from 'react-native'; import * as AppIntents from 'expo-app-intents'; const dishes = [ { id: 'margherita-pizza', title: 'Margherita Pizza', synonyms: ['margherita', 'pizza'] }, { id: 'spaghetti-carbonara', title: 'Spaghetti Carbonara', synonyms: ['carbonara'] }, { id: 'tiramisu', title: 'Tiramisu', synonyms: ['dessert'] }, ]; export function RestaurantIntentHandler() { const [latestOrder, setLatestOrder] = useState<string | null>(null); const handledIds = useRef(new Set<string>()); useEffect(() => { AppIntents.setEntityCatalogAsync('dish', dishes).catch(error => console.warn('Could not publish the dish catalog.', error) ); }, []); AppIntents.useAppIntents(async pendingIntents => { for (const invocation of pendingIntents) { if (invocation.name !== 'orderFood' || handledIds.current.has(invocation.id)) { continue; } handledIds.current.add(invocation.id); setLatestOrder(String(invocation.params.dishName || invocation.params.dishId)); await AppIntents.removePendingInvocationAsync(invocation.id); } }); return ( <View> <Text>{latestOrder ? `Latest order: ${latestOrder}` : 'No orders yet.'}</Text> </View> ); }
处理邮件示例
邮件示例采用 Apple 的 mail 架构域,且没有快捷指令短语。你可以通过 Siri、Apple Intelligence 或“快捷指令”App 使用这两个意图。createMailDraft 会发送 id、subject、body、recipients 和 attachmentCount。deleteMailDrafts 会发送一个 ids 数组。由于删除操作具有破坏性,架构要求进行本地设备认证,并会在意图运行前显示确认提示。
DeleteDraftIntent 接收一个 MailDraftEntity 值数组,因此一次调用可以删除多个草稿。MailDraftEntityQuery 会从 mailDraft 目录中解析这些实体。每当草稿发生变化时,都要发布目录。否则,Siri 和“快捷指令”App 无法提供草稿,删除意图也无法运行。每条记录都将 title 映射到主题,将 subtitle 映射到正文。原生存储会在重新启动后保留目录,因此不要在草稿加载前发布空目录。
为每条记录提供非空的 title。如果任一实体的 id 或 title 为空,setEntityCatalogAsync() 会拒绝该目录,因为 Siri 无法解析该实体。拒绝操作会丢弃整个更新,并保留之前的目录。创建时没有主题的草稿会提供空字符串 subject,而不是 undefined。回退值请使用 || 而非 ??,因为 ?? 会保留空字符串。
import { useEffect, useRef, useState } from 'react'; import { FlatList, Text } from 'react-native'; import * as AppIntents from 'expo-app-intents'; type MailDraft = { id: string; subject: string; body: string; }; export function MailIntentHandler() { const [drafts, setDrafts] = useState<MailDraft[]>([]); const handledIds = useRef(new Set<string>()); const hasPublished = useRef(false); useEffect(() => { // 原生目录的生命周期长于 App,但 `drafts` 初始为空,因此在首次渲染时发布会清除之前启动时写入的目录。请等待草稿就绪。 if (drafts.length === 0 && !hasPublished.current) { return; } hasPublished.current = true; AppIntents.setEntityCatalogAsync( 'mailDraft', drafts.map(draft => ({ id: draft.id, title: draft.subject, subtitle: draft.body })) ).catch(error => console.warn('无法发布邮件草稿目录。', error)); }, [drafts]); AppIntents.useAppIntents(async pendingIntents => { for (const invocation of pendingIntents) { const isMailIntent = invocation.name === 'createMailDraft' || invocation.name === 'deleteMailDrafts'; if (!isMailIntent || handledIds.current.has(invocation.id)) { continue; } handledIds.current.add(invocation.id); if (invocation.name === 'createMailDraft') { const body = String(invocation.params.body ?? ''); const draft = { id: String(invocation.params.id || invocation.id), // 使用 `||`,而非 `??`:没有主题的草稿会以空字符串传入。 subject: String(invocation.params.subject || body.slice(0, 40) || '无主题'), body, }; setDrafts(current => [draft, ...current]); } else { const ids = new Set((invocation.params.ids as string[] | undefined) ?? []); setDrafts(current => current.filter(draft => !ids.has(draft.id))); } await AppIntents.removePendingInvocationAsync(invocation.id); } }); return ( <FlatList data={drafts} keyExtractor={draft => draft.id} renderItem={({ item }) => ( <Text> {item.subject}: {item.body} </Text> )} /> ); }
从 JavaScript 为带参数的意图提供动态值:
await AppIntents.setEntityCatalogAsync('dish', [ { id: 'margherita-pizza', title: 'Margherita Pizza', synonyms: ['margherita', 'pizza'] }, { id: 'spaghetti-carbonara', title: 'Spaghetti Carbonara', synonyms: ['carbonara'] }, ]);
生成的实体查询会通过 await AppIntentEntityStore.shared.entities(ofKind:) 读取这些值。随后,Siri 和“快捷指令”即可将它们作为意图参数提供并解析。
在实体上设置 hideInSpotlight,即可将其从 Spotlight 索引中排除,同时仍可解析该实体。Siri 仍可以将其作为参数提供,并通过标识符打开它:
await AppIntents.setEntityCatalogAsync('mailDraft', [ { id: 'release-notes', title: '待审核的发行说明' }, { id: 'salary-review', title: '薪资审核', hideInSpotlight: true }, ]);
该标记仅适用于通过 registerIndexed 在原生端注册的实体。切换该标记后,会在下次发布目录时生效,已经建立索引的实体将被移除。若要让实体完全不对 Siri 显示,请不要将其添加到目录中。未发布的实体无法被提供、匹配或解析。
经典 App 快捷指令短语最多只能插入一个非数组参数。生成的餐厅点餐示例声明了四个短语:
"Place an order in \(.applicationName)", "Order food in \(.applicationName)", "Order \(\.$dish) in \(.applicationName)", "Place an order for \(\.$dish) in \(.applicationName)"
前两个短语不插入参数,因此 Siri 会询问你想要哪道菜。后两个短语会插入 dish 参数。借助此参数,Siri 可以运行“一次性”命令,例如“在 MyApp 中点一份提拉米苏”。带参数的短语可以在“快捷指令”中创建预填充的磁贴。如果旧版本创建的磁贴使用了过期的参数值,请将其删除,以便 iOS 根据当前 App 元数据重新创建。
平台限制
- 原生运行时支持 iOS 16.4 及更高版本、tvOS 16.4 及更高版本,以及 macOS 13.4 及更高版本。
expo-app-intents在 Expo Go 中不可用。 - 本页的初始化程序、捆绑示例和构建命令均以 iOS 为重点。单个 App Intents API 和架构域可能需要更新版本的操作系统,也可能无法在其他 Apple 平台上使用。
- App 快捷指令短语会编译进 App,无法通过 JavaScript 动态添加。
- 每个 App 快捷指令短语都必须包含
\(.applicationName)。 - 单个 App 快捷指令短语最多只能插入一个非数组参数。
- 每个 App 最多可以定义 10 个 App 快捷指令。
- 架构示例要求 iOS 18 或更高版本,并且必须匹配 Apple 的受支持 App Intent 域之一。只有主域才能被 Apple Intelligence 和 Siri 发现。
- 有些架构所需的 iOS 版本可能高于其所属域所需的版本。例如,
.mail.openDraft和.system.open要求 iOS 27 或更高版本。
其他资源
从 npx expo-app-intents init 生成的计数器示例开始。它演示了 JavaScript 和 Swift 运行时之间的基本连接。然后使用餐厅和邮件示例了解动态实体和架构意图。
使用以下 Apple 资源进一步了解 App Intent 开发:
API
import * as AppIntents from 'expo-app-intents';
Component
Type: React.Element<AppEntityViewProps>
A wrapper that associates its contents with an App Entity so Apple Intelligence and Siri
can understand which entity is visible onscreen. Use it around React Native views; for @expo/ui
views, use the appEntityIdentifier() modifier instead.
Note: The entity association requires iOS 18.4 or later and a project compiled with Xcode 27 or later. The children still render normally when those requirements are not met, on other platforms, or when the native module is unavailable.
Props for a UIKit wrapper that associates its onscreen content with one App Entity.
Hooks
Calls handler once with the pending invocations recorded while JavaScript was not running,
then again for every new invocation received while the component is mounted.
newIntent is null for the initial pending snapshot. Later calls include the current
pending snapshot and the new invocation that triggered the call. The initial call is always
delivered first, and new invocations are delivered one at a time in arrival order.
Pending invocations are not removed automatically. The handler must call
removePendingInvocationAsync(id)
after handling each one. The queue holds at most 100 invocations, and once it is full the oldest
are dropped to make room, so a handler that never removes them does eventually lose invocations.
When App Intents are unavailable, this hook calls the handler with an empty snapshot and does not call it again.
voidMethods
Returns an ExpoUI SwiftUI modifier config that ties a view to an AppEntity identifier.
The entity value must be registered from app-target Swift with
AppEntityIdentifierRegistry.shared.register(_:as:) or
AppEntityIdentifierRegistry.shared.registerIndexed(_:as:).
Note: The entity association requires iOS 18.4 or later and a project compiled with Xcode 27 or later. Otherwise the view renders normally without it.
AppEntityIdentifierModifierRemoves all pending invocations. Does nothing when App Intents are unavailable.
Promise<void>Returns the current entity catalog of the given kind. The returned promise is fulfilled with an empty array when the kind was never published or App Intents are unavailable.
The returned promise is rejected when the stored catalog cannot be read.
Promise<AppIntentEntity[]>Returns invocations that have not been removed from the pending queue yet, oldest first. The returned promise is fulfilled with an empty array when App Intents are unavailable.
The queue keeps at most 100 invocations. An app that never removes them keeps only the newest 100.
The returned promise is rejected when the stored queue cannot be read. In this case, the invocations waiting in the queue are not delivered. The queue starts empty afterward, so a later call succeeds.
Promise<AppIntentInvocation[]>Returns whether App Intents are available on this device.
Returns false on Android and web.
booleanAsks the system to re-evaluate App Shortcut phrases and parameter values.
The returned promise is rejected with UnavailabilityError when App Intents are unavailable. It
is also rejected when the app has no AppShortcutsProvider to refresh. Publishing a catalog with
setEntityCatalogAsync() also refreshes
shortcuts.
Promise<void>Rebuilds the Spotlight index from the stored entity catalog, whether or not the catalog
changed. setEntityCatalogAsync already keeps the index in step, so this is only needed to
recover from an index that no longer matches the catalog: one the system evicted, or one left
stale by an app update that changed how entities describe themselves.
Pass a kind to rebuild one catalog, or omit it to rebuild every kind registered natively with
registerIndexed. Kinds with no indexed registration are ignored.
Rejects when a catalog cannot be read or the index cannot be written, because this is the retry
path and a caller that asked for a rebuild has no other way to learn it did not happen. Every
kind is attempted before the first failure is reported, so one unreadable catalog does not skip
the rest. A kind whose rebuild failed is retried by the next setEntityCatalogAsync, even when
the catalog it publishes is unchanged.
Promise<void>Removes a handled invocation so it is no longer delivered or returned as pending. Does nothing when App Intents are unavailable.
The returned promise is rejected when the stored queue cannot be read or written. A rejection caused by an unreadable queue leaves nothing pending. The native layer sets aside the unreadable data, so it removes every invocation that was waiting in the queue instead of only this one.
Promise<void>Replaces the entity catalog of the given kind and asks the system to retrain
parameterized shortcut phrases against the new values.
Entities registered natively with
registerIndexed also have their Spotlight index rebuilt from the new catalog.
Publishing a catalog that matches the stored catalog does nothing, so apps can safely call this function on every start.
The native store uses UserDefaults, which is best suited to compact catalogs. For large
datasets, such as thousands of contacts or songs, apps should store the full data locally and
publish only the subset that Siri and Shortcuts need.
When kind or an entity is invalid, the returned promise is rejected and the previous catalog
remains available. The kind is invalid when it is empty or contains only whitespace. An entity
is invalid when its id or title is empty or contains only whitespace. An entity is also
invalid when another entity in the catalog has the same id.
Promise<void>Event subscriptions
Adds a listener for live App Intent invocations dispatched while JavaScript is observing.
Pending invocations recorded while JavaScript was not running are available through
getPendingInvocationsAsync()oruseAppIntents().
EventSubscriptionInterfaces
Handles a snapshot of pending invocations. After the initial call, it also receives the new invocation that triggered the handler.
Types
ExpoUI modifier config that associates a SwiftUI view with an AppEntity identifier.
Built on @expo/ui's own ModifierConfig rather than restating its shape, so that a change to
what the modifiers prop accepts is a type error here instead of a value ExpoUI rejects at
runtime.
Type: ModifierConfig extended by:
A single recorded App Intent invocation.
The native layer persists each invocation until removePendingInvocationAsync removes it.
Delivery is at-least-once, so handlers must be idempotent for each id.