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:01)CLSActivity 可以保存多种进度数据。文件型 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:53)primaryActivityItem 和 additionalActivityItems 都保存 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:24)additionalActivityItems 适合放补充指标。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
- What’s new in managing Apple devices — 介绍组织环境中的 Apple 设备管理更新,适合教育和企业部署场景。
- Meet declarative device management — 讲解声明式设备管理如何减少服务器轮询,让设备主动报告状态。
- Discover account-driven User Enrollment — 介绍学校和企业常见的自带设备注册流程。
- Improve MDM assignment of Apps and Books — 讲解组织如何通过 API 分配应用和图书,和教育部署场景相关。
评论
GitHub Issues · utterances