What’s new in App Intents (一)

1,338 阅读8分钟

iOS 16 Apple为我们带来了全新的快捷指令框架 App Intents。在iOS 16之前,快捷指令是和Siri息息相关的。与iOS 16之前Siri Shortcut相比,新框架最大的优势是用户安装App后可立即使用快捷指令,无需在App内操作添加到Siri这个过程。

2024 WWDC 总更新预览(技术层面更新)

在WWDC2024 会议中提到的App Intents的主要技术上的更新内有以下几个方面

系统集成

  1. 将您的应用与 Siri 和 ~~Apple Intelligence ~~通过 App 意图域集成。
  2. 使用 ControlConfigurationIntentWidgetKit,允许用户将控件放置在锁屏或控制中心。
  3. 为您的应用创建锁定的相机捕获扩展,并实现 CameraCaptureIntent,允许用户通过控件或操作按钮捕获照片和视频。
  4. 通过实现 AudioRecordingIntent 创建捕获音频的应用意图。
  5. 通过实现 IndexedEntity 协议允许用户在 Spotlight 中查找应用实体。

内容共享

  1. 通过遵循 Transferable,使得共享和传输您描述应用实体的数据成为可能。
  2. 使用 IntentFile 作为应用意图参数,通过应用意图接收其他应用提供的内容
  3. 使用 FileEntity 描述存储应用意图数据的文件。

通用

  1. 提供关于错误的附加信息,例如 AppIntentError.PermissionRequiredAppIntentError.UnrecoverableAppIntentError.UserActionRequired

  2. 传递一个条件到 requestConfirmation(conditions:actionName:dialog:),仅当用户的上下文满足所提供的条件时才需要用户确认。

  3. 使用 URLRepresentableIntentURLRepresentableEntityURLRepresentableEnum 将您的应用意图、应用实体和应用枚举表示为通用链接,以便您提供应用内容的深层链接。

  4. 使用 UnionValue() 宏为意图参数定义一组类型,以创建灵活的应用意图,因为参数可以是几个预定义联合类型之一。

下面将为这些更新内容做详细的具体介绍:

Spotlight integration

2024年的更新中,Spotlight 的集成得到了显著增强,使得用户能够通过 Spotlight 更轻松地访问应用程序的功能。

主要特点:

  1. 增强索引和搜索能力

    应用程序可以将更多的内容和功能索引到 Spotlight 中,使用户可以通过简单的搜索直接访问特定的应用功能。例如,用户可以通过 Spotlight 搜索直接找到应用中的特定任务或快捷指令,而不必打开应用并导航到相应的功能。

  2. 快捷指令的直接触发

    用户可以在 Spotlight 搜索结果中直接触发应用的快捷指令。这使得常用功能变得更加触手可及,提升了用户体验。

  3. 智能建议

    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 对实体和文件的处理能力得到了提升,允许开发者创建更复杂和强大的快捷指令。

主要特点:

  1. 丰富的实体支持

    开发者可以在 App Intents 中定义和使用复杂的实体(Entities),这些实体可以包含丰富的数据和属性。例如,一个待办事项应用可以定义一个“任务”实体,其中包含任务的标题、截止日期、优先级等属性。

  2. 文件处理

    App Intents 现在支持处理各种类型的文件,包括文本文件、图片、视频等。这使得应用程序能够创建涉及文件操作的快捷指令,例如将文件上传到云端、在文件中添加注释等。

  3. 动态实体: 实体可以根据应用的状态或用户的输入动态生成。例如,用户在快捷指令中选择一个项目后,可以动态生成该项目的相关任务列表。

视频代码内容

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
}

使用场景:

  1. App 可以让用户通过快捷指令创建带有图片的意见反馈。例如,用户可以使用快捷指令从照片库中选择图片并进行意见反馈上传。

Universal Links

Universal Links 的增强使得 App Intents 能够更无缝地与应用的深层链接(deep links)集成。

主要特点:

  1. 深度链接集成

    1. App Intents 支持与应用的 Universal Links 深度集成,使用户可以通过点击链接直接进入应用的特定页面或功能。例如,用户收到一封包含特定任务链接的电子邮件,点击链接即可直接打开应用中的该任务详情页。
  2. 跨平台支持

    1. Universal Links 现在支持跨不同设备和平台的一致体验。用户可以在iOS设备上创建一个快捷指令并通过Universal Links在macOS设备上无缝继续操作。
  3. 智能处理

    1. 应用程序可以智能地处理通过 Universal Links 传递的数据,并根据数据的内容自动执行相应的操作。例如,点击一个包含用户信息的链接时,应用可以自动填充表单并准备提交。

视频代码内容

新 API:

  • URLRepresentableEntity
  • URLRepresentableEnum
  • URLRepresentableIntent

首先使实体符合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 并从应用程序和扩展目标中引用

参考文档

  1. App Intents updates | Apple Developer Documentation
  2. App Intents | Apple Developer Documentation
  3. Introduction to Apple Intelligence with Siri & App Intents, Episode 3: Demo
  4. GitHub - dacharyc/WWDC2024-Notes: Notes from (virtually) attending WWDC2024