The ClassKit framework helps you surface educational activities within your app to teachers through the Schoolwork app. Discover how to provide a richer assignment experience for students and teachers through enhanced metadata properties and progress reporting. We'll also show you how the new ClassKit Catalog APIs decouple management of your content from that of your app and improve overall discoverability.
Hello, I'm Baskaran. I'm an engineer on the Apple Education team. Today, my coworker Daniel and I would like to update you on the latest changes to ClassKit. First, I'll show you enhancements we have made to ClassKit, enhancements that will enable you to provide rich details about content in your apps.
Then I will go over some recommended best practices for adopting ClassKit.
After that, Daniel will introduce you to a new way to report your content using a service called ClassKit Catalog API.
And finally, he will go over some recommended best practices for adopting ClassKit Catalog API.
Let me begin with a brief overview of ClassKit. It's a core component of Apple education ecosystem.
It's a framework that enables you to surface educational content in your apps as activities that teachers can use in assignments for their students. ClassKit enables your app to report progress as students work on their assignments. And most importantly, it does this while protecting the privacy of the students by only reporting progress on assigned activities. For example, if a teacher assigns students to read a chapter from Fun with Science and a student also reads a chapter from Basic Arithmetic, the student and the teacher will see progress only on Fun with Science because that was assigned reading. ClassKit was introduced in 2018. Now it's also available on macOS for creating native and Catalyst apps.
For more information on ClassKit, check out our 2019 session "What's New in ClassKit" and our 2018 session "Introducing ClassKit." Before I go into the enhancements, let's take a quick look at how content in your app appears as assignable activities in Schoolwork. Your app defines the content and describes it as activities using ClassKit, which can be browsed using Schoolwork.
With Schoolwork, it's easy to share class materials, get students to a specific activity in an app, collaborate with students and view student progress.
When you open Schoolwork, you'll land on this view which serves as home base for all content teachers assign to their students.
You tap on the New Handout button on the top right to create a new handout.
Let me zoom in to make it easier to see.
Now, tap on the App Activities to show the app activity chooser.
The app activity chooser shows ClassKit-enabled apps with App Activities. Here, we see three apps with activities. I'm going to use the Book Reader app as an example for this presentation. Let's look at the activities within Book Reader.
We see a book, Fun with Science, with activities.
Let's browse the activities in Fun with Science.
There are several activities in Fun with Science organized by chapters. Let's see what's in the first chapter titled "Inquiry." This chapter has one activity, Science Investigation. Let's look at that activity.
At the end of the section on Science Investigation, we see a quiz.
By the way, did you notice the rich details shown for the activities? We see here a thumbnail, a summary describing the activity, suggested age and suggested completion time and the types of progress reported by the app for the activity. These details help teachers to decide if they want to use this activity in an assignment or not. Now let me show you how we can add these details to activities in our app. Before we do that, let's take a brief look at App Activities.
An app activity is any assigned content in your app. This could be a chapter in a book, a media stream in a podcast, a test, a quiz and so on.
They are defined by your app and are structured hierarchically.
Let's look at our example. We saw a hierarchy of activities in a book titled Fun with Science.
At the top level of that hierarchy is the book Fun with Science. The children of the book are the chapters in the book, and their children are the sections in the chapters and so on. You may be wondering how we surface these activities in Schoolwork. We use the CLSContext class in ClassKit and create an instance of CLSContext to represent each activity. ClassKit provides a root context for your app called the Main App Context. This is the parent of your activity hierarchy.
Get the Main App Context...
create a CLSContext for the topmost activity Fun with Science and add it as a child of the Main App Context.
Then go down the activity hierarchy...
create a context for each activity and add it to its parent's context. Now I'm going to use the measurements quiz activity as an example and show you how to add rich details to that activity. As we saw earlier, there are several details here.
Let me walk you through, in steps, and show you how to add these details to the quiz context. So, first, let me show you how you can add a thumbnail and summary to the quiz context. First, create a context for the activity. In this case, it is a measurements quiz. Then add a summary to the context. You say what the quiz is about. Then add a thumbnail. Remember, ClassKit limits the size of thumbnails to 330 by 330 pixels.
It will downsize if needed. In this example, we are creating a thumbnail from an image asset, measurements_quiz.jpg. Notice that we are using a function, thumbnailFromImage, to get a thumbnail from a file URL. Let's take a look at this function.
This function shows you how to create a thumbnail from a file URL in a memory-efficient way using CoreGraphics.
First create an image source from the file...
then set some options for creating a thumbnail.
We are choosing to always create a thumbnail from an image with a maximum size of 330 pixels.
Then create the thumbnail and return it. This is just one way of creating a thumbnail.
If UIImage is more readily available to you, you can get its CGImage property and use it as thumbnail. But keep in mind, doing it that way may significantly increase the memory used by your app. Okay. Let's add some more properties to the quiz context. Let's add suggested age and suggested completion time.
It is very simple to add these properties. They are both expressed as ranges. Let's set the suggested age as nine to 11 years and the suggested completion time as 15 to 20 minutes. Next we will add the type of progress data reported for the quiz activity.
In this example, we see four kinds of progress data being reported-- percentage of progress, correct/incorrect score for each question...
the number of hints used by the student when taking the quiz and an overall score. I'm going to show you how to add a couple of these to the quiz context. First, create a capability for reporting progress percent.
The CLSProgressReportingCapability class has two properties-- "kind" and "details." You set the kind to percent kind and set the details to a descriptive text. Next create a capability to report the number of hints used when taking the quiz. You can see how simple it is to create each reporting capability. Finally, we add these capabilities to the quiz context. In this example, I have set the capability details in English, but these text strings are visible to users, so please localize them at run time based on the current locale. Remember, this is similar to what you have been doing when setting the user visible title of the activity. Okay. Now you know how to add the new properties to your app activities.
This is how you make your app activities rich. Now I would like to go over some recommended best practices for creating and managing context for your app.
The newly introduced properties on CLSContext adds valuable metadata that informs the teacher about the rich set of activities in your app.
So, when you create a context, please create a full-featured context with as much metadata as you can provide.
If you find context saved by an older version of your app, update any missing properties.
If your app no longer supports an older context, then delete the context so it does not show in the list of activities supported by your app.
If your app uses a context provider app extension to create context, please update that app extension as well.
I'm almost done here, and Daniel is itching to tell you all about the brand-new ClassKit Catalog APIs. However, I want to tell you about a few more enhancements we have added to CLSContext that many of you requested.
Like a read-only property identifierPath.
This returns you the full identifierPath of the context. And a new context type "course," and a new context type "custom." Use this in case none of the predefined types is suitable for your app's content. And an optional settable property to give a name to your custom context, your new property to mark a context as a nonassignable activity. Let's see where this property is very useful.
In our example, we created context for a book. You see the entire book, Fun with Science, is an assignable activity indicated by a plus. But I don't think your teacher would assign that entire book. In fact, as a developer, you did not intend to make a book an assignable activity.
You were just using a CLSContext for the book as a container to hold its chapters.
Now, by setting the new isAssignable property of the context to "false," you can make it clear that the book is not an assignable activity. I encourage you to review the updated developer documentation to learn about all the enhancements we have made to ClassKit.
These are the latest enhancements to ClassKit.
With that, let me invite Daniel to introduce you to the new and exciting ClassKit Catalog API. Here you go, Daniel. Thanks, Baskaran. Today, we'd like to introduce a new web service called ClassKit Catalog API.
Our motivation behind this service was to provide an even better experience for our developers who build ClassKit-enabled apps and for the teachers who use them. We built this to help you with several things in mind. First, we wanted to improve your content's discoverability within Schoolwork. Consumers of your app should see the rich repository of content you've made available to them without having to work through each piece first.
Next, we wanted to give you support to provide an even wider array of ClassKit-enabled content than otherwise possible, using the native approach of CLSContext creation.
Lastly, we wanted to decouple the management of your content from the management of your application.
Your content is subject to change, and you should be able to make those modifications without changing your app.
How do we achieve this? Currently, the CLSContexts are defined natively in your app. As teachers use it, it delegates to ClassKit to generate the necessary contexts that get saved on the teacher's device and eventually on the students' devices too.
ClassKit Catalog API prescribes a different way to report your content.
To use this new service, begin by translating your existing CLSContext hierarchy into a JSON structure.
Next, take the payload and upload it using the ClassKit Catalog API.
From there, using the data that you've provided, we'll handle the generation of CLSContext on the teacher or students' devices whenever your app is referenced in Schoolwork. Before I move on, it's important to note that ClassKit Catalog API is intended for content you'd like to display publicly in Schoolwork. Private content, such as dynamically generated or user-specific CLSContexts should continue to be reported to ClassKit using the native API.
We have a new address to which you can make requests: api.classkit-catalog.apple.com.
This is suffixed with "v1" in order to version our API to support any changes we may introduce in the future.
The API itself is comprised of two main components, one to manage contexts and one to manage thumbnails.
Contexts are the fundamental unit that describe your content and that the catalog operates on.
You can think of them as the CLSContexts that Baskaran presented earlier, but with a couple of more bells and whistles. We'll take a closer look at them a little later.
Thumbnails are images you associate with your contexts. In the first part of this presentation, we saw how this is done natively using ClassKit.
Well, with the API, we'll use the thumbnail endpoint, which we'll see later as well. Today, we'll focus on the POST requests. Let's begin by looking at one for contexts.
Here's an example of a POST request with the required fields you'll be expected to provide. First is a query parameter labeled "environment." It determines whether the changes you make target a development environment or are made live to production.
We'll see how to take advantage of this a little later. Next, a couple of headers are expected. Content-Type and Authorization.
Content-Type describes what sort of payload you'll be delivering. For this endpoint, it should be application/json.
Authorization describes the credential with which you'll authenticate with our service.
For ClassKit Catalog API, we decided to adopt the use of JSON Web Tokens. This is an industry-standard approach to authentication that's used across several of Apple's web services.
We'll detail how to generate these tokens later in the presentation.
The body of this request should contain a JSON-formatted list of contexts to be created or updated.
These contexts represent the CLSContexts in your application, and in order for your application's capabilities to be properly represented, the entire context tree associated with your ClassKit-enabled app will have to be reported.
A single context consists of two fields, data and metadata. Let's take a closer look at how these two fields are constructed.
The data object is comprised of fields taken directly from a CLSContext, including the new fields added to the latest update of ClassKit, which Baskaran mentioned earlier.
When you create this data object, use the values of the fields from the CLSContext that your app already creates.
Make sure that they match the contexts your app natively creates, so that your content can be properly represented in the catalog.
The metadata field is meant to capture info that's not represented in CLSContext.
It contains three fields.
Locale, which is meant to distinguish your content between different languages and regions of your application, minimumBundleVersion, which acts as a hit for the client to determine whether this context is supported by a version of your app, and keywords, which represent tags that you believe accurately describes your content. Metadata is a sparse object, but we expect it to grow along with the API.
That was an example of a POST request for contexts.
More details on this endpoint, along with the GET and DELETE methods for contexts, will be available in Apple Developer documentation.
For now, let me highlight a couple of guidelines on the context endpoint. First, there's a limit of 200 contexts per POST request. If you have more, split them up across multiple requests.
If you do, make sure that the children contexts are submitted after their ancestors. Aside from your main app context, child contexts are considered invalid if their parent does not exist. Lastly, should you decide to delete a context, be aware that any of its descendants will be deleted as well. This is done to ensure the integrity of your app's context hierarchy. Moving on, let's take a look at the POST method to upload a thumbnail.
Here's an example of one with the fields expected in a request.
It begins with a thumbnail reference, which is used to associate the thumbnail to one or more contexts.
This is done by matching against the thumbnail field on a context object.
The rest of this request should be similar to the first one we saw. For instance, we must also target an environment using the "environment" query parameter...
provide credentials using the Authorization header, as well as a Content-Type to describe the image being uploaded.
Unlike our previous POST request, we have the addition of a Content-Length to describe the size of the thumbnail to be uploaded in bytes and a body that consists of the bytes representing the image.
During the Handout creation flow, this image is used to represent an Activity. So keep that in mind as you decide how best to represent your content.
That was a look at the POST endpoint for a thumbnail. The corresponding GET and DELETE endpoints are structured similarly, and more details can be found in Apple Developer documentation. Before I move on, let me highlight some guidelines for the thumbnail endpoints. First, use PNG or JPEG to represent your thumbnails.
Next, format your thumbnails to be 330 by 330 pixels. That's the resolution we'll use for Schoolwork.
Lastly, upload your contexts first, followed by your thumbnails.
This is to prevent thumbnails to be orphaned. Upload requests will be rejected if the thumbnail isn't referenced by any context.
This also means if the last context to reference a thumbnail were to be deleted, then the thumbnail would be deleted too.
Before I move on, let's talk about how the API would respond when something goes wrong.
Here is an example of a response you might get back from trying to POST a thumbnail that's reporting an error.
Aside from an expected HTTP status code, a response reporting an error should also contain a JSON body to provide more details on the problem.
The ID field provides a unique identifier which we can use to identify and track this error.
We can tell from the code field that there's a problem with authentication...
and upon inspecting the message field, we find a human-readable explanation of the problem, as well as the way to resolve it. A full list of error codes and their respective messages will be available in Apple Developer documentation. As for this authentication error, how do we get past this? As promised, let's talk about authentication. We mentioned earlier that authentication in ClassKit Catalog API is handled with the use of JSON Web Tokens, or JWT.
This is an industry standard that's used across several of Apple's web services.
To begin, log into the Apple Developer portal and provision a ClassKit Catalog API key for your team.
This private key will be used to create the JWT used to authenticate with ClassKit Catalog API, and must be provided with all the requests you make as part of the Authorization header we mentioned earlier.
The JWT consists of three parts, a header, a payload and a signature.
To form the header and payload, provide the key ID and team ID found in the Apple Developer portal.
This will associate the credential to your developer team.
Additionally, generate a UUID to identify this token and two UNIX time stamps, one to represent when the token was issued and one to represent when the token expires.
What we see here are the raw fields.
To form the eventual token, these fields will have to be Base64 encoded.
To complete the JWT, a signature is required, which is generated using encoded header and encoded payload.
The algorithm to be used to sign this is ECDSASHA256.
There are plenty of public libraries across programming languages that should make it easy to do so.
Altogether, the fully formed JWT will look like this.
This token must be included in the Authorization header and prefixed with the word "Bearer." Now that we have all the pieces, how would you test the content you've uploaded before it's shown to your users? We have two environments to which you can push your content, development and production.
To test your changes, use the "environment" query parameter to upload your content to development, like we showed earlier in the presentation.
Once you've done that, open Settings and navigate to the Developer tab on your device that's running a development build of iOS.
Tap on ClassKit API to see more options.
Under ClassKit Catalog Environment, select the Development environment.
Once it's set, ClassKit will change where it pulls data from and display the appropriate content in Schoolwork based on the environment you have chosen.
Take note that content pushed to development will be available to all ClassKit Apple developers who are also using the Development environment.
To wrap up, I'd like to highlight some best practices to keep in mind while using the API.
First and most importantly, as you define new content or remove old content, report this using ClassKit Catalog API. This ensures that your app and its Activities are properly represented in Schoolwork.
Next, take advantage of locale support provided for contexts.
Doing so helps your app reach the widest audience possible.
Lastly, you'll want to provide as much detail as you can in the contexts you report and have thumbnails associated with them. This will provide more detail to users in Schoolwork as well as a more compelling reason for them to assign content from your app. That's all for ClassKit Catalog API. It will be available for ClassKit developers this summer.
This also concludes our presentation of "What's New in ClassKit." Thanks.
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.