大多数浏览器和
Developer App 均支持流媒体播放。
-
ClassKit 的创新
ClassKit 框架可帮助你通过课业 app 向教师展示你的app 中的教育活动。 探索如何通过增强后的元数据属性和进度报告为学生和教师提供更丰富的作业体验。我们还将向你展示新开发的 ClassKit Catalog API 如何将内容管理与 app 管理分离开来,并提高整体的可视性。
资源
相关视频
WWDC20
-
下载
(你好 WWDC 2020)
你好 欢迎来到 WWDC (ClassKit 的创新) 你好 我是 Baskaran 是 Apple 教育团队的一名工程师 今天 我和我的同事 Daniel 想给你介绍 ClassKit 的最新变化 首先 我会向你展示 我们对 ClassKit 所做的优化 这些优化让你可以提供丰富的细节 这些细节与你的 app 的内容相关
然后我将介绍一些推荐的最佳做法 来采用 ClassKit 之后 Daniel 会向你介绍 一种新方式来报告你的内容 这种方式使用 名为 ClassKit Catalog API 的服务 最后 他会介绍一些推荐的最佳做法 来采用 ClassKit Catalog API 让我先简单介绍一下 ClassKit 它是 Apple 教育生态系统的核心组件
它是一个框架 它让你能够 在你的 app 中将教育内容呈现为活动 教师可以使用活动给他们的学生布置作业 ClassKit 让你的 app 能够报告进度 它会报告学生的作业完成进度 最重要的是 它在报告进度的同时 还保护了学生的隐私 它只报告所分配活动的进度 例如 如果一位教师布置学生 阅读《Fun with Science》中某一章 而一个学生还阅读了 《Basic Arithmetic》中的一章 该学生和教师看到的进度 只是《Fun with Science》上的进度 因为这个是布置的阅读 ClassKit 于 2018 年推出 现在 它也可以在 macOS 上使用 用于创建本机和 Catalyst app
关于 ClassKit 的更多信息 请查看我们 2019 年的视频 “ClassKit 的创新” 以及我们 2018 年的视频 “ClassKit 的介绍”
在我介绍优化内容之前 让我们快速查看一下你的 app 中的内容 是如何在“课业”中 以可分配活动的形式呈现的 你的 app 会使用 ClassKit 定义内容 并将其描述为活动 这可以使用“课业”进行浏览 有了“课业” 你可以轻松地分享课堂材料 让学生在 app 中进行特定的活动 与学生协作并查看学生的进度 当你打开“课业”时 你会进入这一视图 这是教师分配给学生的所有内容的主基地
你轻点右上角的 New Handout 按钮 以创建一个新讲义 让我放大以使其更易于看清 现在 轻点 App Activities 以显示 app 活动选择器
app 活动选择器显示 启用 ClassKit 的 app 该 app 带有 App Activities 在这里 我们看到三个有活动的 app 我将以 Book Reader app 为例进行讲解 让我们来看看 Book Reader 中的活动 我们看到一本书《Fun with Science》 其中有一些活动 让我们浏览 《Fun with Science》中的活动 在《Fun with Science》中有几个活动 这些活动是按章组织的 让我们看看题为“Inquiry”的第一章
这章有一个活动 Science Investigation 让我们一起看下这个活动 在 Science Investigation 本节的末尾 我们看到一个测验 顺便问一下 你注意到了 活动所显示的丰富细节吗?
我们在这里看到一个缩略图 一个描述活动的摘要 建议年龄和建议完成时间 以及 app 报告的活动进度类型 这些细节可帮助教师决定 他们是否要在布置作业时使用这一活动 现在 让我向你展示 我们可以如何将这些细节添加至 我们的 app 的活动中 在这样做之前 让我们一起简单了解下 App Activities
一个 app 活动是指 你的 app 中分配的任何内容 这可以是书的一个篇章、播客中的媒体流 一场考试、一次测验等等
它们由你的 app 定义 并且结构是分层的
让我们来看看我们的示例 我们在一本名为《Fun with Science》 的书中看到了活动的一个层次结构
层次结构的顶层是 《Fun with Science》这本书 书的子级是这本书的章 章的子级是节 以此类推 你可能在想 我们是如何 在“课业”中呈现这些活动的 我们在 ClassKit 中使用 CLSContext 类 并创建 CLSContext 的一个实例 来表示每个活动 ClassKit 为你的 app 提供根上下文 叫做 Main App Context 这是活动层次结构的父级 获取 Main App Context
为最顶层的活动《Fun with Science》 创建一个 CLSContext 将其添加至 Main App Context 的子级
然后沿着活动层次结构往下
为每个活动创建一个上下文 并将其添加至其父级的上下文中 现在 我会以测量测验活动为例 并向你展示如何向该活动添加丰富的细节 和我们之前看到的一样 此处有几个细节 让我一步一步地向你介绍 并向你展示如何将这些细节 添加到测验内容中 那么 首先让我向你展示 如何在测验上下文中添加缩略图和摘要 首先 为该活动创建一个上下文 在本例中 它是一个测量测验 然后在上下文中添加摘要 你说明一下这个测验的内容 然后添加一个缩略图 记住 ClassKit 将缩略图的大小 限制为 330x330 像素 它会根据需要缩小尺寸 在本例中 我们从图像资源中创建一个缩略图 measurements_quiz.jpg 注意 我们使用了一个函数 thumbnailFromImage 以从文件 URL 获取一个缩略图 让我们来看看这个函数
该函数向你展示如何从 URL 创建缩略图 使用 CoreGraphics 以一种节省内存的方式进行创建 首先从文件创建一个图像源
然后设置一些选项来创建缩略图 我们选择总是从一个图像创建缩略图 并且图像的最大尺寸为 300 像素
然后创建缩略图并返回 这只是创建缩略图的一种方法 如果对你来说更容易获得 UI-Image 你可以获取它的 CGImage 属性 并将其作为缩略图使用 但请记住 这样做 可能会显著增加 app 的内存使用量 好的 让我们为测验上下文添加更多的属性 让我们添加建议年龄和建议完成时间 添加这些属性非常简单 它们都表示为范围 让我们把建议年龄设为 9 至 11 岁 建议完成时间设为 15 至 20 分钟 接下来 我们将添加测验活动 报告的进度数据类型 在本例中 我们看到报告了四种进度数据 进度百分比 每道题正确/错误分数
学生在做测验时使用的提示数量 以及一个总成绩 我将向你展示 如何将这些内容添加到测验内容中 首先 创建一个报告进度百分比的功能
CLSProgressReportingCapability 类 有两种属性 “类型”和“细节”
你将类型设为百分比类型 将细节设为描述性文本 接下来创建一个功能 来报告使用提示的数量 在做测验时所使用的提示 你可以看到 创建每种报告功能是多么地简单 最后 我们将这些功能 添加到测验上下文中 在本例中 我将功能细节设置为英文 但这些文本字符串对用户是可见的 所以请在运行时 根据当前语言环境对它们进行本地化 请记住 这类似于 你在设置活动的用户可见标题时做的事情 好的 现在你知道如何将新属性 添加到你的 app 活动中了 这就是你丰富你的 app 活动的方法 现在我想要介绍一些推荐的最佳做法 来为你的 app 创建和管理上下文
CLSContext 上新引进的属性 添加了有价值的元数据 这些元数据可以让教师了解 你的 app 中丰富的活动集
因此 当你创建上下文时 请你使用你可以提供的尽可能多的元数据 创建一个功能齐全的上下文
如果你发现旧版本的 app 保存了上下文 请更新所有丢失的属性
如果你的 app 不再支持旧的上下文 请删除该上下文 这样它不会显示在 你的 app 支持的活动列表中
如果你的 app 使用上下文提供者 app 扩展来创建上下文 也请更新那个应用程序扩展
我差不多要讲完了 Daniel 迫不及待地想告诉你 关于全新 ClassKit Catalog API 的 所有信息 然而 我想告诉你 我们又做出了一些优化 我们已将其添加到 CLSContext 中 这也是你们很多人所要求的
比如只读属性 identifierPath 这将返回上下文的完整 identifierPath 一个新的上下文类型“course” 和一个新的上下文类型“custom” 请使用此属性 如果所有的预定类型 都不适合你的 app 的内容 还有一个可选的可设置属性 用于为你的自定义上下文命名 你的新属性可以将上下文标记为 一个不可分配的活动 让我们看看这个属性在哪里很有用 在我们的示例中 我们为一本书创建了上下文 你看到整本书《Fun with Science》 是一个可分配的活动 由加号表示 但我认为你的教师不会布置整本书 实际上 作为一个开发者 你不打算让一本书成为一项可分配的活动 你只是将 CLSContext 用作本书的容器 来保存其章节
现在 通过将上下文的 新 isAssignable 属性设置为“false” 你可以明确表示 这本书不是一项可分配的活动 我鼓励你回顾更新的开发者说明文档 以了解我们对 ClassKit 所做的所有优化 这些是对 ClassKit 的最新优化
接下来 让我邀请 Daniel 向你介绍令人兴奋的新的 ClassKit Catalog API 到你了 Daniel 谢谢 Baskaran 今天 我想介绍一个新的网络服务 叫做 ClassKit Catalog API 我们提供这项服务的动机 是为了将一个更好的体验提供给 我们构建启用 ClassKit app 的开发人员 以及使用这些 app 的教师 我们构建此服务的目的是帮助你 考虑到一些事情 首先 我们想提高你的内容 在“课业”中的可发现性 你的 app 用户应该看到丰富的内容库 该内容库是你提供给他们的 他们不必先完成每一个部分
接下来 我们想为你提供支持 提供更加广泛的 启用 ClassKit 的内容数组 相比其他方法能提供的而言 使用本机方式创建 CLSContext 实现这点
最后 我们想要将你的内容管理 从你的 app 管理中解耦出来 你的内容可能会发生更改 你应该能够在 不改变你的 app 的情况下进行这些更改 我们如何实现这点呢? 目前 CLSContexts 是在你的 app 中本地定义的 当教师使用它时 它会委托 ClassKit 生成必要的上下文 这些上下文会保存在教师的设备上 最终也会保存在学生的设备上
ClassKit Catalog API 规定了 一种不同的报告内容的方法
要使用这个新服务 请先将现有的 CLSContext 层次结构 转换为 JSON 结构 接下来 获取有效负载 并使用 ClassKit Catalog API 将其上传
然后 使用你提供的数据 我们将在教师或学生的设备上 处理 CLSContext 的生成 只要“课业”引用了你的 app 在我继续介绍之前 请务必注意 ClassKit Catalog API 用于你想要在“课业”中 公开显示的内容 私人内容 比如动态生成的内容 或特定于用户的 CLSContext 应该继续使用本机 API 向 ClassKit 报告 我们提供了一个新地址 你可以向它提出请求 api.classkit-catalog.apple.com
它的后缀是“v1” 以便对我们的 API 进行版本化 从而支持未来可能引进的任何更改
API 本身由两个主要组件组成 一个用于管理上下文 一个用于管理缩略图
上下文是基本单元 它用于描述你的内容 以及用于目录的运行
你可以将它们看成 CLSContext Baskaran 在前面介绍过的 但是它有更多的附加功能 稍后我们将进一步来了解它们
缩略图是与你的上下文相关联的图像 在本次演示的第一部分 我们已经看到了 如何使用 ClassKit 在本机实现这一点 好的 有了 API 我们将使用缩略图端点 我们在后面也会看到这点 今天 我们将重点介绍 POST 请求 让我们先来看看上下文
下面是一个 POST 请求的示例 其中包含你需要提供的必填字段 首先是一个标注为“环境”的查询参数 它决定了你所做的更改 是针对开发环境还是生产环境的
稍后我们将看到如何利用这一点 接下来 需要两个标头 Content-Type 与 Authorization
Content-Type 描述 你将要交付的有效负载类型 对于此端点 它应该是 application/json
Authorization 描述的是凭证 该凭证用于在我们服务中进行身份验证
对于 ClassKit Catalog API 我们决定采用 JSON Web Token 这是一种行业标准的身份验证方法 Apple 的几个网络服务都使用该方法
我们在后面的演示中 将详细介绍如何生成这些令牌
这个请求的主体 应该包含一个要创建或要更新的 JSON 格式的上下文列表 这些上下文表示 你的 app 中的 CLSContext 为了让你的 app 的功能 可以得到正确的表示 与你启用了 ClassKit 的 app 相关联的整个上下文树 都必须进行报告
单个上下文包含两个字段 数据和元数据 让我们详细了解下 这两个字段是如何构造的 数据对象由直接从 CLSContext 获取的 字段组成 包括 ClassKit 最近更新的新字段 Baskaran 前面已经提到过
当你创建此数据对象时 请使用 CLSContext 中的字段值 你的 app 已经创建了该字段值 确保它们与你的 app 本机创建的 上下文相匹配 以便可以在目录中正确表示你的内容
元数据字段用于捕获信息 该信息在 CLSContext 中没有表示出来 它包含三个字段 语言环境 用于区分你的 app 在不同语言 和不同区域之间的内容 最小捆绑版本 它作为客户端的命中点 以确定你的 app 版本 是否支持这个上下文 以及关键字 它们表示你认为的标签 能够准确描述你的内容的标签 元数据是一个稀疏对象 但我们希望它能随着 API 的发展而发展
这是对上下文 POST 请求的一个示例
关于此端点的更多详细信息 以及上下文的 GET 与 DELETE 方法 都将在 Apple 开发者说明文档中提供
现在 让我重点介绍一些 关于上下文端点的指导原则 首先 每个 POST 请求 有 200 个上下文的限制 如果你有更多的请求 请将它们拆分为多个请求
如果你要这样做 请确保子上下文 在其祖先上下文之后提交 除了你的主 app 上下文之外 子上下文被认为是无效的 如果它们的父上下文不存在的话 最后 如果你决定删除一个上下文 请注意 它的所有后代都会被删除 这样做是为了确保 你的 app 上下文层次结构的完整性 继续 让我们看看上传缩略图的 POST 方法
下面是一个示例 其中包含请求中要求的字段
它以一个缩略图引用开始 该引用用于将缩略图关联到 一个或多个上下文中
这是通过匹配上下文对象上的 缩略图字段来实现的
该请求剩下的部分 应该类似于我们看到的第一个请求 例如 我们必须也针对一个环境 使用“环境”查询参数
使用 Authorization 标头提供凭证 以及一个 Content-Type 以描述正在上传的图像
与我们之前的 POST 请求不同 我们添加了一个 Contact-Length 以字节为单位描述要上传的缩略图的大小 以及一个主体 它由表示图像的字节组成
在创建 Handout 流程中 该图像用于表示一个活动 因此当你决定如何最好地表示你的内容时 请记住这一点 这是一个缩略图的 POST 端点的样子 对应的 GET 和 DELETE 端点结构也类似 更多细节可以在 Apple 开发者说明文档中找到 在我继续之前 让我强调一些指导方针 关于缩略图端点的指导方针 首先 使用 PNG 或 JPEG 来表示缩略图
接下来 格式化你的缩略图 为 330×330 像素 这就是我们将用于“课业”的 解决方法
最后 先上传你的上下文 再上传你的缩略图 这是为了防止缩略图被遗弃 上传请求会受到拒绝 如果没有任何上下文引用该缩略图的话
这也意味着如果最后一个 引用缩略图的上下文被删除 那么缩略图也将被删除 在我继续之前 让我们谈谈当出现错误时 API 会如何回应 下面是一个响应示例 你可能会从 试图 POST 一个报告错误的缩略图中 得到该响应 除了要求一个 HTTP 状态代码外 报告错误的响应 也应该包含一个 JSON 主体 以提供该问题的更多细节 ID 字段提供唯一的标识符 我们可以用它来识别和追踪这个错误 我们可以从代码字段看出 身份验证存在问题
在检查信息字段时 我们发现有一个人工可读的问题说明 以及解决该问题的方法 一个完整的错误代码列表 和它们各自的信息 将在 Apple 开发者说明文档中提供 至于这个身份验证错误 我们如何克服这个错误呢?
按照说好的 让我们来谈谈身份验证 我们前面提到 ClassKit Catalog API 中的身份验证 是使用 JSON Web Tokens 或 JWT 处理的 这是一个行业标准 Apple 的多个网络服务都使用该标准
首先 登录到 Apple Developer 门户网站 并为你的团队提供 ClassKit Catalog API 密钥
这个私人密钥将用于创建 JWT JWT 用于使用 ClassKit Catalog API 进行身份验证 并且必须随你提出的所有请求一起提供 作为我们前面提到的 Authorization 标头的一部分
JWT 由三部分组成 标头、有效负载和签名
要形成标头和有效负载 请提供密钥 ID 和团队 ID 在 Apple 开发者门户网站上找到的 ID
这将把凭证与你的开发者团队关联起来 另外 生成一个 UUID 来标识这个令牌 和两个 UNIX 时间标签 一个表示令牌的发行时间 一个表示令牌的到期时间 我们在这里看到的是原始字段 要形成最终的令牌 这些字段必须采用 Base64 编码 要完成 JWT 还需要一个签名 签名是使用编码的标头 和编码的有效负载生成的 用来签名的算法是 ECDSASHA256 有许多编程语言的公共库 它们应该可以很容易地实现这一点
总之 完全成形的 JWT 看起来是这样的
此令牌必须包含在 Authorization 标头中 并以“Bearer”一词作为前缀
既然我们已经准备好所有内容 你将如何测试已上传的内容 然后再将其显示给你的用户呢? 我们有两种环境可以让你推送你的内容 开发和生产
要测试你的更改 请使用“环境”查询参数 上传你的内容至开发中 就像我们之前展示的那样 完成此操作后 打开 Settings 并导航至你设备中的 Developer 标签 iOS 开发构建正在该设备上运行 轻点 ClassKit API 以查看更多选项 在 ClassKit Catalog 环境下 选择 Development 环境
设置完成后 ClassKit 就会更改 其获取数据的位置 并在“课业”中显示适当的内容 这些都是基于你所选择的环境进行的
请注意 推送到开发中的内容 将提供给所有 ClassKit Apple 开发者 他们也正在使用 Development 环境
总结一下 我想强调一些最佳做法 这些是使用 API 时要记住的最佳做法
首先也是最重要的是 当你定义新内容或删除旧内容时 请使用 ClassKit Catalog API 报告此内容 这可以确保你的 app 及其 Activities 在“课业”中都是正确表示的 接下来 利用为上下文提供的 语言环境支持
这样做有助于尽可能地 扩大你的 app 的受众范围
最后 你要在你报告的上下文中 提供尽可能多的细节 并且将缩略图与它们关联起来 这将在“课业”中 为用户提供更多的细节 以及一个强而有力的理由 让他们从你的 app 中分配内容 这就是 ClassKit Catalog API 的 全部内容 它将在今年夏天提供给 ClassKit 开发者 我们关于“ClassKit 的创新”的 介绍也到此结束 谢谢
-
-
6:25 - Add Thumbnail and Summary to Quiz Context
// Create a context for quiz let quizContext = CLSContext.init(type: CLSContextType.quiz, identifier: "science_Investigation_quiz", title: "Measurements Quiz") // Add a summary describing this context quizContext.summary = "A short quiz to test how much students know about scientific measurements and how to examine and analyze scientific data." // Add a thumbnail for this context — ClassKit will downsize thumbnails to 330 x 330 px if needed let bundle = Bundle.main if let resourceURL = bundle.resourceURL { let imageURL = resourceURL.appendingPathComponent("measurements_quiz.jpg") if let thumbnail = thumbnailFromImage(atURL: imageURL) { quizContext.thumbnail = thumbnail } }
-
6:52 - thumbnailFromImage
// Create a thumbnail of maximum dimension 330 x 330 px from an image file func thumbnailFromImage(atURL: URL) -> CGImage? { if let imageSource = CGImageSourceCreateWithURL(atURL as CFURL, nil) { let thumbnailOptions = [kCGImageSourceCreateThumbnailFromImageAlways as String: true, kCGImageSourceThumbnailMaxPixelSize as String: 330] return CGImageSourceCreateThumbnailAtIndex( imageSource, 0, thumbnailOptions as CFDictionary); } return nil }
-
7:59 - Add suggestedAge and suggestedCompletionTime
// Add suggested age range appropriate for the content — ages 9 to 11 years quizContext.suggestedAge = NSRange(9...11) // Add suggested time to complete this quiz — 15 to 20 minutes quizContext.suggestedCompletionTime = NSRange(15...20)
-
8:15 - Add progress reporting capabilities
// Add progress reporting capabilities let reportingPercentDetails = "Reports percentage of progress" let reportingCapabilityPercent = CLSProgressReportingCapability.init( kind: .percent, details: reportingPercentDetails) let reportingQuantityDetails = "Reports number of hints used" let reportingCapabilityQuantity = CLSProgressReportingCapability.init( kind: .quantity, details: reportingQuantityDetails) quizContext.addProgressReportingCapabilities([reportingCapabilityPercent, reportingCapabilityQuantity])
-
-
正在查找特定内容?在上方输入一个主题,就能直接跳转到相应的精彩内容。
提交你查询的内容时出现错误。请检查互联网连接,然后再试一次。