Meet Focus filters
System & Services 进阶 20m

认识 Focus 过滤器

Meet Focus filters

2022年6月6日

在 Apple 官方观看视频

一句话判断

Focus filters 让你的应用能根据用户当前的专注模式自动调整行为——如果你做多账号、多日历或内容筛选类功能,这个 API 应该立刻排进排期。

这场 Session 讲了什么

iOS 16 和 macOS Ventura 为 Focus(专注模式)引入了 Focus Filters 机制。之前 Focus 只能控制系统层面的通知行为,现在它可以深入到每个应用的内部逻辑。

系统应用的示范很直观:

  • 日历:Work Focus 时只显示工作日历,Personal Focus 时只显示个人日历
  • 邮件:按 Focus 过滤收件箱和通知,只显示当前场景相关的邮件
  • Safari:不同 Focus 使用不同的标签页组

开发者可以通过 SetFocusFilterIntent 协议让自己的应用也支持这种上下文感知。用户在系统设置的 Focus 配置界面中就能直接定制你的应用在不同 Focus 下的行为。

值得深挖的点

Focus Filter 的信息传递机制是整个框架的核心。你的应用定义一个 SetFocusFilterIntent,声明哪些参数可以被用户配置(比如”显示哪个账号”、“是否启用深色模式”)。系统在用户切换 Focus 时通过两种方式通知你的应用:

  1. 应用正在运行时,调用 perform() 方法
  2. 应用不在运行时,通过 App Extension 接收通知

Display Representation 的动态生成决定了用户在设置界面看到的文案。主标题描述”配置了什么”,副标题描述”配置成了什么”。因为是动态生成的,你可以在用户配置了不同参数组合时显示不同的描述文案。这个细节直接影响用户对你的 Focus Filter 的理解程度。

参数类型的灵活性值得关注。除了标准的 Bool、String、Float 类型,你还可以用自定义 Entity 作为参数。这意味着”选择一个特定账号”、“选择一个特定日历”这种复杂参数也能被 Focus Filter 支持。

代码片段

定义一个 Focus Filter:

import AppIntents

// 定义你的 Focus 过滤器
struct MyFocusFilter: SetFocusFilterIntent {
    static var title: LocalizedStringResource = "我的应用过滤器"
    static var description = IntentDescription("根据专注模式调整应用行为")

    // 必填参数:是否使用深色模式
    @Parameter(title: "始终使用深色模式", default: false)
    var alwaysUseDarkMode: Bool

    // 可选参数:状态消息
    @Parameter(title: "状态消息")
    var statusMessage: String?

    // 可选参数:自定义实体(账号)
    @Parameter(title: "选择账号")
    var account: AccountEntity?

    // 动态显示配置摘要
    static var displayRepresentation: DisplayRepresentation {
        // 根据实际配置的参数动态生成显示文案
    }
}

在应用中响应 Focus 变化:

// 当 Focus 切换时,系统调用 perform 方法
func perform() async throws -> some IntentResult {
    // 读取当前 Focus 的配置值
    if alwaysUseDarkMode {
        // 切换到深色模式
        ThemeManager.shared.applyDarkMode()
    }

    if let account = account {
        // 切换到指定账号
        AccountManager.shared.switchTo(account)
    }

    if let status = statusMessage {
        // 更新状态消息
        StatusManager.shared.update(status)
    }

    return .result()
}

最佳实践

  • 必填参数给默认值:所有非 optional 的 Parameter 必须有合理默认值
  • Display Representation 要写清楚:用户在 Focus 设置中看到的就是这些文案,模糊的描述会导致配置错误
  • Focus 变化可能频繁发生:perform() 方法要做轻量处理,不要在这里做耗时操作
  • 支持 App Extension:如果你的应用有后台需求,记得实现对应的 Extension 来接收 Focus 变更
  • 减少干扰而非完全屏蔽:Focus Filter 的目的是帮用户聚焦,不是把功能全部锁死。考虑降低 badge 数量、收起不相关内容而不是完全隐藏

还有什么值得关注

  • Focus Filter 的配置 UI 是系统自动生成的,你不需要自己画界面
  • 与 App Intents 框架深度绑定,建议同步了解 App Intents 的 Entity 系统
  • 多账号类应用是 Focus Filter 最自然的适配场景,如果你的应用支持”工作账号/个人账号”切换,这个功能几乎是必做的
  • 系统在应用安装时就会读取 title 和 description,这两者是静态的,不支持动态更新
WWDC 2022