Highlight
App Intents 新增自定义对话响应、交互捐赠、实体所有权声明、屏幕感知和系统实体标注等 API,让应用在 Siri 和 Apple Intelligence 中提供更自然、更个性化的体验。
核心内容
以前让应用通过 Siri 执行操作,开发者能控制的很有限。Siri 会自动生成响应,开发者无法匹配应用的独特语气和视觉风格。当用户在应用内执行操作时,Apple Intelligence 无法学习这些行为模式。应用内容对 Siri 来说也不够透明,用户看到屏幕上的内容,Siri 可能”看不见”。
Apple 在 WWDC26 推出了 App Intents 高级功能,解决了这些限制。
开发者现在可以自定义 Siri 的对话响应,让回复匹配应用的语言风格。应用可以通过交互捐赠 API 告诉 Apple Intelligence 用户在 UI 中的操作,帮助系统理解用户偏好。实体所有权声明让 Siri 知道哪些内容是公开或共享的,从而决定是否需要确认。屏幕感知 API 将屏幕上显示的内容连接到 Siri 能理解的实体。系统级别的实体标注让 Siri 在通知、正在播放、闹钟等场景中也能理解应用内容。
这些功能让应用与 Siri 的协作更深入,用户体验更连贯。
详细内容
自定义对话响应 (02:00)
ProvidesDialog 协议让 intent 返回自定义对话。Siri 可以显示 supporting 文本,在语音设备上朗读 full 文本。
@AppIntent(schema: .audio.addToPlaylist)
struct AddToPlaylistIntent {
func perform() async throws -> some IntentResult & ProvidesDialog {
// Adds song to playlist and responds
return .result(
dialog: IntentDialog(
full: """
Added \(song.title) to the \
\(playlist.title) mix tape.
""",
supporting: "Added"
)
)
}
}
关键点:
ProvidesDialog协议声明 intent 提供自定义对话IntentDialog的 full 字段包含完整描述,供语音设备使用- supporting 字段是简短版本,可显示在 UI 上
在 intent 执行过程中,可以使用 $label.requestValue() 询问用户。
@AppIntent(schema: .clock.createTimer)
struct CreateTimerIntent {
var duration: Duration
var label: String?
var isSleepTimer: Bool
func perform() async throws -> some ReturnsValue<TimerEntity> {
// Checks active timers and requests label parameter
label = try await $label.requestValue(
"""
You already have a timer running. \
What should we call this one?
"""
)
return .result(value: timerEntity)
}
}
关键点:
$label.requestValue()在执行过程中请求用户输入- 适用于需要澄清的场景,比如避免重名冲突
Intent 响应还可以返回自定义 SwiftUI 视图,使用 ShowsSnippetView 协议。
@AppIntent(schema: .audio.addToPlaylist)
struct AddToPlaylistIntent {
var audioEntity: AudioEntity
var playlist: PlaylistEntity
func perform() async throws -> some IntentResult & ProvidesDialog & ShowsSnippetView {
// Adds to playlist and shows dialog and snippet
let view = PlaylistSnippetView(
playlist: updatedEntity,
tracks: updated.tracks
)
return .result(dialog: dialog, view: view)
}
}
实体展示表示 (04:26)
实体可以通过 DisplayRepresentation 定义在系统中的视觉呈现。
@AppEntity(schema: .audio.song)
struct SongEntity {
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(
title: "\(title)",
subtitle: "\(artistName)",
image: artworkImage
)
}
}
关键点:
DisplayRepresentation定义实体在 Siri、Spotlight、Shortcuts 中的显示方式- 包含 title、subtitle、image 三个组件
- 用于实体选择、确认对话等场景
交互捐赠 (07:00)
用户在应用 UI 中的操作,通过 IntentDonationManager 告诉 Apple Intelligence,帮助系统学习用户偏好。
@ModelActor
actor ModelManager {
func sendMessage(_ /* ... */, donateIntent: Bool = false) async throws -> [Message.ID] {
// Donate intent with parameters and result so Siri can learn user preferences
if donateIntent {
let intent = SendMessageIntent()
intent.destination = .recipients(conversation.recipients.map(\.entity))
let result = messages.map(\.entity)
Task {
try await IntentDonationManager.shared.donate(
intent: intent,
result: .result(value: result)
)
}
}
}
}
关键点:
- 捐赠只在 UI 交互时需要,Siri 调用 intent 已自动记录
- 需要传递 intent 参数和执行结果
- 系统用这些信息推断用户偏好,比如对某个联系人习惯用哪个应用
过度捐赠会被系统忽略,应只在真正代表用户行为时捐赠。
实体所有权声明 (10:03)
OwnershipProvidingEntity 协议告诉 Siri 实体是公开的还是共享的,影响确认策略。
@AppEntity(schema: .calendar.event)
struct EventEntity: OwnershipProvidingEntity {
var ownership: EntityOwnership {
// isShared used to compute ownership state: .shared, .public, or .unknown
attendees.isEmpty ? .unknown : .shared
}
}
关键点:
- 公开或共享实体的操作更可能触发确认
- 默认是
.unknown,系统假设为私有 - 应根据实体状态动态返回
语义索引与结构化搜索 (11:30)
使用 IndexedEntity 和 CSSearchableIndex.indexAppEntities() 将实体加入 Spotlight 语义索引。
struct EntityIndexingHelper {
// Indexes playlist entities
func indexPlaylist(_ playlist: Playlist) async throws {
let entity = PlaylistEntity(playlist: playlist)
try await CSSearchableIndex(name: indexName)
.indexAppEntities([entity])
}
}
关键点:
- 索引后可通过 Siri 按名称搜索实体
- Spotlight 也可搜索到这些实体
- 某些域支持语义搜索,不仅匹配关键词
对于不提前索引的内容,使用 IntentValueQuery 提供结构化搜索。
struct AudioIntentValueQuery: IntentValueQuery {
func values(for input: AudioSearch) async throws -> [AudioEntity] {
switch input.criteria {
case .searchQuery(let query):
return try await searchResults(for: query)
case .unspecified:
return try await likedSongResults()
// ... also a .url case
}
}
}
关键点:
IntentValueQuery接收结构化搜索输入- 可返回多种实体类型(UnionValue)
- 支持
.searchQuery、.unspecified、.url等条件
应用内搜索 (14:49)
.system.searchInApp schema 让 Siri 在应用内执行搜索,而不是直接显示结果。
@AppIntent(schema: .system.searchInApp)
struct SearchAudioLibraryIntent {
var criteria: StringSearchCriteria
func perform() async throws -> some IntentResult {
// Perform in-app search with Siri search string
navigation.searchText = criteria.term
navigation.selectedTab = .library
return .result()
}
}
关键点:
- 适用于开发者精心设计的应用内搜索体验
- 即使不索引实体也能工作
- 将搜索字符串传递给应用导航系统
屏幕感知 (16:27)
屏幕感知让 Siri 理解屏幕上显示的实体。主要有三种 API。
单一主实体使用 NSUserActivity。
struct NowPlayingView: View {
@Environment(PlaybackController.self) private var playback
var body: some View {
VStack {
// Player UI
}
.userActivity("cosmotunes.nowPlaying", isActive: playback.currentTrack) { activity in
activity.title = playback.currentTrack?.title
activity.appEntityIdentifier = EntityIdentifier(
for: SongEntity.self,
identifier: playback.currentTrack.id
)
}
}
}
关键点:
- 用于整个屏幕展示单一实体的场景
appEntityIdentifier将 activity 连接到具体实体
列表中的多个实体使用 View Entity annotation。
struct AlbumView: View {
private var header: some View {
VStack(alignment: .leading, spacing: 6) {
// ...
}
.appEntityIdentifier(
EntityIdentifier(for: AlbumEntity.self, identifier: session.id.uuidString)
)
}
}
关键点:
- 为每个代表实体的 view 添加标注
- 适用于屏幕上有少量实体的场景
大量实体使用 Collection annotation。
struct PlaylistDetailView: View {
var body: some View {
List {
ForEach(playlist.tracks) { track in
PlaylistTrackRow(track: track)
}
}
.appEntityIdentifier(forSelectionType: GeneratedTrack.ID.self) { trackID in
EntityIdentifier(for: SongEntity.self, identifier: trackID)
}
}
}
关键点:
- 避免为每个列表项单独添加标注
- 系统按需获取实体标识符
- 支持滚动出屏幕的实体
自定义画布视图使用 custom canvas view annotation,详见示例代码。
系统实体标注 (21:07)
在通知、正在播放、闹钟等系统场景中标注实体。
// User notifications
func scheduleNotification(message: Message, author: Contact, conversation: Conversation) {
let content = UNMutableNotificationContent()
content.title = author.name
content.body = message.body
content.appEntityIdentifiers = [
EntityIdentifier(for: MessageEntity.self, identifier: message.id)
]
}
// Now Playing
final class CosmoTunesMediaSession: MediaSessionRepresentable {
var content: (any MediaContentRepresentable)? {
var content = MusicContent(id: track.id.uuidString, songTitle: track.title /* ... */)
content.appEntityIdentifiers = [
EntityIdentifier(for: SongEntity.self, identifier: track.id),
EntityIdentifier(for: ArtistEntity.self, identifier: track.session.artistName),
EntityIdentifier(for: PlaylistEntity.self, identifier: currentPlaylist.id),
]
return content
}
}
// AlarmKit
func scheduleAlarm(_ alarm: Alarm) async throws {
let configuration = AlarmManager.AlarmConfiguration<CosmoTunesAlarmMetadata>.alarm(
schedule: schedule,
attributes: attributes,
appEntityIdentifier: EntityIdentifier(for: AlarmEntity.self, identifier: alarm.id),
stopIntent: DismissAlarmIntent(),
secondaryIntent: SnoozeAlarmIntent(),
sound: sound
)
}
关键点:
- 三个场景使用相同的标注模式
- 按从具体到一般的顺序排列实体标识符
- 不能使用
TransientAppEntity,因为需要持久标识符
核心启发
1. 个性化对话响应
做什么:为应用的关键 Intent 添加自定义对话,让 Siri 的回复听起来像应用自己的语言。
为什么值得做:Siri 自动生成的响应千篇一律,无法匹配应用的独特语气。自定义对话让用户感觉 Siri 真的”懂”这个应用,从添加歌曲、创建计时器等高频操作开始,投入产出比最高。
怎么开始:让 Intent 的 perform() 返回 some IntentResult & ProvidesDialog,用 IntentDialog(full:supporting:) 定义完整和简短两个版本的回复。入口:ProvidesDialog 协议 + IntentDialog
2. UI 捐赠
做什么:在应用的核心操作流程中添加 Intent 捐赠,比如发送消息、开始导航、播放音乐。
为什么值得做:Apple Intelligence 需要了解用户在应用内的实际行为才能提供个性化建议。捐赠告诉系统”这个用户习惯用这款应用给这个人发消息”,Siri 下次就能优先推荐。
怎么开始:在操作完成后调用 IntentDonationManager.shared.donate(intent:result:),传入包含完整参数的 intent 和执行结果。注意只在真正的 UI 交互时捐赠,Siri 调用已自动记录。入口:IntentDonationManager.shared.donate(intent:result:)
3. 实体所有权感知
做什么:如果应用有公开或共享内容,实现 OwnershipProvidingEntity。
为什么值得做:公开或共享的实体操作更可能触发 Siri 的确认,避免用户误操作共享数据。明确所有权状态让 Siri 知道什么时候该多问一句,提升用户对应用的信任度。
怎么开始:让 Entity 遵循 OwnershipProvidingEntity 协议,实现 ownership 属性,根据实体状态动态返回 .shared、.public 或 .unknown。入口:OwnershipProvidingEntity 协议 + var ownership: EntityOwnership
4. 语义索引
做什么:将应用中相对稳定的内容实体加入 Spotlight 索引。
为什么值得做:用户可能忘了某个内容在哪个应用里,但记得它的名字。加入 Spotlight 语义索引后,用户可以从系统搜索直接跳转到应用内的具体内容,降低内容发现门槛。
怎么开始:让 Entity 遵循 IndexedEntity,调用 CSSearchableIndex.indexAppEntities() 将实体加入索引。对于不提前索引的内容,用 IntentValueQuery 提供结构化搜索。入口:IndexedEntity + CSSearchableIndex.indexAppEntities()
5. 屏幕感知
做什么:在应用的详情页、列表页添加实体标注,让 Siri 理解屏幕上显示的内容。
为什么值得做:用户看到屏幕上的内容后,可以直接说”播放第三个”或”分享那个”,而不必说出完整名称。这消除了”用户看到了但 Siri 看不到”的断层,让语音交互更自然。
怎么开始:单一实体用 .userActivity 的 appEntityIdentifier,少量实体用 .appEntityIdentifier 修饰 View,大量列表用 .appEntityIdentifier(forSelectionType:) 做 Collection 标注。入口:appEntityIdentifier + EntityIdentifier(for:identifier:)
关联 Session
- App Intents 新增功能 — App Intents 基础与新特性概览
- Code-Along: Make your app available to Siri — 动手实现 App Intents 的实践演示
- App Intents Testing — 测试 App Intents 的工具与方法
- Secure your app: Mitigate risks to agentic features — Agentic 应用的安全考量
- Apple Intelligence and Siri AI — Apple Intelligence 与 Siri AI 体系概述
评论
GitHub Issues · utterances