注意:
- 快捷指令只要定义便可以在快捷方式工具中心中使用
- 经过试验快捷指令如果不通过 AppShortcutsProvider 注册无法在 快捷指令、Spotlight 及 Siri 中展示和使用
定义Intent
定义 Intent 需要实现 AppIntent 协议
struct MyIntent: AppIntent {
static var title = LocalizedStringResource("Dialog")
static var description = IntentDescription("Dialog描述")
func perform() async throws -> some IntentResult & ProvidesDialog & ReturnsValue<String> {
return .result(value: "## title", dialog: IntentDialog(stringLiteral: "Result message"))
}
}
AppIntent 在实现快捷指令时需要根据需求实现以下方法:
-
title(必须实现):快捷指令名称_
-
description(可选):快捷指令描述_
-
openAppWhenRun(可选):执行快捷指令时打开App
-
perform(必须实现):业务逻辑处理
在快捷指令中使用,【快捷指令】->【+】->【搜索指令】添加,添加完效果如下:
openAppWhenRun 打开App
struct OpenIntent: AppIntent {
static var title = LocalizedStringResource("输入框")
static var description = IntentDescription("输入框描述")
// 意图执行时,是否自动将应用拉起到前台
static var openAppWhenRun: Bool = true
func perform() async throws -> some IntentResult & ProvidesDialog {
return .result(dialog: "app拉起了")
}
}
在 AppIntent 中配置 openAppWhenRun 为 true,快捷指令执行时就会默认拉起主App,效果如下:
注册快捷指令
注册快捷指令需要继承 AppShortcutsProvider 协议并实现对应方法。
struct MyShortcuts: AppShortcutsProvider {
static var appShortcuts: [AppShortcut] {
AppShortcut(
intent: MyIntent(),
phrases: [
"Start a \(.applicationName)",
"Start a session with \(.applicationName)"
],
shortTitle: "Start a Task",
systemImageName: "plus.circle"
)
}
}
AppShortcutsProvider 在实现快捷指令时需要根据需求实现以下方法:
- appShortcuts:快捷指令数组
- AppShortcut 快捷指令前面提过,不清楚的可以查看【iOS小组件】Siri和快捷指令
快捷指令返回值类型
注意:
- 快捷指令
perform()函数返回值的类型和真实返回的类型必须匹配否则会引起崩溃
快捷指令支持多种返回值类型及组合返回值类型
- IntentResult: **
perform()**的返回值类型,支持无参 - ProvidesDialog:提供对话框类型
- ShowsSnippetView:展示自定义视图
- OpensIntent:打开意图
- ReturnsValue:返回一个结果值类型
1.IntentResult
struct MyIntent: AppIntent {
static var title = LocalizedStringResource("MyIntent")
static var description = IntentDescription("MyIntent描述")
func perform() async throws -> some IntentResult {
return .result()
}
}
2.ProvidesDialog
struct MyIntent: AppIntent {
static var title = LocalizedStringResource("MyIntent")
static var description = IntentDescription("MyIntent描述")
func perform() async throws -> some ProvidesDialog {
return .result(dialog: "dialog title")
}
}
3.ShowsSnippetView
struct MyIntent: AppIntent {
static var title = LocalizedStringResource("MyIntent")
static var description = IntentDescription("MyIntent描述")
func perform() async throws -> some ShowsSnippetView {
return .result(view:
VStack(spacing: 10) {
Text("this is text")
Image(systemName: "plus.circle")
Button("this is a button") {}
}.padding()
)
}
}
4.OpensIntent
struct MyIntent: AppIntent {
static var title = LocalizedStringResource("MyIntent")
static var description = IntentDescription("MyIntent描述")
func perform() async throws -> some OpensIntent {
print("预处理打印")
return .result(opensIntent: MyIntent2())
}
}
struct MyIntent2: AppIntent {
static var title = LocalizedStringResource("MyIntent2")
static var description = IntentDescription("MyIntent2描述")
func perform() async throws -> some IntentResult {
print("最终处理打印")
return .result()
}
}
5.ReturnsValue
struct MyIntent: AppIntent {
static var title = LocalizedStringResource("MyIntent")
static var description = IntentDescription("MyIntent描述")
func perform() async throws -> some ReturnsValue<Int> {
return .result(value: 1111)
}
}
快捷指令复合类型
1.弹窗dialog
struct MyIntent: AppIntent {
static var title = LocalizedStringResource("MyIntent")
static var description = IntentDescription("MyIntent描述")
func perform() async throws -> some IntentResult & ProvidesDialog {
return .result(dialog: "this is a dialog")
}
}
2.自定义弹窗
注意:
perform()返回值类型为需要是some IntentResult & ProvidesDialog & ShowsSnippetView或者some ProvidesDialog & ShowsSnippetView否则会报错
// 自定义弹窗
struct CustomDialogIntent: AppIntent {
static var title = LocalizedStringResource("自定义弹窗信息")
static var description = IntentDescription("自定义弹窗信息描述")
func perform() async throws -> some IntentResult & ProvidesDialog & ShowsSnippetView {
return .result(dialog: .init("Dialog标题")) {
VStack(spacing: 10) {
Text("自定义的弹窗内容")
Text("自定义的弹窗内容")
Text("自定义的弹窗内容")
}
}
}
}
3. 自定义返回及视图
struct TrailEntity: AppEntity {
let id: String
let name: String
let currentConditions: String
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Trail"
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(
title: "\(name)",
subtitle: "Current conditions: \(currentConditions)"
)
}
static var defaultQuery = TrailQuery()
struct TrailQuery: EntityQuery {
func entities(for identifiers: [String]) async throws -> [TrailEntity] {
// 在实际应用中,这里应该从数据源获取实体
// 这里我们只返回一个示例实体
return [TrailEntity(id: "11", name: "Sample Trail", currentConditions: "Good")]
}
func suggestedEntities() async throws -> [TrailEntity] {
// 返回建议的实体列表
return [TrailEntity(id: "11", name: "Popular Trail", currentConditions: "Excellent")]
}
}
}
struct TrailInfoView: View {
let trail: TrailEntity
let includeConditions: Bool
var body: some View {
VStack(alignment: .leading) {
Text(trail.name)
.font(.headline)
if includeConditions {
Text("Current Conditions: \(trail.currentConditions)")
.font(.subheadline)
}
}
}
}
struct TrailInfoIntent: AppIntent {
static var title: LocalizedStringResource = "Get Trail Info"
static var description = IntentDescription("Retrieves information about a specific trail.")
@Parameter(title: "Trail")
var trail: TrailEntity?
func perform() async throws -> some IntentResult & ReturnsValue<TrailEntity> & ProvidesDialog & ShowsSnippetView {
guard let trail else {
throw $trail.needsValueError(.init("trail?"))
}
let snippet = TrailInfoView(trail: trail, includeConditions: true)
let dialog = IntentDialog(
full: "The latest conditions reported for \(trail.name) indicate: \(trail.currentConditions).",
supporting: "Here's the latest information on trail conditions."
)
return .result(value: trail, dialog: dialog, view: snippet)
}
}
struct TrailInfoShortcuts: AppShortcutsProvider {
static var appShortcuts: [AppShortcut] {
AppShortcut(
intent: TrailInfoIntent(),
phrases: ["\(.applicationName) 创建用户表单"],
shortTitle: "创建用户表",
systemImageName: "plus.circle"
)
}
}
快捷指令自定义参数
1.基本数据类型
struct MyIntent: AppIntent {
static var title = LocalizedStringResource("MyIntent")
static var description = IntentDescription("MyIntent描述")
@Parameter(title: "name", description: "用户姓名")
var name: String?
@Parameter(title: "age", description: "用户年龄")
var age: Int?
@Parameter(title: "isChinese", description: "用户国籍为中国?", default: true)
var isChinese: Bool
func perform() async throws -> some IntentResult & ShowsSnippetView {
return .result(view: VStack{
Text("name: \(name ?? "")")
Text("age: \(age ?? 18)")
Text("isChinese: \(isChinese)")
})
}
}
2.输入框
import AppIntents
import SwiftUI
// 输入框
struct InputIntent: AppIntent {
static var title = LocalizedStringResource("输入框")
static var description = IntentDescription("输入框描述")
// 定义参数
@Parameter(title:"用户昵称")
var username: String?
func perform() async throws -> some IntentResult & ProvidesDialog {
return .result(dialog: "显示用户昵称: \(username)")
}
}
- 使用
@Parameter定义一个参数 - 使用($参数名)内置对象调用
needsValueError方法弹出一个提示弹窗进行数据录入和验证
3.枚举Intent
// 枚举
enum TemperatureUnit: String, CaseIterable, AppEnum {
case celsius
case fahrenheit
// 类型描述
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Temperature Unit"
// 枚举值
static var caseDisplayRepresentations: [TemperatureUnit : DisplayRepresentation] = [
.celsius: "Celsius (°C)",
.fahrenheit: "Fahrenheit (°F)"
]
}
struct InputIntent: AppIntent {
static var title = LocalizedStringResource("输入框")
static var description = IntentDescription("输入框描述")
// 定义参数
@Parameter(title:"用户昵称")
var username: String?
// 温度枚举
// @Parameter(title: "Temperature Unit", default: TemperatureUnit.celsius)
// var temperatureUnit: TemperatureUnit
// add --- 设置为可选
@Parameter(title: "Temperature Unit")
var temperatureUnit: TemperatureUnit?
func perform() async throws -> some IntentResult & ProvidesDialog {
// 数据验证
guard let username else {
throw $username.needsValueError()
}
// 验证数据长度
if username.count < 3 {
throw $username.needsValueError(.init("用户昵称小于3个字符"))
}
// add --- 验证枚举
guard let temperatureUnit else {
throw $temperatureUnit.needsValueError()
}
return .result(dialog: "显示用户昵称: \(username), 温度: \(temperatureUnit)")
}
}
...
3.对象Intent
构建一个建议对象列表,选取返回一个对象信息
import AppIntents
import SwiftUI
var WeatherLocations: [String: WeatherLocation] = [
"london": WeatherLocation(id: "london", name: "London", latitude: 51.5074, longitude: -0.1278),
"new-york": WeatherLocation(id: "new-york", name: "New York", latitude: 40.7128, longitude: -74.0060),
"tokyo": WeatherLocation(id: "tokyo", name: "Tokyo", latitude: 35.6762, longitude: 139.6503)
]
struct WeatherLocation: AppEntity {
let id: String
let name: String
let latitude: Double
let longitude: Double
// 配置界面中的显示方式
static var typeDisplayRepresentation: TypeDisplayRepresentation = "Weather Location"
// 显示的参数名称
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(title: "\(name)")
}
// 默认数据查询
static var defaultQuery = WeatherLocationQuery()
}
struct WeatherLocationQuery: EntityQuery {
// 根据 ID 获取位置实体
func entities(for identifiers: [WeatherLocation.ID]) async throws -> [WeatherLocation] {
return identifiers.compactMap { id in
return WeatherLocations[id]
}
}
// 建议列表
func suggestedEntities() async throws -> [WeatherLocation] {
return Array(WeatherLocations.values)
}
}
struct ConfigurationAppIntent: AppIntent {
static var title: LocalizedStringResource = "Configuration"
static var description = IntentDescription("This is an example widget.")
@Parameter(title: "Location")
var location: WeatherLocation?
func perform() async throws -> some ProvidesDialog & ShowsSnippetView {
guard (location != nil) else {
throw $location.needsValueError()
}
return .result(dialog: "") {
VStack(spacing: 10) {
Text("location info: \(String(describing: location))")
}
}
}
}
struct ObjectShortcuts: AppShortcutsProvider {
static var appShortcuts: [AppShortcut] {
AppShortcut(
intent: ConfigurationAppIntent(),
phrases: ["\(.applicationName) 对象"],
shortTitle: "对象",
systemImageName: "lock.open"
)
}
}
数据验证
1.强校验
import AppIntents
import SwiftUI
// 输入框
struct InputIntent: AppIntent {
static var title = LocalizedStringResource("输入框")
static var description = IntentDescription("输入框描述")
// 定义参数
@Parameter(title:"用户昵称")
var username: String?
func perform() async throws -> some IntentResult & ProvidesDialog {
// 数据验证
guard let username else {
throw $username.needsValueError()
}
// 验证数据长度
if username.count < 3 {
throw $username.needsValueError(.init("用户昵称小于3个字符"))
}
return .result(dialog: "显示用户昵称: \(username), 温度: \(temperatureUnit)")
}
}
2.校验建议
struct MyIntent: AppIntent {
static var title = LocalizedStringResource("MyIntent")
static var description = IntentDescription("MyIntent描述")
@Parameter(title: "name", description: "用户姓名")
var name: String?
func perform() async throws -> some IntentResult & ShowsSnippetView {
guard let name else {
throw $name.needsDisambiguationError(among: ["1", "2"])
}
return .result(view: VStack{
Text("name: \(name)")
})
}
}
本文同步自微信公众号 "程序员小溪" ,这里只是同步,想看及时消息请移步我的公众号,不定时更新我的学习经验。