ClassKit was first introduced by Apple at a special educational keynote held in one of the Chicago’s schools in March 2018. The venue was by no means random – ClassKit enables developers to integrate their application with a brand new application called Schoolwork, an app for assigning and collecting student homework. Even though the target group of the ClassKit framework is really small at the moment, I think it’s a very good idea to integrate it as soon as possible, if you have any type of educational app. As you will see from this blog post, doing so is very easy, and you could contribute to making schools more innovative!

Schoolwork App

The Schoolwork app lets you easily assign anything from worksheets to activities in educational apps, follow students’ progress, and collaborate with them in real time. The app was presented at the March event, however, it wasn't available for users to download until the end of the June. For a couple of months, it was only available for developers selected by Apple – you could ask for access if you had some kind of an educational app. Fortunately, it isn’t the thing anymore. Everyone can download Schoolwork straight from the App Store, and if you are a developer, downloading it adds very handy additional options inside the device’s developer settings. Schoolwork allows two kinds of users: teachers and students. Thanks to these additional settings, you can easily switch between those groups, and you don’t have to use separate devices and set everything up by yourself. The development version of the Schoolwork app automatically creates fake classes with fake students for your convenience. Your app will be automatically available under the Schoolwork app, if you prepare it correctly. Of course, I will show you how to do this in the next sections. There is one inconvenience, however: you can’t test it in simulator, because Schoolwork isn’t available in that environment. You have to use a real device during the development, and this device must be an iPad. Schoolwork isn’t available for iPhones.

ClassKit





ClassKit is available for devices with iOS 11.4 or newer. Integrating it doesn’t replace any existing logic or storage mechanisms in your app, and you don’t use it to generate any new user interfaces. Instead, you use ClassKit to publicize the structure you already have. Every app that adopts ClassKit has a single, predefined top-level context called the main app context. Starting from this context, you have to build a structure of separate child contexts that describe and fit your app best. After creating this structure of contexts, you have to share them with the ClassKit DataStore. Thanks to this, your app will be visible in the Schoolwork app, and teachers will be able to use it to navigate to the tasks inside your app. You should do this as early as possible – best if it is possible during the launch of the app – however, if your data is stored on a remote server, and it requires doing some kind of network request first, it still possible to integrate it with ClassKit. Just fetch the data during the startup and share it with ClassKit later.

Demo project

For the purpose of this blog post, I’ve created a very simple application for testing students' algebra skills. You can find it on my GitHub. There are two branches: master and without_class_kit. If you want to code when reading the blog post, switch to without_class_kit. I’ve already prepared // TODO comments for your convenience. Every TODO has a number that will match the number of step described here. If you just want to see complete working projects, use the master branch. There’s one class, called GameService, which will be responsible for all of the ClassKit integration. Here’s how the application looks in action: A user can select the module and then the app shows them three questions. Each question has four answers. After answering all questions, the user sees the score. That’s all. Let’s start integrating ClassKit, then!

Step 1: Project settings setup

We have to start with enabling ClassKit inside the project’s Capabilities. After you've done it, head to the Apple Developer website and do the same thing there. To test the ClassKit, your developer membership must be active. Remember to regenerate your provisioning profiles after doing so. You should be able to import and use the ClassKit framework now.

Step 2: Create your context structure

// Create contexts var contextsCoCreate: [String: CLSContext] = [:] for module in modules { let context = CLSContext(type: .quiz, identifier: module.identifier, title: module.title) contextsCoCreate[context.identifier] = context } // Add contexts to parent only if they didn't exist before let parent = CLSDataStore.shared.mainAppContext let predicate = NSPredicate(format: "parent = %@", parent) CLSDataStore.shared.contexts(matching: predicate) { (contexts, error) in contexts.forEach { contextsCoCreate.removeValue(forKey: $0.identifier) } contextsCoCreate.forEach { parent.addChildContext($0.value) } // Save our changes CLSDataStore.shared.save() }

Inside the method setupClassKitContexts(), we have to define contexts matching the application logic and share them with CLSDataStore. This method gets called at the launch time of the app. I’ve decided to put every module in a separate context with a type of quiz. These contexts are going to be siblings of the main app context – the teacher will be able to select the quiz exactly in the same way using the Schoolwork app as we are doing it inside our app. Before adding new contexts, we have to check if we haven’t done it already.

Step 3: Handle deep linking

func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool { guard let contextPath = userActivity.contextIdentifierPath, let lastItem = contextPath.last, let module = gameService.modules.first(where: { $0.identifier == lastItem }) else { return false } selectModuleViewController?.deepLink(to: module) return true }

When a student launches the app from the Schoolwork app, it should take him directly to the module that was selected. We can do this using deep linking. ClassKit shares the selected context path using a NSUserActivity property called contextIdentifierPath. It’s an array of Strings pointing exactly to the context that was selected by the teacher. Our app didn’t have nested context, so I’ve just used the last id to map the appropriate module.

Step 4: Inform ClassKit that a student started the module

func didStart(module: Module) { // Start ClassKit Activity CLSDataStore.shared.mainAppContext.descendant(matchingIdentifierPath: [module.identifier]) { context, error in guard let context = context else { return } context.becomeActive() self.currentActivity = context.createNewActivity() self.currentActivity?.start() } }

A teacher should be able to track the time students took to complete the quiz. We don’t have to calculate it by ourselves, as ClassKit will handle it for us. The only thing we have to is to create a new activity and call the start() method when a student starts the module. Also, we have to keep the reference to this activity, because it will be useful in the next steps.

Step 5: Add additional activity items

[...] let item = CLSBinaryItem(identifier: lastAnswered.identifier, title: lastAnswered.question, type: .trueFalse) if case ExerciseState.answered(correct: true) = lastAnswered.state { item.value = true } else { item.value = false } currentActivity.addAdditionalActivityItem(item)

Besides the final score that we will provide to the ClassKit at the end, we can provide additional activity items. It will allow the teacher to see which questions were answered correctly and which were not. There are many types of the activity items, so I encourage you to explore the documentation regarding CLSActivityItem and pick the one that will fit your app best. For this kind of question, I’ve decided to go with the CLSBinaryItem with the trueFalse type of question. Don’t forget to add this item to the activity we created in the previous step.

Step 6: Publish the score and stop the activity

func didFinish(module: Module) { // Finish activity and publish score to ClassKit guard let currentActivity = currentActivity else { return } let score = CLSScoreItem( identifier: module.identifier + "_score", title: "Total score", score: module.calculateNumberOfCorrectAnswers(), maxScore: Double(module.exercises.count) ) currentActivity.primaryActivityItem = score currentActivity.stop() CLSDataStore.shared.save { _ in self.currentActivity = nil } }

The user has answered all questions, so now we need to share the score with the ClassKit. We are using the CLSActivityItem for this again, however, this time we will set it as the primary activity item. We don’t have to calculate the score by ourselves, as ClassKit will handle that for us again – we just need to select the most appropriate activity item. I’ve chosen the CLSScoreItem for my use case. We can now call the stop() method on the activity to stop the timer and save the ClassKit Data Store. That’s all. Our app is integrated with the ClassKit, and we can test it on real device.

Final result

Go to the settings, select the teacher mode under the developer ClassKit settings and inside Schoolwork, you should be able to create a handout with one of the modules shared with the ClassKit at the

beginning

. Now head again to the settings and switch to the student role. After running the Schoolwork app, you should see that there’s

a

handout assigned to you and after tapping the application icon, it will take you straight to the module using deep linking. After you answer all questions, your score will be visible inside the Schoolwork app, and the teacher will see the same.

Conclusion

Adopting the ClassKit is very easy and straightforward. The ClassKit API is very well documented and easy to understand. If you want to learn more about it, Apple created a great landing page for the framework with all links to the documentation you should have a look at if you want to play with the technology. I also really recommend watching the WWDC 2018 session. If you have any questions, just post a comment or mention me on Twitter (@kwiecien_co)

Link to the repository: GitHub