iOS 16 Apple为我们带来了全新的快捷指令框架 App Intents。在iOS 16之前,快捷指令是和Siri息息相关的。与iOS 16之前Siri Shortcut相比,新框架最大的优势是用户安装App后可立即使用快捷指令,无需在App内操作添加到Siri这个过程。
2024 WWDC 总更新预览(技术层面更新)
在WWDC2024 会议中提到的App Intents的主要技术上的更新内有以下几个方面
系统集成
- 将您的应用与 Siri 和 ~~Apple Intelligence ~~通过 App 意图域集成。
- 使用
ControlConfigurationIntent和WidgetKit,允许用户将控件放置在锁屏或控制中心。 - 为您的应用创建锁定的相机捕获扩展,并实现
CameraCaptureIntent,允许用户通过控件或操作按钮捕获照片和视频。 - 通过实现
AudioRecordingIntent创建捕获音频的应用意图。 - 通过实现
IndexedEntity协议允许用户在 Spotlight 中查找应用实体。
内容共享
- 通过遵循
Transferable,使得共享和传输您描述应用实体的数据成为可能。 - 使用
IntentFile作为应用意图参数,通过应用意图接收其他应用提供的内容。 - 使用
FileEntity描述存储应用意图数据的文件。
通用
-
提供关于错误的附加信息,例如
AppIntentError.PermissionRequired、AppIntentError.Unrecoverable和AppIntentError.UserActionRequired。 -
传递一个条件到
requestConfirmation(conditions:actionName:dialog:),仅当用户的上下文满足所提供的条件时才需要用户确认。 -
使用
URLRepresentableIntent、URLRepresentableEntity和URLRepresentableEnum将您的应用意图、应用实体和应用枚举表示为通用链接,以便您提供应用内容的深层链接。 -
使用
UnionValue()宏为意图参数定义一组类型,以创建灵活的应用意图,因为参数可以是几个预定义联合类型之一。
下面将为这些更新内容做详细的具体介绍:
Spotlight integration
2024年的更新中,Spotlight 的集成得到了显著增强,使得用户能够通过 Spotlight 更轻松地访问应用程序的功能。
主要特点:
-
增强索引和搜索能力:
应用程序可以将更多的内容和功能索引到 Spotlight 中,使用户可以通过简单的搜索直接访问特定的应用功能。例如,用户可以通过 Spotlight 搜索直接找到应用中的特定任务或快捷指令,而不必打开应用并导航到相应的功能。
-
快捷指令的直接触发:
用户可以在 Spotlight 搜索结果中直接触发应用的快捷指令。这使得常用功能变得更加触手可及,提升了用户体验。
-
智能建议:
Spotlight 可以根据用户的使用习惯和上下文,智能地建议相关的快捷指令。例如,用户每天早上都会使用的快捷指令将在早上自动出现在 Spotlight 中。
视频代码内容
IndexedEntity 协议
通过采用新增的 IndexedEntity 协议允许用户在 Spotlight 中查找应用实体。
IndexedEntit能够将您的应用实体索引到 CSSearchableIndex,同时仍然允许您自定义属性集。这使得您的实体能够显示在Spotlight搜索结果中,并帮助Siri理解和找到它们。
// Add conformance to the new protocol
extension TrailEntity: IndexedEntity { }
// In App's init method, index the trail entities in the data manager
try await CSSearchableIndex
.default()
.indexAppEntities(
trailDataManager
.trails
.map(TrailEntity.init(trail:))
)
// 自定义属性集
extension TrailEntity: IndexedEntity {
var attributeSet: CSSearchableItemAttributeSet {
let attributes = CSSearchableItemAttributeSet()
attributes.city = self.city
attributes.stateOrProvince = self.state
attributes.keywords = activities.map(.rawValue)
return attributes
}
}
associateAppEntity可以使用新API添加语义搜索支持和设置优先级
public extension CSearchableItem {
func associateAppEntity(
_ appEntity: some IndexedEntity,
priority: Int
)
}
通过该协议,开发人员可以为他们的应用程序中的所有项目(或实体)创建索引,为每个项目赋予一组属性(包括关键字),甚至可以为它们分配优先级以匹配收藏夹列表等功能。然后,通过在应用程序启动或更新时将其交给 Spotlight,所有数据都将被索引和搜索,并且在使用自然语言查询时更容易匹配。
使用场景:
App 可以将用户的睡眠事件索引到 Spotlight 中,使用户可以通过 Spotlight 搜索快速查看和管理睡眠信息。例如,用户可以搜索“睡眠”并立即查到当天的睡眠详情。
Entities and Files
2024年的更新中,App Intents 对实体和文件的处理能力得到了提升,允许开发者创建更复杂和强大的快捷指令。
主要特点:
-
丰富的实体支持:
开发者可以在 App Intents 中定义和使用复杂的实体(Entities),这些实体可以包含丰富的数据和属性。例如,一个待办事项应用可以定义一个“任务”实体,其中包含任务的标题、截止日期、优先级等属性。
-
文件处理:
App Intents 现在支持处理各种类型的文件,包括文本文件、图片、视频等。这使得应用程序能够创建涉及文件操作的快捷指令,例如将文件上传到云端、在文件中添加注释等。
-
动态实体: 实体可以根据应用的状态或用户的输入动态生成。例如,用户在快捷指令中选择一个项目后,可以动态生成该项目的相关任务列表。
视频代码内容
Make entities meaningful
将AppEntity 导出为不同的类型并在不同的地方使用,使实体变得更有意义
extension ActivityStatisticsSummary: Transferable {
static var transferRepresentation: some TransferRepresentation {
DataRepresentation(exportedContentType: .rtf) { summary in
try summary.asRTFData
}
FileRepresentation(exportedContentType: .png) { summary in
SentTransferredFile(try summary.asImageFile)
}
}
}
IntentFile
检查可用的内容类型,并使用所需的类型
struct AppendToNote: AppIntent {
@Parameter var not: NoteEntity
@Parameter(title: "content to append", supportedContentTypes: [.jpeg, .rtf])
var attachment: IntentFile
// ...
}
struct AppendToNote: AppIntent {
// ...
public func perform() async throws -> some IntentResult {
if attachment.availableContentTypes.contains(.png) {
let png = try await attachment
.withFile(contentType: .png) { url, openedInPlace in
guard let image = UIImage(contentsOfFile: url.absolutePath) else {
throw Error.unableToLoadImage
}
returnImage
}
}
return .result()
}
}
FileEntity
- 非常适合基于文档的应用程序或管理文件的应用程序
struct PhotoEntity: FileEntity {
static var typeDisplayRepresentation: TypeDisplayRepresentation = "My Photo Entity"
static var supportedContentTypes: [UTType] = [.png]
var id: FileEntityIdentifier
@Property(title: "Width")
var width: Double
@Property(title: "Height")
var height: Double
}
使用场景:
- App 可以让用户通过快捷指令创建带有图片的意见反馈。例如,用户可以使用快捷指令从照片库中选择图片并进行意见反馈上传。
Universal Links
Universal Links 的增强使得 App Intents 能够更无缝地与应用的深层链接(deep links)集成。
主要特点:
-
深度链接集成:
- App Intents 支持与应用的 Universal Links 深度集成,使用户可以通过点击链接直接进入应用的特定页面或功能。例如,用户收到一封包含特定任务链接的电子邮件,点击链接即可直接打开应用中的该任务详情页。
-
跨平台支持:
- Universal Links 现在支持跨不同设备和平台的一致体验。用户可以在iOS设备上创建一个快捷指令并通过Universal Links在macOS设备上无缝继续操作。
-
智能处理:
- 应用程序可以智能地处理通过 Universal Links 传递的数据,并根据数据的内容自动执行相应的操作。例如,点击一个包含用户信息的链接时,应用可以自动填充表单并准备提交。
视频代码内容
新 API:
URLRepresentableEntityURLRepresentableEnumURLRepresentableIntent
首先使实体符合URLRepresentableEntity:
extension TrailEntity: URLRepresentableEntity {
static var urlRepresentation: URLRepresentation {
"https://trailsapp.example/trail/(.id)/details"
}
}
然后你就可以深度链接到内容:
struct OpenTrailIntent: OpenIntent, URLRepresentableIntent {
static var title: LocalizedStringResource = "Open Trail"
static var parameterSummary: some ParameterSummary {
Summary("Open (.$target)")
}
@Parameter(title: "Trail")
var target: TrailEntity
}
或者从perform函数中中返回一个OpenURLIntent:
func perform() async throws -> some OpensIntent {
let newTrail = TrailEntity(name: name)
.result(
// You can open a URL directly
/* opensIntent: OpenURLIntent(
* "https://developer.apple.com"
* ) */
// Or use urlRepresentable to
opensIntent: OpenURLIntent(urlRepresentable: newTrail)
)
}
使用场景:
- App 可以通过 Universal Links 直接带用户进入开通会员页面。
Developer improvements
UnionValue
对于开发人员来说,UnionValue 意味着当一个参数需要接受多种不同类型的值(如字符串、数字、或文件)时,可以使用 UnionValue 来支持这些类型。而无需为每种类型分别定义参数。
- 可能不止一种类型
- 枚举中的每个案例必须只有一个关联值
- 必须是唯一的
@UnionValue
enum DayPassType {
case park(ParkEntity)
case trail(TrailEntity)
}
struct BuyDayPass: AppIntent {
// ...
@Parameter var passType: DayPassType
// ...
@Parameter var input: UnionValue<String, Int, FileEntity>
func perform() async throws -> some IntentResult {
switch passType {
case let .park(park):
// purchase for park
case let .trail(trail):
// purchase for trail
}
switch input {
case .string(let text):
case .int(let number):
case .file(let file):
}
}
Generated titles
不再需要为 AppEntity 属性或参数提供标题
struct SuggestTrails: AppIntent {
@Parameter(title: "Activity")
var activity: ActivityStyle
@Parameter(title: "Search Radius")
var searchRadius: Measurement<UnitLength>?
@Parameter(title: "Location")
var location: String?
@Parameter(title: "Featured Collection")
var trailCollection: TrailCollection?
}
// 上面的代码将变体成下面的这种写法
struct SuggestTrails: AppIntent {
@Parameter var activity: ActivityStyle
@Parameter var searchRadius: Measurement<UnitLength>?
@Parameter var location: String?
@Parameter(title: "Featured Collection")
var trailCollection: TrailCollection?
}
Framework improvements
AppIntent 类型不再必须位于同一模块中,devs可以在框架中使用 AppEntities 并从应用程序和扩展目标中引用