iOS Siri 快捷指令开发
Siri 快捷指令,(Siri Shortcuts) 是 Apple 在系统层面提供的一种自动化能力。
通过它,用户可以将多个操作步骤组合为一个工作流程。在不打开多个应用的情况下,通过点击,语音等方式快速执行并完成复杂操作。
开发者可以在快捷指令中创建复杂的功能逻辑,例如每天早上自动获取天气并提醒;根据日历安排自动发送通知等。
Intent
开发者可以通过 Intent(Intent) 将应用能力暴露给系统,使其可以被 Siri 或 Shortcuts 调用。
Intent 本质上是一个对于应用的某个功能的结构化描述,使得系统能够理解并调用该功能。
iOS 16,新增了 App Intents 框架。
iOS 16+ 之后,推荐使用 App Intents 定义 Intent。
在此之前,则是 Intents 和 IntentsUI 两者。
在此之前,如果用户想要使用 APP 提供的快捷指令,则需要首先在应用内 Add to Siri 才行。
而使用 App Intents,则可以在用户安装应用后,直接通过 Siri 中调用应用的快捷指令。
通过在 XCode 中,为应用增加一个 App Intents Extension 来使用 App Intents。
// 定义一个可被系统调用的 Intent
struct TodayWeather: AppIntent {
static let title: LocalizedStringResource = "今天天气怎么样?"
static let description: IntentDescription = IntentDescription("查看天气怎么样")
static let openAppWhenRun: Bool = false // 执行后不打开应用
// 具体执行逻辑
@MainActor
func perform() async throws -> some IntentResult {
if let ok = await fetchWeather() {
return .result(value: "天气很好")
}
return .result(value: "天气不好")
}
}
Shortcuts
光有 Intent 还不够,Siri 无法直接调用 Intent。
我们需要增加快捷指令来调用 Intent。
import AppIntents
struct TodayWeatherShortcuts: AppShortcutsProvider {
// appShortcuts 是一个数组,因此可以向外暴露多个 Intent
static var appShortcuts: [AppShortcut] {
AppShortcut(
intent: TodayWeather(),
phrases: [
"使用\(.applicationName)查看天气",
"打开\(.applicationName)查看天气",
"使用\(.applicationName)查询天气",
"查看\(.applicationName)获取天气",
],
shortTitle: "查看天气",
systemImageName: "bolt.fill"
)
}
}
phrases 中的成员就是暴露给 Siri 的快捷指令。
它是一个数组,可以存放多条近似内容。但每个成员都必须包含应用名称。
这是基于安全考虑的,如果应用注册了一个全局的指令,例如:“给 xxx 转账”,会出现巨大安全风险,因此官方要求必须带有应用名称。
视图
可以根据定义的 Intent,创建对应的视图 UI 界面。在不进入应用的情况下,将结果更好的展示给用户。
import SwiftUI
struct ResultInfoView: View {
let title: String
let weather: String
var body: some View {
VStack(alignment: .leading) {
Text(title).font(.body)
Spacer()
Text(weather).font(.subheadline).foregroundColor(.secondary)
}
}
}
struct TodayWeather: AppIntent {
static let title: LocalizedStringResource = "今天天气怎么样?"
static let description: IntentDescription = IntentDescription("查看天气怎么样")
static let openAppWhenRun: Bool = false // 执行后不打开应用
@MainActor
func perform() async throws -> some IntentResult {
var title = "天气不好"
var weather = "阴天"
if let ok = await fetchWeather() {
title = "天气很好"
weather = "晴天"
}
let snippet = ResultInfoView(title: title, weather: wather)
let dialog = IntentDialog(
full: title,
supporting: weather)
// value - Siri 返回的文本
// dialog - Siri 语音或文本反馈
// view - 展示给用户的 UI
return .result(value: title, dialog: dialog, view: snippet)
}
}
注册
在 App 入口,调用 TodayWeatherShortcuts.updateAppShortcutParameters()。
通常放在 AppDelegate 或启动逻辑中。
目录路径
例如:
App/TodayWeather/
- TodayWeather.entitlements
- TodayWeather.swift
- TodayWeatherExtension.swift