WWDC Quick Look 💓 By SwiftGGTeam
Meet ClassKit for file-based apps

Meet ClassKit for file-based apps

观看原视频

Highlight

Apple 为 ClassKit 增加文件级进度 API,文件型教育应用可以用文件 URL 获取 CLSActivity,向 Schoolwork 报告学习时长、进度和文件相关指标。

核心内容

很多教育应用的核心对象是文件。

学生打开一份作文、编辑一个演示文稿、观看一个音频文件。教师关心的是这份文件上的学习过程:学生花了多久,完成到哪里,最终产生了多少内容。

以前接入 ClassKit,应用通常要先发布 CLSContext。它适合有固定内容结构的应用,例如课程、章节、练习题。文件型应用的问题在这里:内容来自用户或教师的文件,应用无法提前为每份文件建好内容树。

Apple 在 WWDC 2021 给 ClassKit 加了文件级 API。应用拿到文件 URL 后,调用 CLSDataStore.shared.fetchActivity(for:) 获取这份文件对应的 CLSActivity。学生开始编辑时调用 start(),结束时写入指标并调用 stop()

这个设计把接入点放回文件生命周期。你的应用仍然按原来的方式打开和关闭文件,只在已有流程里多加几行 ClassKit 代码。教师在 Schoolwork 里分配文件,学生打开同一份文件,Schoolwork 就能看到应用上报的时长、进度和主要指标。

前提:应用要支持 Open in Place

文件级进度依赖同一份文件。

Apple 明确要求应用采用 Open in Place(原地打开)。这样,教师分配的文件会在学生设备上以原文件形式打开,应用处理的是共享文件本身。ClassKit 才能用文件 URL 把 Schoolwork 作业和应用里的活动关联起来。

数据流:应用、ClassKit、Schoolwork

Schoolwork 是教师创建和查看作业的地方。应用负责把学习数据交给 ClassKit。ClassKit 再把这些数据呈现给学生和教师。

这场 session 的示例是文本编辑器。学生打开教师分配的文本文件时,应用开始计时。学生点击 Done 前,应用记录总字数、进度,并停止计时。回到 Schoolwork 后,学生能看到自己的时长和字数。教师视图里可以看到全班平均时长、平均字数,以及每个学生的详情。

详细内容

用文件 URL 获取学习活动

03:22)文件级 API 叫 fetchActivity(for:),位于 CLSDataStore。调用时传入文件 URL,返回 CLSActivity。这个活动对象承载这份文件的进度数据。

func openFile() async throws {
     // Your existing code for opening a file goes here.
     let activity = try await CLSDataStore.shared.fetchActivity(for: fileURL)
     activity.start()
     try await CLSDataStore.shared.save()
}

关键点:

  • openFile() 放在应用已有的打开文件流程里,session 的示例在学生开始编辑文件时调用。
  • fileURL 是当前打开文件的 URL,ClassKit 用它找到这份文件对应的活动。
  • CLSDataStore.shared.fetchActivity(for: fileURL) 获取 CLSActivity
  • activity.start() 开始记录学生在这份文件上的学习时长。
  • CLSDataStore.shared.save() 提交本次修改;session 明确说明,不调用 save() 就不会持久化刚写入的数据。

关闭文件时写入主要指标和进度

08:07)学生完成编辑准备关闭文件时,应用再次获取同一个 CLSActivity,写入主要指标、进度,并停止计时。

func closeFile() async throws {
     let activity = try await CLSDataStore.shared.fetchActivity(for: fileURL)
     let wordCount = activity.primaryActivityItem as? CLSQuantityItem ??
         CLSQuantityItem(identifier: "total_word_count", title: "Word Count")
     wordCount.quantity = currentDocumentWordCount()
     activity.primaryActivityItem = wordCount
     activity.progress = progress()
     activity.stop()
     try await CLSDataStore.shared.save()
}

关键点:

  • fetchActivity(for: fileURL) 让关闭流程拿到同一份文件的活动对象。
  • activity.primaryActivityItem as? CLSQuantityItem 尝试复用已有的主要指标。
  • CLSQuantityItem(identifier:title:) 在没有已有指标时创建一个数量型指标;示例用它记录总字数。
  • wordCount.quantity = currentDocumentWordCount() 把当前文档字数写入指标。
  • activity.primaryActivityItem = wordCount 把字数设为 Schoolwork 主界面展示的指标。
  • activity.progress = progress() 写入 0 到 1 之间的完成进度。
  • activity.stop() 停止时长记录。
  • save() 保存字数、进度和停止计时这几项变更。

选择合适的进度数据类型

04:01CLSActivity 可以保存多种进度数据。文件型 API 复用 ClassKit 已有的数据模型。

let activity = try await CLSDataStore.shared.fetchActivity(for: fileURL)

activity.start()
activity.progress = 0.5

let wordCount = CLSQuantityItem(identifier: "total_word_count", title: "Word Count")
wordCount.quantity = currentDocumentWordCount()
activity.primaryActivityItem = wordCount

activity.stop()
try await CLSDataStore.shared.save()

关键点:

  • activity.start()activity.stop() 用来报告时长。Apple 建议所有文件类型都报告时间。
  • activity.progress = 0.5 表示学生完成了 50%。session 用音频或视频播放到一半作为例子。
  • CLSQuantityItem 表示通用数值,例如页数、幻灯片数量、文档总字数。
  • primaryActivityItem 是主指标,会出现在 Schoolwork 的主要 UI 位置。
  • save() 把这些活动数据提交给 ClassKit。

05:53primaryActivityItemadditionalActivityItems 都保存 CLSActivityItem 的子类。session 列出三种可用类型。

let correct = CLSBinaryItem(identifier: "question_correct", title: "Correct")
let wordCount = CLSQuantityItem(identifier: "total_word_count", title: "Word Count")
let quizScore = CLSScoreItem(identifier: "quiz_score", title: "Quiz Score")

关键点:

  • CLSBinaryItem 表示二元结果。session 的例子是测验题答对或答错。
  • CLSQuantityItem 表示普通数值。session 的例子包括页数、幻灯片数量、总字数。
  • CLSScoreItem 表示得分与总分。session 的例子是测验得到 8/10。
  • 一个活动可以添加其中一种,也可以组合多种指标。

用附加指标补充文件元数据

05:24additionalActivityItems 适合放补充指标。session 后面的 Schoolwork 教师视图展示了一个附加指标:Readability Grade Level(可读性年级水平)。

let readability = CLSQuantityItem(identifier: "readability_grade_level", title: "Readability Grade Level")
readability.quantity = readabilityGradeLevel()
activity.addAdditionalActivityItem(readability)
try await CLSDataStore.shared.save()

关键点:

  • CLSQuantityItem 可以表示可读性年级水平这类数值。
  • readability.quantity 保存应用自己计算出的指标值。
  • activity.addAdditionalActivityItem(readability) 把该指标加入附加指标列表。
  • save() 保存附加指标,教师在 Schoolwork 详情里可以看到它。

在开发者模式里测试

09:05)测试流程分为两步:先让应用使用 ClassKit development 环境,再在 iPad 的开发者设置中切换 Schoolwork 角色。

func openFile() async throws {
     // Your existing code for opening a file goes here.
     let activity = try await CLSDataStore.shared.fetchActivity(for: fileURL)
     activity.start()
     try await CLSDataStore.shared.save()
}

func closeFile() async throws {
     let activity = try await CLSDataStore.shared.fetchActivity(for: fileURL)
     let wordCount = activity.primaryActivityItem as? CLSQuantityItem ??
         CLSQuantityItem(identifier: "total_word_count", title: "Word Count")
     wordCount.quantity = currentDocumentWordCount()
     activity.primaryActivityItem = wordCount
     activity.progress = progress()
     activity.stop()
     try await CLSDataStore.shared.save()
}

关键点:

  • 在 Xcode 中把 ClassKit environment entitlement 从 production 改成 development,下一次运行会进入开发者模式。
  • 在 iPad 设置里进入 Developer > ClassKit API,选择 Teacher,打开 Schoolwork 创建作业并添加应用支持的文件。
  • 再回到 Developer > ClassKit API,切换成 Student,打开 Schoolwork 作业里的文件,触发应用的 openFile()
  • openFile() 调用 save() 后,示例里出现 Student Progress 横幅,表示计时提交成功。
  • 学生点击 Done 触发 closeFile() 后,Schoolwork 会显示时长和主指标;session 示例看到 41 分钟和 558 words。
  • 测试结束后,把 entitlement 改回 production。

核心启发

1. 写作应用的课堂作业模式

  • 做什么:教师分配一份作文模板,学生在应用里编辑,Schoolwork 显示写作时长、字数和完成进度。
  • 为什么值得做:文件级 API 让应用直接围绕教师分配的文件报告数据,CLSQuantityItem 可以承载字数。
  • 怎么开始:在打开文档时调用 fetchActivity(for:)start();在学生点击 Done 时写入 CLSQuantityItem(identifier: "total_word_count", title: "Word Count"),设置 primaryActivityItem,再调用 stop()save()

2. 音频听力练习的播放进度

  • 做什么:学生打开教师分配的音频文件,应用把播放完成度同步到 Schoolwork。
  • 为什么值得做:session 明确把音频或视频播放到 50% 作为 progress = 0.5 的例子。
  • 怎么开始:用文件 URL 获取 CLSActivity,播放开始时 start(),暂停或结束时根据播放位置设置 activity.progress,最后保存。

3. 演示文稿制作作业

  • 做什么:学生编辑演示文稿文件,教师查看学生完成的幻灯片数量和花费时间。
  • 为什么值得做CLSQuantityItem 适合报告 number of slides,Schoolwork 可以把它作为主要指标显示。
  • 怎么开始:在关闭文件时统计当前幻灯片数量,写入 CLSQuantityItem(identifier:title:),赋值给 activity.primaryActivityItem

4. 测验文件的得分回传

  • 做什么:教师分配一个测验文件,学生完成后,应用回传得分和单题正确状态。
  • 为什么值得做CLSScoreItem 适合表示 8/10 这类分数,CLSBinaryItem 适合表示题目正确或错误。
  • 怎么开始:完成测验时获取活动,创建 CLSScoreItem 作为主指标;把每题状态做成 CLSBinaryItem,用 addAdditionalActivityItem 添加。

5. 阅读材料的可读性反馈

  • 做什么:学生编辑或阅读文件后,应用向教师显示可读性年级水平等补充指标。
  • 为什么值得做:session 的 Schoolwork 教师视图展示了 Readability Grade Level 作为附加指标。
  • 怎么开始:在应用内部计算可读性数值,创建 CLSQuantityItem,用 addAdditionalActivityItem 加到 CLSActivity

关联 Session

评论

GitHub Issues · utterances