View in English

  • 打开菜单 关闭菜单
  • Apple Developer
搜索
关闭搜索
  • Apple Developer
  • 新闻
  • 探索
  • 设计
  • 开发
  • 分发
  • 支持
  • 账户
在“”范围内搜索。

快捷链接

5 快捷链接

视频

打开菜单 关闭菜单
  • 专题
  • 相关主题
  • 所有视频
  • 关于

返回 WWDC25

  • 简介
  • 概要
  • 转写文稿
  • 代码
  • 了解 App Intents

    了解 App Intents 框架及其在 Apple 开发者平台中愈显关键的作用。我们将向你全面介绍意图、实体、查询等核心概念。你将了解如何综合运用这些概念,以便使你的 App 与 Apple 设备 (从“聚焦”和“快捷指令”等软件功能到操作按钮等硬件功能) 实现整合。我们还将介绍如何将 App Intents 作为你 App 的入口,以便将来实现与 Apple 智能的整合。

    章节

    • 0:00 - 简介
    • 0:45 - App Intents 生态系统
    • 2:47 - 框架导览
    • 21:15 - 运作方式

    资源

    • Accelerating app interactions with App Intents
    • Adopting App Intents to support system experiences
    • App intent domains
    • App Intents
    • App Shortcuts
    • Building a workout app for iPhone and iPad
    • Creating your first app intent
    • Integrating actions with Siri and Apple Intelligence
    • Making actions and content discoverable and widely available
      • 高清视频
      • 标清视频

    相关视频

    WWDC25

    • 使用 App Intents 针对“快捷指令”和“聚焦”进行开发
    • 设计交互式摘要卡片

    WWDC24

    • 利用 App Intents 为用户奉上 App 的核心功能
    • 利用 App Intents 设计提升系统体验
    • 带你的 App 登陆 Siri
    • App Intents 的新功能
  • 搜索此视频…

    大家好 我叫 James 是 Swift Intelligence Frameworks 团队的工程经理 很高兴为大家介绍 App Intents 通过这个框架 你可以提高 App 在整个系统中 以及所有 Apple 平台中的曝光度 让它更容易被用户发现 并拓展 App 的功能 首先 我将谈谈 App Intents 在 Apple 开发者生态系统中 愈加重要的作用

    随后将展示如何使用这个框架 让 App 的操作和实体 在整个系统中可供用户使用 最后 我将分享一些 在编写 App Intents 代码时 你应当了解的重要细节

    在讨论如何使用 App Intents 之前 我们应该先了解使用它的原因 App Intents 不仅仅是一个 引入到应用程序中来构建功能的框架 更是一个生态系统 让你的 App 功能 得以扩展到整个系统

    借助 App Intents 你可以 在“聚焦”中为用户提供定制化结果 实现操作按钮的情境感知体验、 小组件中的可配置性和交互性、 “控制中心”里简单易用的控件 甚至 Apple Pencil Pro 的自定操作

    作为今年的新增功能 “聚焦”现可从 Mac 上的任意位置 调用你的 App 的操作

    通过 App Intents 框架 用户即使没有进入到你的 App 也能享用上述丰富体验 这一切都始于 App 可以执行的操作 例如 打开备忘录、 开始体能训练 或将商品添加到杂货列表 这些是你的 App 的动词 你可能已经猜到 你可以通过创建 App Intents 即简称的“意图”来描述它们 在创建意图时 你需要向系统提供额外的信息 以确保正确执行操作 意图可以接受参数并返回值 这些输入和输出 可以是原生的 Swift 类型 也可以是你在 App 中定义的类型 你可以使用 App Intents 创建两种类型的值 对包含常量值的类型使用 AppEnum 对于动态类型使用 AppEntity AppEnum 和 AppEntity 是 App 中的名词 你可以借助 App 快捷指令 展示你的关键意图 让它们更易于访问和发现 当通过使用 Siri、配置操作按钮 或其他方法在“聚焦”中进行搜索时 会显示 App 快捷指令 你可以将这些视为 App 的句子 由意图和运行意图 所需的任何参数共同组成 我一直认为 实践才是最有效的学习方式 来看看创建我的首个意图 需要完成哪些工作 我喜欢和家人一起旅行 我一直在开发一款 App 用来查看世界各地的著名地标 我的 App 分为几个板块 我可以滚动浏览著名地标列表 在地图上查看它们 或者查看已创建的合辑 许多用户喜欢 滚动浏览地标网格界面 我将构建一个 App Intent 让用户能够更方便地导航到 App 的这个板块 具体应该怎么做呢? 首先我要定义一个 采用 App Intents 协议的新结构体 App Intent 至少需要包含 标题和 perform 方法

    标题是一个唯一的本地化字符串 它将显示为意图的名称 perform 方法包含了意图的逻辑 我将使用共享的导航器 打开“地标”视图 导航必须在主线程上完成 所以我将 perform 标记为 @MainActor

    perform 方法会返回一个意图结果 意图结果可以包含许多信息 包括 Siri 可以播报的对话内容 以及可以显示的片段视图 默认情况下 执行意图 不会将你的 App 置于前台 因此 提供对话和片段视图 是展示操作结果的好方法

    由于这个意图旨在 导航 App 中的屏幕 所以我将意图配置为在运行时打开 我会将新的 supportedModes 属性 设置为 foreground 以便在意图执行之前打开我的 App 这就是构建我的首个意图 所需的全部操作 安装我的 App 后 你可以在“快捷指令”中找到意图 我可以创建一个新的快捷指令 并添加我的导航意图 运行这个意图会将 App 置于前台 并直接将我带到“地标”视图 我的 App 还包含查看合辑 以及在地图上显示地标的板块 如果意图也能导航到 App 的这些板块就太好了 我的 App 使用一个简单的 Swift 枚举来对板块进行建模 要使类型与框架兼容 我将采用 AppEnum 协议 AppEnum 只需满足几个要求 它们必须能通过字符串来创建实例 所以我要添加一个 String 原始值 typeDisplayRepresentation 将类型 作为一个整体描述 caseDisplayRepresentation 描述了枚举的每种用例

    这些表示必须是常量值 因为这些信息会在编译时使用

    在我的意图中 我将添加一个新变量 来保存 navigationOption 我将添加 @Parameter 属性 将它变为意图参数 意图参数会用作意图的输入 可能是必要参数 也可能是可选参数 我已将这个参数设为必要参数 因此 运行时在调用 perform 方法前 将确保这个参数有一个值

    我将更新 perform 方法 以使用解析后的 navigationOption 并更改标题以反映新操作

    回到我的快捷指令 会看到作为 可编辑参数的导航选项 当我运行意图时 会显示这个板块的快捷指令 我将选择“地图” 以直接进入这个视图 App Intents 框架中的类型 被设计为高度可定制 这样你就可以快速将构建块放置到位 然后优化体验 我将向这个意图添加一些额外信息 让它更好用

    默认情况下 快捷指令 会将每个参数显示为一行 轻点这一行将显示相关类型的值列表 它可以正常显示 但我可以采取一些措施来优化体验

    AppEnum 只需要每个枚举用例的标题 但可以使用额外信息进行配置 例如图标

    要添加图标 我需要使用 DisplayRepresentation 构造器 然后我可以为每个用例添加一个符号 这样 快捷指令将在选项中显示图像 意图可以使用流畅的 类似句子的表示配置 这种表示称为 parameterSummary parameterSummary 会以一种 可直接识别的方式 描述操作及其参数

    我将提供 summary 并将参数插入到字符串中 快捷指令将显示 带有内联可选参数的 summary 提供更加实用的操作描述

    实际上 它读上去还不太成句 我可以通过向参数添加自定标题 来解决这个问题

    在这里我还将添加一段自定对话 以便在请求值时显示 今年 为包含所有必要参数的意图 实现 parameterSummary 后 用户将能够从 Mac 上的“聚焦”中 运行你的操作 要进一步了解“聚焦”的新增功能 建议大家观看这个讲座

    通过将 App 的操作建模为意图 你的用户可以构建 强大的快捷指令和自动化功能 不过 有些意图对你的 App 至关重要 因此应该在 App 安装后就立即可用 你可以通过采用 App 快捷指令 来提供这些操作

    App 快捷指令是一种在整个系统中 自动公开 App Intent 的类型 在“聚焦”中进行搜索时 App 快捷指令会在醒目的位置显示 用户只需说出某个触发短语 即可通过 Siri 运行 App 快捷指令 它们可以配置为通过操作按钮 或 Apple Pencil 轻捏手势运行 App 快捷指令会显示在“快捷指令”App 中 无需进行任何用户设置 最棒的是 - 只需几行代码即可构建 我们来看看如何构建

    App 通过 AppShortcutsProvider 提供 App 快捷指令 你的 App 应该定义一个包含 所有 App 快捷指令的提供程序 App 快捷指令接受意图 以及短语列表、 标题、 和图像的实例 向 Siri 说出或键入 App 快捷指令 短语即可运行 App 快捷指令 每个短语都必须包含 applicationName 占位符 短语最多可以包含一个意图参数 如果提供 则将为这个类型的每个值 创建一个 App 快捷指令

    这个简单的结构就是 创建 App 快捷指令所需的全部内容 “快捷指令”将在新的板块中 显示 App 的 App 快捷指令 这些短语会影响 创建的 App 快捷指令 提供不带参数的短语 将创建使用标题和图像名称的 App 快捷指令 由于我用 AppEnum 提供了一个短语 因此系统会为每个用例 创建一个 App 快捷指令

    现在我就可以用 Siri 或“聚焦” 来运行我的意图了 App 快捷指令是让你的意图 更容易被用户发现的好方法 有关构建 App 快捷指令的更多信息 请观看 WWDC23 中的这个讲座

    地标是我的 App 中的核心概念 如果能从我的意图对地标进行操作 那就太好了 与导航选项的常量列表不同 地标是动态的 因此我不能使用 AppEnum 我会改为创建一个 AppEntity 来为我的地标建模

    我的 App 已具有 Landmark 类型 虽然我能让这个类型符合 AppEntity 在本例中 我将新建一个 LandmarkEntity 结构体 这种类型将充当 App Intents 与我的底层数据模型之间的桥梁

    AppEntity 必须是可识别的 因此我要添加一个 ID 这个标识符必须持久化 而且你可以通过这个 ID 查找实体的实例 我们稍后再谈这个问题 与包含参数的意图类似 实体可以包含 用 @Property 属性表示的属性 这些将在“快捷指令”中向用户公开 并可用于查找和筛选操作 我可以从我的数据模型中设置这些值 但从今年开始 我可以使用 新的 ComputedProperty 属性 将 getter 添加到我的实体属性 我可以遵从我的数据模型 而不是在这些类型之间拷贝值 与 AppEnum 类似 AppEntity 需要针对相关类型 及这个类型的实例的表示

    AppEntity 需要一条 名为查询的额外信息

    与具有一组已知值的 AppEnum 不同 AppEntity 是动态的 我的 App 可以有任意数量的地标 查询是系统推断我的实体的方式 它通过回答许多不同的问题 来进行推断 第一类问题是“有哪些实体?” 查询负责回答这个问题 并返回匹配实体的集合

    查询支持这个问题的多种类型 例如 实体字符串查询会询问: “是否有任何与这个字符串 匹配的实体?” 实体属性查询可能会询问: “这个州都有哪些地标?”

    所有查询都必须回答 一个非常具体的问题: “这个 ID 的实体是什么?” 这样 系统就可以唯一引用一个实体 并仅在需要时解析这个实体 我可以通过创建一个遵从 Entity Query 协议的类型 来提供这项查询 entities(for:) 方法是回答 “这个 ID 属于哪个实体?” 这一问题的查询 它接受一组标识符 并返回一组实体实例 我们稍后会回到 “有哪些实体”这个问题 查询通常需要访问本地数据库 或其他依赖项来获取实例 我可以使用 @Dependency 属性 将依赖项注入到查询中 我需要用共享的 AppDependencyManager 来注册我的依赖项 你应该在 App 的生命周期中 尽早注册依赖项 现在我已经创建了 一个地标 AppEntity 我可以在我的意图中使用它 在旅行时 如果能够得知 最近的地标就太好了 我将创建一个 App Intent 来显示 我首先要创建一个 基本的 ClosestLandmarkIntent 我需要从数据模型中获取最近的地标 意图中也支持依赖项 所以我可以添加我的依赖项 对于我的 perform 方法 我将添加 Landmark 实体的 ReturnsValue 用作意图参数的类型 也可以从意图返回 返回值可以用作另一个意图的输入 例如在多步骤快捷指令中 我还将返回一个对话和一个片段视图 这样 我的意图就可以 显示或说出意图的结果 最后 我可以实现 perform 方法 找到最近的地标后 我将返回一个包含实体、对话 和视图的结果 通过同时提供对话和视图 我会确保用户总能找到 离他们最近的地标 无论意图是如何被调用的 就此说明一下 为这个意图创建 App 快捷指令 有助于更轻松地运行这个意图 我的用户现在可以通过 Siri 或“聚焦”来方便地访问这个意图 即使手机放在口袋里也可以运行 意图、实体和查询是 App Intents 的构建块 每个协议都有子协议和相应配置 可用于提供附加功能 我们来看看如何优化我的 AppEntity 以提供额外的体验 我希望能够轻松看到 离我最近的地标的照片 我可以使用“快捷指令”App 来帮助我 首先创建一个快捷指令并 添加“查找最近地标”意图 为了查看结果 我将添加“显示内容”操作 这将接受我意图的结果并呈现它 默认情况下 这个操作将显示 Landmark 实体的 displayRepresentation 但是我可以选择 要呈现的任何实体属性

    Landmark 实体不会直接保存图像 但它确实有一条通往图像的路径 我可以使用 Transferable 协议 来声明这个实体的图像表示 Transferable 是一种声明式方法 可以声明类型的不同数据表示 这些表示可用于在 App 间共享数据 我将提供图像数据作为类型的 TransferRepresentation 的一部分 回到“快捷指令”中 现在我可以选择 显示这个实体的图像表示 运行我的快捷指令将显示地标的图像 声明图像表示还有其他好处 我可以将这个图像值 用作任何拍照操作的输入 甚至其他 App 的拍照操作 如需进一步了解 Transferable 请观看这些讲座

    我想对我的实体再做一次更改 让用户能更轻松地在系统中找到它 “聚焦”提供了跨应用程序的 强大语义搜索 将实体提供给“聚焦”可以扩展 系统对内容的理解 为了让“聚焦”将我的实体加入索引 我可以采用 IndexedEntity 协议 IndexedEntity 是包含 “聚焦”核心属性集的 AppEntity

    从今年开始 你可以直接在属性上 添加“聚焦”indexingkey 通过注释属性 “聚焦”可以 向用户显示更多相关信息 在提供索引实体时 这个框架会负责为你创建可搜索项目 和属性集 提供的实体可以在“聚焦”中找到 默认情况下 轻点实体会将 App 置于前台 我可以进一步优化这种体验 并直接打开地标细节视图 首先创建一个遵从 OpenIntent 协议的意图 采用这个协议的意图会在执行前 自动打开 App 因此我可以跳过 添加受支持模式的步骤 OpenIntent 必须具有目标参数 轻点“聚焦”中的实体时 它将运行存在的匹配 OpenIntent

    我没有调出导航器 现在我可以采用专为导航设计的 新 TargetContentProvidingIntent 协议 这些意图不需要 perform 方法 相反 我可将 onAppIntentExecution 修饰符附加到我的视图 在括号中 我可以使用这个意图的参数 来执行 SwiftUI 导航

    现在 当轻点“聚焦”中的地标时 我会直接导航到地标详细信息视图 在结束这一部分之前 我想进一步介绍查询 以及它们如何提供实体 为了查看实际效果 我将 新建一个 App 快捷指令来打开地标 我已经为提供程序添加了一个新的 App 快捷指令以及两个短语 一个包含地标参数 另一个不包含 但是 运行我的 App 快捷命令 并不能为我提供任何可供选择的地标 你可能已经猜到了 我可以使用查询 来提供一些地标 我将在 EntityQuery 中实现可选的 suggestedEntities 方法 返回用户最喜欢的地标 现在再次运行这个意图时 会呈现建议的实体列表 suggestedEntities 还有另一个用途 记住当我为这个意图 添加参数化短语时 我可以为每个建议实体 生成一个 App 快捷指令 方法是在提供程序上调用 updateAppShortcutParameters 方法 现在我可以用 Siri 和“快捷指令” 轻松直接导航到我最喜欢的地标 查询可回答有关实体的许多其他问题 如果你的实体都能纳入内存 可以使用 EnumerableEntityQuery 返回所有实体 App Intents 可以从这个查询 派生出更复杂的查询 使用 EntityPropertyQuery 你可以通过一组谓词 返回按特定顺序排列的实体列表 在本例中 我将实现一个 EntityStringQuery 以支持从字符串中查找实体 我将返回一个地标列表 其中字符串 与地标的名称或描述相匹配 现在用户在配置需要地标的意图时 将能够搜索所有地标 我们只是初步探索了 App Intents 框架 及其众多功能 请查看 App Intents 文档 了解你可以使用这个框架 为用户打造理想体验的各种方法 最后 我想带大家简单了解一下 为 App Intents 提供支持的架构 在用 App Intents 构建 App 时 你的代码是真实数据来源 App Intents 不需要 任何设置或配置文件 你的 Swift 源代码 将在构建时被读取 进而生成它的 App Intents 表示 这种表示形式存储在 你的 App 或框架中 用户安装你的 App 后 系统将使用这些数据 在不运行你的 App 的情况下 了解你 App 的功能 让我们通过一个意图 来看看具体是怎么运作的 意图的名称将成为操作的唯一标识符 意图的标题可帮助用户 区分不同的意图 而 perform 方法返回的签名 描述了如何呈现这个意图的结果 由于这个过程 发生在构建时而不是运行时 某些值必须被赋予常量值 例如 意图的标题必须是常量 调用函数或计算属性将导致错误 针对 App 中的每个目标 都会单独进行这种处理 为了在目标之间 正确分享你的 App Intent 类型 还有一些其他事项需要注意 去年 我们为 App 和 App Intents 扩展推出了这项功能 以引用框架中定义的 App Intent 类型 今年 我们很高兴地宣布 你现在可以将 App Intents 添加到 Swift 软件包和静态库中 在多个目标中 使用 App Intent 类型时 你必须向运行时提供 有关每个目标的额外信息 这可确保正确索引和验证类型 来深入了解一下具体是如何实现的 我的 App 只有一个目标 包含我所有的 App Intents 代码 我想引入新的 App Intents 扩展 来托管我的一些意图 两个目标都需要访问地标 因此我要创建一个 Swift 软件包 并将我的 Landmark 实体迁移到其中 要在目标之间共享类型 我需要将每个目标 注册为 App Intents 软件包 首先 我要在与实体相同的目标中 创建一个 App Intents 软件包 我再将另一个 App Intents 软件包 添加到我的 App 目标 我可以提供一个所包含软件包的列表 所以我要添加刚创建的那个软件包 最后 我将从扩展执行相同的操作 这可以确保 App Intents 运行时 对软件包中定义的所有类型 都有适当的访问权限 在引用未编译为静态库的代码时 你应该使用 App Intents 软件包

    是时候结束这次讲座了 如果这是你首次了解 App Intents 不妨从小处着手 将第一个 App 快捷指令添加到 App 中 随后再探索框架 了解你可以利用它的哪些功能 为用户带来最大价值 如需进一步了解 App Intents 今年还有一些相关的精彩讲座 谢谢观看!

    • 3:23 - Navigate Intent

      struct NavigateIntent: AppIntent {
          static let title: LocalizedStringResource = "Navigate to Landmarks"
      
          static let supportedModes: IntentModes = .foreground
      
          @MainActor
          func perform() async throws -> some IntentResult {
              Navigator.shared.navigate(to: .landmarks)
              return .result()
          }
      }
    • 5:02 - Navigation Option App Enum

      enum NavigationOption: String, AppEnum {
          case landmarks
          case map
          case collections
      
          static let typeDisplayRepresentation: TypeDisplayRepresentation = "Navigation Option"
      
          static let caseDisplayRepresentations: [NavigationOption: DisplayRepresentation] = [
              .landmarks: "Landmarks",
              .map: "Map",
              .collections: "Collections"
          ]
      }
    • 5:38 - Navigate Intent with Parameter

      struct NavigateIntent: AppIntent {
          static let title: LocalizedStringResource = "Navigate to Section"
      
          static let supportedModes: IntentModes = .foreground
        
          @Parameter var navigationOption: NavigationOption
      
          @MainActor
          func perform() async throws -> some IntentResult {
              Navigator.shared.navigate(to: navigationOption)
              return .result()
          }
      }
    • 6:57 - Case Display Representations with Images

      static let caseDisplayRepresentations = [
          NavigationOption.landmarks: DisplayRepresentation(
              title: "Landmarks",
              image: .init(systemName: "building.columns")
          ),
          NavigationOption.map: DisplayRepresentation(
              title: "Map",
              image: .init(systemName: "map")
          ),
          NavigationOption.collections: DisplayRepresentation(
              title: "Collections",
              image: .init(systemName: "book.closed")
          )
      ]
    • 7:28 - Navigation Option With Parameter Summary

      struct NavigateIntent: AppIntent {
          static let title: LocalizedStringResource = "Navigate to Section"
      
          static let supportedModes: IntentModes = .foreground
        
          static var parameterSummary: some ParameterSummary {
              Summary("Navigate to \(\.$navigationOption)")
          }
        
          @Parameter(
              title: "Section",
              requestValueDialog: "Which section?"
          )
          var navigationOption: NavigationOption
      
          @MainActor
          func perform() async throws -> some IntentResult {
              Navigator.shared.navigate(to: navigationOption)
              return .result()
          }
      }
    • 9:22 - App Shortcuts Provider and Navigation Intent App Shortcut

      struct TravelTrackingAppShortcuts: AppShortcutsProvider {
          static var appShortcuts: [AppShortcut] {
              AppShortcut(
                  intent: NavigateIntent(),
                  phrases: [
                      "Navigate in \(.applicationName)",
                      "Navigate to \(\.$navigationOption) in \(.applicationName)"a
                  ],                
                  shortTitle: "Navigate",
                  systemImageName: "arrowshape.forward"
              )
          }
      }
    • 11:02 - Landmark Entity

      struct LandmarkEntity: AppEntity {
          var id: Int { landmark.id }
      
          @ComputedProperty
          var name: String { landmark.name }
      
          @ComputedProperty
          var description: String { landmark.description }
        
          let landmark: Landmark
        
          static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Landmark")
      
          var displayRepresentation: DisplayRepresentation {
              DisplayRepresentation(title: "\(name)")
          }
        
          static let defaultQuery = LandmarkEntityQuery()
      }
    • 13:19 - Landmark Entity Query

      struct LandmarkEntityQuery: EntityQuery {
          @Dependency var modelData: ModelData
        
          func entities(for identifiers: [LandmarkEntity.ID]) async throws -> [LandmarkEntity] {
              modelData
                  .landmarks(for: identifiers)
                  .map(LandmarkEntity.init)
          }
      }
    • 13:50 - App Dependency Manager

      @main
      struct LandmarksApp: App {    
          init() {
              AppDependencyManager.shared.add { ModelData() }
          }
      }
    • 14:18 - Closest Landmark Intent

      struct ClosestLandmarkIntent: AppIntent {
          static let title: LocalizedStringResource = "Find Closest Landmark"
      
          @Dependency var modelData: ModelData
      
          @MainActor
          func perform() async throws 
              -> some ReturnsValue<LandmarkEntity> & ProvidesDialog & ShowsSnippetView {
              
              let landmark = try await modelData.findClosestLandmark()
      
              return .result(
                  value: landmark,
                  dialog: "The closest landmark to you is \(landmark.name)",
                  view: ClosestLandmarkView(landmark: landmark)
              )
          }
      }
    • 15:18 - Closest Landmark App Shortcut

      AppShortcut(
          intent: ClosestLandmarkIntent(),
          phrases: [
              "Find closest landmark in \(.applicationName)"
          ],
          shortTitle: "Closest landmark",
          systemImageName: "location"
      )
    • 16:33 - Transferable

      extension LandmarkEntity: Transferable {
          static var transferRepresentation: some TransferRepresentation {
              DataRepresentation(exportedContentType: .image) {
                  return try $0.imageRepresentationData
              }
          }
      }
    • 17:31 - Indexed Entity

      struct LandmarkEntity: IndexedEntity {
          // ...
          
          @Property(
              indexingKey: \.displayName
          )
          var name: String
      
          @Property(
              indexingKey: \.contentDescription
          )
          var description: String
      }
    • 18:17 - Open Landmark Intent

      struct OpenLandmarkIntent: OpenIntent, TargetContentProvidingIntent {
          static let title: LocalizedStringResource = "Open Landmark"
      
          @Parameter(title: "Landmark", requestValueDialog: "Which landmark?")
          var target: LandmarkEntity
      }
      
      struct LandmarksNavigationStack: View {
          @State var path: [Landmark] = []
      
          var body: some View {
              NavigationStack(path: $path) {}
              .onAppIntentExecution(OpenLandmarkIntent.self) { intent in
                  path.append(intent.target.landmark)
              }
          }
      }
    • 19:24 - Open Landmark App Shortcut

      AppShortcut(
          intent: OpenLandmarkIntent(),
          phrases: [
              "Open \(\.$target) in \(.applicationName)",
              "Open landmark in \(.applicationName)"
          ],
          shortTitle: "Open",
          systemImageName: "building.columns"
      )
    • 19:39 - Suggested Entities

      struct LandmarkEntityQuery: EntityQuery {
          // ...
      
          func suggestedEntities() async throws -> [LandmarkEntity] {
              modelData
                  .favoriteLandmarks()
                  .map(LandmarkEntity.init)
          }
      }
    • 20:06 - Update App Shortcut Parameters

      TravelTrackingAppShortcuts.updateAppShortcutParameters()
    • 20:25 - EnumerableEntityQuery

      extension LandmarkEntityQuery: EnumerableEntityQuery {
          func allEntities() async throws -> [LandmarkEntity] { 
              // ...
          }
      }
    • 20:36 - EntityPropertyQuery

      extension LandmarkEntityQuery: EntityPropertyQuery {
          static var properties = QueryProperties {
              // ...
          }
      
          static var sortingOptions = SortingOptions {
              // ...
          }
      
          func entities(
              matching comparators: [Predicate<LandmarkEntity>],
              mode: ComparatorMode,
              sortedBy: [Sort<LandmarkEntity>],
              limit: Int?
          ) async throws -> [LandmarkEntity] {
              // ...
          }
      }
    • 20:44 - EntityStringQuery

      extension LandmarkEntityQuery: EntityStringQuery {
          func entities(matching: String) async throws -> [LandmarkEntity] {
              modelData
                  .landmarks
                  .filter { $0.name.contains(matching) || $0.description.contains(matching) }
                  .map(LandmarkEntity.init)
          }
      }
    • 23:10 - App Intents Package

      // TravelTrackingKit
      public struct TravelTrackingKitPackage: AppIntentsPackage {}
      public structaLandmarkEntity: AppEntity {}
      
      // TravelTracking
      struct TravelTrackingPackage: AppIntentsPackage {
          static var includedPackages: [any AppIntentsPackage.Type] {
              [TravelTrackingKitPackage.self]
          }
      }
      struct OpenLandmarkIntent: OpenIntent {}
      
      // TravelTrackingAppIntentsExtension
      struct TravelTrackingExtensionPackage: AppIntentsPackage {
          static var includedPackages: [any AppIntentsPackage.Type] {
              [TravelTrackingKitPackage.self]
          }
      }
      struct FavoriteLandmarkIntent: AppIntent {}
    • 0:00 - 简介
    • 了解 App Intents 框架,通过该框架,你可在所有 Apple 平台上提高 App 的曝光度和功能表现。主题包括框架的关键用途、实现方式,以及编写 App Intents 的最佳实践。

    • 0:45 - App Intents 生态系统
    • App Intents 是一个生态系统,让 App 能够跨系统扩展其功能,这些功能涵盖“聚焦”、操作按钮、小组件、“控制中心”和 Apple Pencil Pro。用户可以从任何位置执行 App 操作,即使他们当前并未打开该 App。 你可以将 App 操作定义为“意图”,这些“意图”可以接受参数,并通过用于常量的 AppEnum 或用于动态类型的 AppEntity 返回值。基于意图和参数构建的 App 快捷指令可通过“聚焦”、Siri 和操作按钮提升辅助功能和曝光度。

    • 2:47 - 框架导览
    • 本示例 App 可用于探索世界各地的著名地标。为了提升用户体验,它实现了 App Intents,让用户能够直接从 Siri、快捷指令和“聚焦”执行操作。 此流程涉及定义采用 App Intents 协议的结构体,需指定标题、perform 方法和意图结果。通过为意图添加参数,用户可以选择 App 中的特定部分,例如地标网格或地图视图。 为了提升 App 的曝光度与易用性,你可以创建 App 快捷指令,它们会自动在系统中公开意图。同时,借助 AppEntity 建模地标等动态数据,用户可通过意图对特定地标执行操作。 在 App Intents 框架中,AppEntity 表示地标等动态数据。查询是系统对这些实体进行推断的重要组件。它们可用于解决各种问题,例如检索全部实体、匹配特定字符串或属性,或通过 ID 精确引用实体。 你可以自定不同类型的查询,例如“实体字符串查询”和“实体属性查询”,这些查询可能依赖于本地数据库或其他资源。可以使用 @Dependency 属性将依赖项注入到查询中。应尽可能在 App 生命周期的初始阶段注册依赖项。 借助 App Intents,你将能够创建可通过 Siri、“聚焦”或快捷指令执行的自定操作。通过在意图中返回实体类型,这些操作可以在多步骤快捷指令中被串联起来。 为了提升用户体验,你可以将实体设计为可转让,从而实现跨 App 共享。通过采用 IndexedEntity 协议,“聚焦”能够执行语义搜索。此外,你还可以创建 OpenIntent,这样一来,如果在“聚焦”中轻点实体,就能直接导航到 App 中的特定视图。

    • 21:15 - 运作方式
    • App Intents 在构建时使用 Swift 源代码来生成 App Intents 表示,然后将其存储在 App 或框架中。这样,系统无需运行 App 即可了解它的功能。 意图的名称可用作其唯一标识符;标题用于帮助用户区分不同的意图;而“perform”方法的返回签名则定义了如何呈现结果。由于处理发生在构建阶段,因此你需要为某些意图属性提供常量值。 要在目标 (例如 App 及其扩展) 之间共享 App Intent 类型,你可以使用 Swift 软件包或静态库。你需要将每个目标注册为 App Intents 软件包,以确保 App Intents 运行时能够正确索引和验证共享类型。

Developer Footer

  • 视频
  • WWDC25
  • 了解 App Intents
  • 打开菜单 关闭菜单
    • iOS
    • iPadOS
    • macOS
    • Apple tvOS
    • visionOS
    • watchOS
    打开菜单 关闭菜单
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • SF Symbols
    打开菜单 关闭菜单
    • 辅助功能
    • 配件
    • App 扩展
    • App Store
    • 音频与视频 (英文)
    • 增强现实
    • 设计
    • 分发
    • 教育
    • 字体 (英文)
    • 游戏
    • 健康与健身
    • App 内购买项目
    • 本地化
    • 地图与位置
    • 机器学习与 AI
    • 开源资源 (英文)
    • 安全性
    • Safari 浏览器与网页 (英文)
    打开菜单 关闭菜单
    • 完整文档 (英文)
    • 部分主题文档 (简体中文)
    • 教程
    • 下载 (英文)
    • 论坛 (英文)
    • 视频
    打开菜单 关闭菜单
    • 支持文档
    • 联系我们
    • 错误报告
    • 系统状态 (英文)
    打开菜单 关闭菜单
    • Apple 开发者
    • App Store Connect
    • 证书、标识符和描述文件 (英文)
    • 反馈助理
    打开菜单 关闭菜单
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program (英文)
    • News Partner Program (英文)
    • Video Partner Program (英文)
    • 安全赏金计划 (英文)
    • Security Research Device Program (英文)
    打开菜单 关闭菜单
    • 与 Apple 会面交流
    • Apple Developer Center
    • App Store 大奖 (英文)
    • Apple 设计大奖
    • Apple Developer Academies (英文)
    • WWDC
    获取 Apple Developer App。
    版权所有 © 2025 Apple Inc. 保留所有权利。
    使用条款 隐私政策 协议和准则