大多数浏览器和
Developer App 均支持流媒体播放。
-
利用 App Intents 实现 App 快捷指令
了解如何在您的 App 中创建快捷指令,而不需要进行任何用户设置。我们将介绍 App Intents 如何帮助您呈现快捷指令视图,探索如何添加对参数化阶段的支持,以允许用户快速表达他们的意图。我们还将分享如何借助 Siri 提示和快捷指令链接提高 App 快捷指令的曝光度。为能更好地理解此讲座,我们建议您先对 SwiftUI 有基本的了解。
资源
相关视频
Tech Talks
WWDC22
-
下载
♪ 柔和乐器演奏的嘻哈音乐 ♪ ♪ 嗨 我是 Michael Sumner 一名从事 Siri 和 App Intents 的软件工程师 本次视频我想谈谈 如何利用新的 App Intents 框架 为您的 App 创建 App 快捷指令 我会先总述 App Shortcuts 的概念 及其与 App Intents 的关系 其次 我将介绍在 Swift 中 创建 App 快捷指令的方法 并添加一个参数 最后 我将介绍用户如何 发现您的 App 快捷指令 从而从您的工作中受益 我们先从使用 App Intents 框架 和 App Shortcuts 开始 用户可在您的 App 上使用快捷指令 创建多步骤工作流程 用于“快捷指令”App 和 Siri 现在 在用户在使用您的意图之前 必须先通过“添加到 Siri”按钮 或“快捷指令”App 设置快捷指令 我们很高兴推出了 App Shortcuts 它无需用户设置 这可让用户更加便捷地使用 快捷指令 通过与 App Shortcuts 集成 您的 App 一经安装 即可使用意图 这可让用户轻松发现和使用 您 App 中的功能 无需前往“快捷指令”App 或使用 “添加到 Siri”按钮进行设置 App Shortcuts 同 用户构建的快捷指令一样 可从“快捷指令”App “聚焦”和 Siri 中运行 这为用户提供了多种方式 从系统中各个位置 发现并与您的 App 交互 例如 在“聚焦”中搜索时 您的 App 快捷指令会 直接显示在搜索结果中 方便访问 使用 App Shortcuts 您的用户能与您的 App 进行快速 轻量级交互 使其更容易 完成任务并继续操作 我的团队正在开发 一个 App 即 Meditation 能通过一组音频提示和声音 帮助用户进行冥想 专注于重要的事情 如今 要开始冥想 用户需启动 App 登录 找到其想要进入的冥想会话 通过与 App Shortcuts 集成 我的用户只需 询问 Siri 就可任意 快速访问这些功能 而且 通过快速开始会话 用户可以将冥想融入日常生活 无论是早上上班前 还是在晚上都有助于 在漫长的一天后放松心情 好 我们直接来看创建 App Intent 并将其转换为 App 快捷指令所用的代码 不同于以往的快捷指令 App Shortcuts 由 新 App Intents 框架构建 App Intents 是从头开始构建的 仅限 Swift 的新框架 可更快 更轻松地构建宏大意图 有了 App Intents 一切都可 在您的 Swift 源代码中定义 无需单独的元数据文件 这省去了代码生成步骤 让您保持专注 无需在源代码编辑器 和元数据编辑器之间切换上下文 还能更容易地进行代码审查 解决合并冲突 要构建 App Shortcuts 您需编写一个 AppShortcutsProvider 列出将您的 App Intent 全然转变为快捷指令 所需的短语和其他元数据 请注意 因为这些设置 没有任何用户交互 您需在 触发短语中包含 App 名称 意图被定义为 Swift 结构体 使用 AppIntent 协议 一个基本意图只有两个要求 一个是标题 用于在“快捷指令”App 中 显示您的意图 另一个是名为 perform 的方法 perform 方法是您运行意图逻辑 返回结果的地方 此外 您可为用户触发提示 等待他们的响应 在此意图中 我将用 我 App 中的 MeditationService 开始默认的冥想会话 由于 perform 方法是异步的 我可以运行异步代码来开始会话 会话开始后 我将返回一个显示给用户的对话框 如果您的 App 已本地化 那么您就会想在您 所有的语言环境中本地化这个字符串 到目前为止 用我已建立起来的内容 在创建快捷指令时 就可在“快捷指令”App 中出现 StartMeditationIntent 有目的的用户可采取此意图 并创建一个开始会话的快捷指令 我在该快捷指令中 还添加了第二个意图来启用焦点 默认情况下 我的 App Intent 是以我在源代码中指定的标题渲染 要为您的操作自定义渲染 请务必在您的 App Intent 中添加参数摘要 参数摘要允许您自定义 您意图的外观 以及内联显示值 但是 该意图本身 就可以很好地用作快捷指令 理想情况下 用户无需编写快捷指令 就能执行我的意图 通过创建 App 快捷指令 我可以代表用户执行此设置步骤 这样用户就能在 App 安装后 立即使用我的意图 现在我已经编写了一个意图 我将为它创建 App 快捷指令 与意图类似 App Shortcuts 在 Swift 代码中被定义 方法是通过实现 AppShortcutsProvider 协议 为了实现协议 我简单创建一个单独的 getter 能返回我想为用户设置的 所有 App 快捷指令 注意 总的来说 您的 App 最多能有 10 个 App 快捷指令 但是 大多数 App 只需要几个快捷指令 所以我将为 StartMeditationIntent 创建一个 AppShortcut 首先 我会传递一个我的意图实例 如果我的意图的初始化器接受参数 那我就可以在这里指定值 其次 我将创建一组口语短语 从 Siri 调用我的 AppShortcut 您会发现 我没有在字符串中直接写 App 的名称 而是用了一个特殊的 .applicationName 令牌 这不仅允许 Siri 插入 App 的主名称 还能插入我配置的 任何 App 的名称同义词 由于用户可能会 说不同的短语来启用冥想 所以我会在这里提供更多替代短语 如果您的 App 已本地化 那么这些替代短语也需要本地化
好 现在要是有人想开始冥想 只需对着 Siri 说“开始冥想” Siri 就会调用 StartMeditationIntent 并说出我返回的对话 此外 如果有人在 “聚焦”中搜索我的 App 就会看到我在代码中列出的第一个 App Shortcut 当用户点击结果时 无需启动 App 快捷指令即可立即运行 切记 如果您的意图 触发了 App 启动 则不会在“聚焦”中显示 因此 只需少量代码 我就能让用户更方便地 使用我的 App 冥想 但现在 Siri 在运行我的意图时 显示的是默认视图 这没关系 但我想在用户运行我的 App 快捷指令时向其展示更多信息 为此 我需要实行一个自定义视图 只要我的意图在运行 就能让 Siri 显示出来 构建 App Intents 框架中的视图 要使用 SwiftUI 并利用 与小组件相同的视图技术 这意味着作为开发人员 您无需为 您的自定义视图构建单独 UI 扩展 相反 您可在运行您的意图时 仅返回视图 重要的是要考虑这为您的视图 带来的具体限制 像小组件一样 自定义 App Intent 视图 不能包括交互性或动画之类的东西 确保在设计 UI 时考虑这一点 App Intents 支持 在三个阶段显示自定义 UI 即数值确认阶段 意图确认阶段 以及意图完成之后 以我的 App 为例 我将在我的意图结束时 返回一个自定义视图 如果您使用其他提示 一定要考虑在这些步骤中 集成自定义 UI 的方法 最后 如前所述 显示自定义 UI 很容易 您只需从您的意图中返回视图 好了 现在来看看代码 添加自定义视图很容易 我提到过 只需在对话框旁边返回视图 App Intents 框架就会负责 在 Siri 片段中展示我的视图 请记住 您的视图将与 其他 Siri 视图一同显示 如片段标题或确认按钮 因此您会让片段的设计 和 Siri 适配 接下来 我们看看 如何扩展 App 快捷指令 使其包含参数 在我之前的操作中 我选择开始默认的冥想会话 但我的 App 包含 许多很棒的会话类型 而且用户也会想要按自己的意愿 开始某个特定会话 理想情况下 用户能够在运行我的意图时 指定自己想要开始的会话 为了支持这种情况 我需要添加一个能捕获用户想要 运行会话的参数来扩展我的意图 为添加参数 首先我需要定义参数的类型 我会创建一个 MeditationSession 结构 包含会话的相关信息 我会把名称囊括进来 给它一个标识符字段 比如 UUID 要将此结构用作我意图的参数 我还需要实现 AppEntity 协议 实现 AppEntity 协议 会告知 App Intents 框架我的类型 并让我指定其他信息 就像实体的显示方式一样 实体协议要求 我的类型需有一个标识符 这我已经提供了 我还可以使用其他类型 例如整数或字符串 我还需要提供实体的显示方式 这类信息 这将在“快捷指令”App 以及显示我的实体的其他地方使用 最后 我需要连接一个默认查询 我将该查询称为 MeditationSessionQuery 并在接下来实施它 为了和我的实体 一起工作 App Intents 框架 要能基于框架标识符 查找到我的实体 为达此目的 EntityQuery 协议只定义了一个要求 即一个接受标识符 并返回匹配实体的函数 我将在 SessionManager 中通过查找会话 来实现此功能 接下来 我将更新 StartMeditationIntent 以添加参数 参数简单明了 只是我结构上的普通属性 但要告知 App Intents 我的参数 还需添加 @Parameter 属性包装器 该包装器能让 App Intents 知道会话属性是我意图的一部分 我还可以在 Parameter 属性包装器中 指定其他元数据 例如显示名称 现在我已经为 我的意图添加了一个参数 我需要询问用户想要运行哪个会话 App Intents 框架具有强大的支持 可用于询问用户后续问题 为我的意图参数收集值 这些提示将显示在 我的意图运行的任何位置 在 Siri 运行时 Siri 会说出问题 并要求用户说出答案 在“聚焦”和“快捷指令”App 中 用户会在触摸驱动 UI 中 看到相同的提示 App Intents 支持三种类型的值提示 消歧要求用户从固定列表中进行选择 当您意图中的某个参数 有少量固定选项时 消歧能很好地向用户展示出来 值提示允许您要求用户 提供一个开放式值 这对字符串或 整数等类型来说就非常适合 可以取任何值 最后 确认要求用户 验证特定值 还有助于 您向用户反复确认是否 理解其意图 提示值是让意图更灵活的好方法 且允许您向用户收集 更多信息 但该值也拉低了对话速度 若您经常使用 可能引起用户反感 关于设计宏大意图的更多信息 请观看 Lynn 的 “设计 App Shortcuts”的视频 好 现在我已经将会话参数添加 到 StartMeditationIntent 然后继续 在执行方法中添加逻辑 来提示这个值 在我的 App 中 用户可运行一小部分 固定数量的会话 如尚未指定会话 我就会从 SessionManager 中检索列表 并向用户呈现消歧 利用我的每个会话的显示表示 App Intents 将把会话格式化为列表项 并显示给用户 用户选择某个列表项时 所选项目就会返回给我 我会将选定的会话 传递给 MeditationService 开始会话 然后我会返回一个对话框让用户知道 意图已经开始 由于用户提供了会话 因此将会话名称输入到对话框中 就可让用户知道我们理解了其请求 很好 现在当我的用户说“开始冥想”时 我的 App 就可提示用户按其意愿运行 特定会话 不过 我之前也提到过 用户更喜欢快速直接地与 Siri 交互 理想情况下 我可让用户告知 Siri 他们想在初始短语中运行的会话 而非在后续问题中运行的会话 有个好消息要告诉大家 App Shortcuts 支持用预定义参数 扩展触发短语 通过实行参数化短语 我的 App 可以支持诸如 “开始静心冥想”或“开始行禅” 当您有一组固定的已知参数值 提前指定给 Siri 时 参数就非常实用 在我的 App 中 我会使用会话名称 参数不适用于开放式值 例如 在用户初始短语中就不可能 收集任意字符串 因此 我的 App 不支持 “在我的日记中搜索 X”这类短语 其中 X 是用户输入的任意内容 相反 您的 App 在运行时 参数值是提前指定的 我们来实现一些参数化短语 要在我的 App 中实现参数化短语 需要做一些更改 首先 我将更新 SessionEntity 的查询 以实现 suggestedResults() 方法 为我的参数化快捷指令返回实体列表 其次 当可用的 SessionEntities 列表 发生变化时 我需要通知 App Intents 框架 允许 App Intents 框架 创建新的快捷短语供 Siri 使用 我将通过更新 我 App 的模型层来做到这一点 以便在我的会话列表发生变化时 通知 App Intents 框架 最后 我将在 AppShortcut 中添加一些新短语 引用 StartMeditationIntent 上的 会话参数 那么首先 我将实现 suggestedEntities 功能 以更新 MeditationSessionQuery App Intents 框架使用该函数 返回的会话来创建参数化快捷指令 需要注意的是 虽然该方法可选 但如果不实现该方法 我根本不会得到任何参数化快捷指令 其次 我需要更新我 App 的模型层 以便在会话列表发生变化时 通知 App Intents 框架 在我的 App 中 我很少发布在后台 服务器中获取的新会话类型 只要我接收到新的会话 我就会更新 SessionModel 以调用 updateAppShortcutParameters() 方法 该方法由 App Intents 框架提供 无需您动手实现 被调用时 App Intents 将调用您的实体查询 为您的快捷短语收集参数列表 最后 我将在 AppShortcut 中添加新短语 包括我意图上的会话密钥路径 App Intents 框架会将该短语 与从我的查询中 返回的所有会话结合起来 用于每个值的文本 是从 SessionEntity 上的 标题属性中提取的 和之前一样 我想囊括 几条用户可能用来表达 我的 App 快捷指令的短语 这可在用户忘记您偏好的短语时 保证体验流畅 好了 现在我有了一个出色的 功能齐全的 App 快捷指令 迫不及待想让我的用户试一试了 但在这之前 我需要做些工作 来让用户发现我的新快捷指令 首先是挑选出色的短语 适合 App 快捷指令的 短语应简短易记 用户的手机上会有很多 App 支持 App Shortcuts 而且在实践中 用户可能无法 准确记住快捷指令短语 因此可能的话 请让您的短语简短明了 顺着这些思路 如果您的 App 名称 可用作名词或动词 那就考虑将其用在您的短语中 在我的 App 中 “冥想”就是一个名词 该词非常简短易记 最后 App 名称同义词 也可帮助您的用户 如果用户称呼的 不是您的 App 显示的名称 那您就要考虑 为其添加 App 名称同义词 iOS 11 增加了支持 App 名称同义词的性能 若您还未创建名称同义词 那现在就可以开始创建 接下来我想谈谈 Siri Tip 和“快捷指令”链接 由于 App Shortcuts 无需任何用户设置 因此可发现性对用户查找并使用 您的 App 快捷指令至关重要 有了 App Shortcuts 用户无需 将“添加到 Siri”按钮 添加到您的快捷指令中 因为该按钮已经添加进去了! 但我们也不想失去 “添加到 Siri”按钮 在可发现性上表现出的优势 考虑到这一点 我们 创建了一个新的 Siri Tip 视图 此视图适用于您过去可能会使用 “添加到 Siri”按钮的任何地方 提示视图在 SwiftUI 和 UIKit 中都可用 而且我们提供了多种样式 使其在任何 App 中的效果都很棒 当 Siri Tip 与屏幕上的内容相关时 最好根据上下文放置 如果用户刚刚 在您的 App 中下了订单 可考虑为提供订单状态的快捷指令 显示提示 Siri Tip 的放置要仔细考量 如果您觉得用户可能 不久就会使用您的 App 快捷指令 Siri Tip 还支持关闭提示 该视图包含一个关闭后按钮 点击时 可在您的代码中触发自定义关闭 您需从布局中删除视图 并在需要时 再予以显示 最后 我们还囊括了 一个新的 ShortcutsLink 这将启动一个 来自您 App 快捷方式的列表 这个新元素非常适合 您的 App 有很多 App 快捷指令 且您想让用户一一探索的情况 现在 App Shortcuts 的伟大之处在于 您的 App 一经安装 即可使用 就算在 App 首次启动之前 用户也可从“聚焦”、Siri 和 Shortcuts App 中 查看和运行您的快捷指令 在构建您的 App 快捷指令时 您可能需要考虑以下问题 例如 若您的 App 需要登录流程 用户在运行 您的意图之前可能还尚未登录 那您的意图应该 妥善处理失败情况 发送错误消息 向用户解释该 App 需要登录 其次 您的 App 快捷指令的参数化短语 在您的 App 启动 并通知 App Intents 框架 您有新的参数值之前 将无法使用 如果您的 App 快捷指令不包含 任何非参数化短语 则用户 在首次启动您的 App 之前 不会看到 App 快捷指令 您可考虑添加一些非参数化短语 来避免这个问题 此外 Siri 还增加了 支持短语功能 比如 “我能在这里做什么?”和 “我可以用冥想做什么?” Siri 会自动收集和推荐 任何 App 快捷指令短语 并替您呈现出来 您的 App 无需额外操作 该功能即可正常工作 最后 在 Siri 和“快捷指令”App 中 您的 App 快捷指令的显示顺序取决于 您在源代码中列出 AppShortcuts 的顺序 您会优先考虑列出您最好 最有用的 App 快捷指令 以便获得最多关注 同样地 您在短语数组中 列出的第一个短语 将被视为该 App 快捷指令的主要短语 主要短语用作 “快捷指令”磁贴上的标签 会在用户 就您的 App 向 Siri 寻求帮助时 显示出来 好 我们介绍了很多 App Intents 框架 和 App Shortcuts 的内容 我想请大家记住两个关键点 第一 App Shortcuts 可方便用户从系统中的任何位置 使用您的 App 因此请考虑您的 App 中 适合这种轻量级模型的最佳用例 第二 一旦您实现了 App 快捷指令 必须告知用户 否则用户不会知情! 认真思考如何 让您的 App 快捷指令可被发现 考虑 App 中有哪些地方可以展示 Siri Tip 以及非产品位置 例如网站或商店中的标志 我们期待您使用 App Intents 新框架 创建出优秀的 App Shortcuts 若要深入挖掘设计 和 App Intents 框架 请务必查看本周的其他讲解视频 谢谢 祝您体验 WWDC 愉快 ♪
-
-
3:43 - Implement an AppIntent
// StartMeditationIntent creates a meditation session. import AppIntents struct StartMeditationIntent: AppIntent { static let title: LocalizedStringResource = "Start Meditation Session" func perform() async throws -> some IntentResult & ProvidesDialog { await MeditationService.startDefaultSession() return .result(dialog: "Okay, starting a meditation session.") } }
-
5:31 - Create an AppShortcutsProvider
// An AppShortcut turns an Intent into a full fledged shortcut // AppShortcuts are returned from a struct that implements the AppShortcuts // protocol import AppIntents struct MeditationShortcuts: AppShortcutsProvider { static var appShortcuts: [AppShortcut] { AppShortcut( intent: StartMeditationIntent(), phrases: ["Start a \(.applicationName)"] ) } }
-
6:35 - Provide multiple phrases
// An AppShortcut turns an Intent into a full fledged shortcut // AppShortcuts are returned from a struct that implements the AppShortcuts // protocol import AppIntents struct MeditationShortcuts: AppShortcutsProvider { static var appShortcuts: [AppShortcut] { AppShortcut( intent: StartMeditationIntent(), phrases: [ "Start a \(.applicationName)", "Begin \(.applicationName)", "Meditate with \(.applicationName)", "Start a session with \(.applicationName)" ] ) } }
-
8:54 - Provide a dialog and snippet view
// Custom views give your intent more personality // and can convey more information func perform() async throws -> some ProvidesDialog & ShowsSnippetView { await MeditationService.startDefaultSession() return .result( dialog: "Okay, starting a meditation session.", view: MeditationSnippetView() ) }
-
10:09 - Implement an AppEntity
// An entity is a type that can be used as a parameter // for an AppIntent. import AppIntents struct MeditationSession: AppEntity { let id: UUID let name: LocalizedStringResource static var typeDisplayName: LocalizedStringResource = "Meditation Session" var displayRepresentation: AppIntents.DisplayRepresentation { DisplayRepresentation(title: name) } static var defaultQuery = MeditationSessionQuery() }
-
10:55 - Query for entities
// Queries allow the App Intents framework to // look up your entities by their identifier struct MeditationSessionQuery: EntityQuery { func entities(for identifiers: [UUID]) async throws -> [MeditationSession] { return identifiers.compactMap { SessionManager.session(for: $0) } } }
-
11:16 - Define a parameter
// Adding a parameter to an intent allows you to prompt the user // to provide a value for the parameter struct StartMeditationIntent: AppIntent { @Parameter(title: "Session Type") var sessionType: SessionType? // ... }
-
13:15 - Prompt for values
// Prompting for values can be done by calling methods // on the property's wrapper type. func perform() async throws -> some ProvidesDialog { let sessionToRun = self.session ?? try await $session.requestDisambiguation( among: SessionManager.allSessions, dialog: IntentDialog("What session would you like?") ) } await MeditationService.start(session: sessionToRun) return .result( dialog: "Okay, starting a \(sessionToRun.name) meditation session." ) }
-
16:11 - Implement suggestedEntities()
// Queries can provide suggested values for your Entity // that serve as parameters for App Shortcuts struct MeditationSessionQuery: EntityQuery { func entities(for identifiers: [UUID]) async throws -> [MeditationSession] { return identifiers.compactMap { SessionManager.session(for: $0) } } func suggestedEntities() async throws -> [MeditationSession] { return SessionManager.allSessions } }
-
16:34 - Update App Shortcut parameters
// Your app must notify App Intents when your values change // This is typically best done in your app’s model layer class SessionModel { @Published var sessions: [MeditationSession] = [] private var cancellable: AnyCancellable? init() { self.cancellable = $sessions.sink { _ in MeditationShortcuts.updateAppShortcutParameters() } } // ... }
-
17:09 - Add parameterized phrases
// Phrases can also contain a single parameter reference import AppIntents struct MeditationShortcuts: AppShortcutsProvider { static var appShortcuts: [AppShortcut] { AppShortcut( intent: StartMeditationIntent(), phrases: [ "Start a \(.applicationName)", "Begin \(.applicationName)", "Meditate with \(.applicationName)", "Start a \(\.$session) session with \(.applicationName)", "Begin a \(\.$session) session with \(.applicationName)", "Meditate on \(\.$session) with \(.applicationName)" ] ) } }
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。