WWDC Quick Look 💓 By SwiftGGTeam
What's new in assessment

What's new in assessment

观看原视频

Highlight

Apple 把 Automatic Assessment Configuration 带到 Mac,并用同一套 AEAssessmentSession API 统一 iPhone、iPad、Mac 和 Catalyst 上的受控考试模式。


核心内容

数字考试应用最怕两件事。第一,考试开始后学生能切到别的 App、打开字典、用自动补全或查看通知。第二,学校的设备并不总是被 MDM 完全托管,开发者很难为个人设备和学校设备做两套可靠流程。早期 iPad 的 autonomous single app mode 依赖 MDM,后来 Apple 用 entitlement 驱动的 Automatic Assessment Configuration (AAC) 降低了部署门槛。(00:28

2020 年的变化是 Mac 也进入了这套模型。Mac 进入 assessment mode 后,只显示考试 App;Dock、Mission Control、Notification Center 不可用;屏幕录制和截图被阻止;其他 App 的窗口会被隐藏,其他进程的网络访问会被阻断;媒体播放会暂停,剪贴板会在会话开始和结束时清空。开发者不用自己封装这些系统限制,只要在合适时机请求系统进入考试模式。(01:14

AAC 的生命周期很明确:App 自己决定何时开始限制设备,创建配置和会话,调用 begin(),等系统完成限制后再展示考试内容。考试结束时调用 end(),等系统恢复后展示结果页或确认页。进入和退出都可能需要时间,所以演讲反复强调要在这两个阶段展示过渡 UI,避免学生以为 App 卡住。(02:14

iOS 和 iPadOS 也迁移到新的 AAC framework。AEAssessmentConfiguration 默认是最严格的配置,但 iOS 14 开始能按考试需要开启 dictation、predictive keyboard、spell check 等系统服务。数学考试可以允许拼写检查,语言考试可以根据输入方式需求开放连续路径键盘。这个配置对象会随系统能力扩展,开发者可以把考试公平性和学生可访问性放在同一个会话模型里处理。(10:12


详细内容

启动和结束 AEAssessmentSession

03:52)AAC 的核心对象是 AEAssessmentConfigurationAEAssessmentSession。配置对象定义考试体验,session 负责进入、退出和回调生命周期事件。

import AutomaticAssessmentConfiguration

class AssessmentManager: NSObject {
    private var assessmentSession: AEAssessmentSession?

    func beginAssessmentMode() {
        let config = AEAssessmentConfiguration() // Configure AAC behavior

        let session = AEAssessmentSession(configuration: config) // Construct your session

        session.delegate = self // Receive lifecycle events via delegation

        assessmentSession = session // Retain the session

        // Present assessment mode bringup transition UI
        // ...

        session.begin()
    }

    func endAssessmentMode() {
        guard let session = assessmentSession else {
            return
        }

        // Present assessment mode teardown transition UI
        // ...

        session.end()
    }
}

extension AssessmentManager: AEAssessmentSessionDelegate {

    func assessmentSessionDidBegin(_ session: AEAssessmentSession) {
        // Stop showing assessment mode bringup transition UI
        // ...

        // Present sensitive testing content
        // ...
    }

    func assessmentSession(_ session: AEAssessmentSession, failedToBeginWithError error: Error) {
        // Stop showing assessment mode bringup transition UI
        // ...

        // Present some kind of error UI
        // ...

        // Release your reference to the AEAssessmentSession
        assessmentSession = nil
    }

    func assessmentSessionDidEnd(_ session: AEAssessmentSession) {
        //  Stop showing assessment mode teardown transition UI
        // ...

        // Present your post-test UI
        // (maybe a result, a confirmation, or just the initial view)
        // ...

        // Release your reference to the AEAssessmentSession
        assessmentSession = nil
    }

    func assessmentSession(_ session: AEAssessmentSession, wasInterruptedWithError error: Error) {
        // Hide all sensitive UI
        // ...

        // Present some kind of error UI
        // ...

        session.end()
    }
}

关键点:

  • import AutomaticAssessmentConfiguration 引入新的 AAC framework。
  • AEAssessmentConfiguration() 创建考试配置;transcript 明确说明默认配置最严格。
  • AEAssessmentSession(configuration:) 用配置创建会话。
  • session.delegate = self 让 manager 接收开始、失败、结束和中断回调。
  • assessmentSession = session 必须保留 session;过早释放会导致 assessment mode 结束。
  • session.begin() 只是请求进入考试模式,真正展示考试内容要等 assessmentSessionDidBegin(_:)
  • session.end() 请求退出考试模式,结果页或初始界面要等 assessmentSessionDidEnd(_:) 后再呈现。

处理开始失败和运行中断

03:03)AAC 有两个关键失败点。进入模式时,系统服务可能无法被限制,或者 App 没有 entitlement。运行考试时,某个服务也可能停止遵守 assessment mode。系统不会在运行中断时自动拆掉考试模式,而是通知 App 停止考试。

func assessmentSession(_ session: AEAssessmentSession, failedToBeginWithError error: Error)

func assessmentSession(_ session: AEAssessmentSession, wasInterruptedWithError error: Error)

关键点:

  • failedToBeginWithError 表示没有成功进入 assessment mode,session 已经失效,应该清理引用。
  • wasInterruptedWithError 表示考试已经在运行,但安全保证被破坏。
  • 中断发生后要先隐藏敏感考试内容,再保存必要进度或清理状态。
  • 清理后调用 session.end(),释放剩余系统服务。

把 AAC 隔离成可替换依赖

07:02)演讲建议把“决定进入考试模式”的业务逻辑和“真的调用 AAC”的系统副作用分开。原因很直接:AAC 会限制 Mac 上哪些 App 能接收输入。调试时如果在 Xcode 命中断点,考试 App 等断点继续,Xcode 又无法接收输入,开发机就会卡住。

protocol AssessmentManaging {
    func beginAssessmentMode()
    func endAssessmentMode()
}

关键点:

  • transcript 明确建议用 protocol-oriented programming 为 assessment mode 建边界。
  • 生产实现可以包装真实的 AEAssessmentSession
  • 测试实现可以只记录调用,或加载 test expectations。
  • 业务逻辑依赖 AssessmentManaging 后,单元测试不会真的锁住开发机。
  • 如果调试中把 Xcode 和考试 App 卡死,重启 Mac 会停止 assessment mode,系统服务会在重启后恢复。

在 iOS、iPadOS 和 Catalyst 上复用同一模型

10:12)新的 AAC framework 也覆盖 iOS 和 iPadOS,并替代原先 UIKit 中的 assessment mode API。配置对象可以开启 dictation、predictive keyboard、spell check 等系统服务,适配不同考试类型和学生需求。Catalyst App 从 iOS 14 和 macOS Big Sur 开始也获得完整 assessment mode 支持。

let config = AEAssessmentConfiguration()
let session = AEAssessmentSession(configuration: config)

关键点:

  • AEAssessmentConfiguration 是可扩展的配置值,用来描述考试允许哪些系统能力。
  • 同一组 AEAssessmentSession API 暴露在 iOS、iPadOS 和 macOS 上。
  • Catalyst App 会触发所在平台正确的 assessment mode。
  • transcript 没有给出 dictation、predictive keyboard、spell check 的具体属性名,实际接入时应以 Automatic Assessment Configuration 文档为准。

核心启发

1. 跨平台考试会话管理器

做什么:把 iPhone、iPad、Mac 和 Catalyst 的考试启动、结束、失败和中断处理收敛到一个 AssessmentManager

为什么值得做:这场 session 的重点是同一套 AEAssessmentSession API 覆盖多平台,考试 App 不需要为 Mac 单独维护一套锁定流程。

怎么开始:先用 AEAssessmentConfiguration 创建默认严格配置,再把 begin()end() 和四个 delegate 回调接到现有考试状态机。

2. 考试开始前的过渡界面

做什么:在学生点击开始后先展示“正在进入考试模式”的过渡页,等 assessmentSessionDidBegin(_:) 后再加载试题。

为什么值得做:AAC 会调整系统服务,进入过程可能耗时;提前展示试题会让敏感内容暴露在完全受限之前。

怎么开始:把试题视图挂在 DidBegin 回调之后,把失败 UI 挂在 failedToBeginWithError 回调之后。

3. 中断后的安全收尾

做什么:建立一个专门的中断路径:隐藏试题、保存进度、提示用户、调用 end() 退出 assessment mode。

为什么值得做:系统在运行中断时只通知 App,不会直接拆掉 assessment mode;App 必须主动停止考试并释放系统服务。

怎么开始:在 wasInterruptedWithError 中先切换到无敏感内容的界面,再执行本地保存和退出流程。

4. 可测试的 AAC 适配层

做什么:定义 AssessmentManaging 协议,用真实 AAC 实现跑生产,用测试实现跑单元测试和本地调试。

为什么值得做:Mac assessment mode 会限制输入,断点调试可能让 Xcode 无法继续操作;协议边界能让业务逻辑不直接依赖系统锁定副作用。

怎么开始:把现有考试流程依赖改成协议类型,在测试 target 注入只记录 begin/end 调用的 fake manager。


关联 Session

评论

GitHub Issues · utterances