Streaming is available in most browsers,
and in the Developer app.
-
Create engaging content for Swift Playgrounds
Learn how you can build guided instructional content designed for Swift Playgrounds. Follow along with us as we explore how you can add a guide to a completed sample code project. We'll demonstrate how to add tasks to your learning center to show off relevant code and optional experiment tasks that encourage learners to extend the project with code of their own.
Resources
Related Videos
WWDC22
-
Download
♪ ♪ Hi. I'm Stephanie Angulo. and I'm Marcus Jackson. We're software engineers on the Swift Playgrounds Content team. Today, we will give you the tools to create engaging content for Swift Playgrounds.
Swift Playgrounds 4 introduced app development on iPad and Mac. It's the best way to learn about building apps for the App Store. Our team has released a number of tutorial and sample code products that help you learn the fundamentals of app development. We cover topics like building dynamic SwiftUI apps using observable data models, customizing views with fun SwiftUI animations and shapes, and more advanced topics, like asynchronous data fetching. In today's session, we'll provide an overview of our new instructional system, write content using the project's guide module, and build an immersive learning experience with walkthrough and experiment tasks. Let's get started. Imagine a learner completing our tutorial, "Keep Going with Apps." They'll end up with an app called, "Emoji App." In this app, they can keep track of all their favorite animals in a list, change their color and size, and tap on the animals to watch them get their groove on in "Creature Dance" view.
This dance view is fun, but I want the vibe to be more of an actual dance party. So let's go ahead and add some extra features we can show our learners.
Here, I added a bit more code to "Creature Dance" view. Every party needs a dance floor. So I made a 10x10 grid that I set as my view's background.
And each tile in the grid updates its color randomly using a custom view modifier.
Looks groovy huh? I also wanted to make sure our favorite animals can dance without our help, so I made a few more custom modifiers that help animate the animals' scale, position offset, and rotation.
In all of these custom modifiers, these animations are set to repeatForever, which means our animals can dance all night on their brand-new dance floor.
And finally, to really light up the dance floor, I added an animated disco ball at the top of our view.
Adding this final touch really pulls it all together for the ultimate dance party. I've made quite a few changes to this project, and I didn't even to dive into the details of custom view modifiers. So how should we explain this to our learners? You could direct a learner to Apple's documentation, but now you also have the option to teach these concepts alongside your project's code in Swift Playgrounds. Our team has built this new instructional system that's designed to help authors like you create engaging in-app experiences for your learners. Today, we'll walk you through how to build the learning content for this app in Swift Playgrounds 4. Let me give you a sneak peek of what you'll be walking away with. When a learner first opens your content on Swift Playgrounds, you can introduce them to the project with an optional welcome message, as shown here from our friend Byte. The welcome message sits at the top of the project's source editor on the left hand side of the screen, while on the right hand side of the screen is the learning center.
The learning center is a designated area where you can add images and instructional text that describes your content to your learners.
In our welcome message and our learning center, we're letting our learners know that this project will be pumping up the jams with the help of SwiftUI colors, shapes, and animations.
The learning center also contains a section for tasks. Tasks are coding objectives you the author can write to help guide your learners. They're a fundamental building block for content.
By tapping a task button in the learning center, our instructional system will open up a Swift file and render a card with learning material at the top of that file. This card can contain a series of pages with text, images, and code snippets. Later, Marcus will go over two tasks types: walkthroughs and experiments.
At a high level, that's what our instructional system has to offer. With the right prose and the right tasks, you can build a compelling educational experience for your learners. Now, in order to start creating your own content, we first need to talk about the guide module. By default, the file structure of a swiftpm project keeps all its source code at its root. In order to upgrade your project to take advantage of the instructional system, you'll need to change its file structure. We first need to create an App module. Once it's created, we need to move all our project's source code and assets into it. The Package.swift file should be left at the root of our project.
Then we need to create a guide module. This module should be at the same level as the App module and Package.swift file. Inside the guide module, you'll need a guide file. This file will include all the prose of your learning content. I've already started on my guide file, so let's check out the content I've written so far.
The guide file contains a combination of directives and markdown. Directives are an extension of markdown that can take in primitive types as attributes, such as strings, as well as more complex types, like markdown elements and other directives.
Directives can act as containers for other directives, but they can also represent UI elements in our instructional system. First in the guide file, I've added the necessary guidebook directive that's wrapped around the entirety of the file. It acts as the main container for all our directives. Its parameters include a title, icon and background image, and the file you first want opened when you open the project. Under the guidebook directive, I've added a welcome message directive. Welcome messages are optional and as mentioned earlier, they are presented to the learner when they first open up the project. Below the welcome message directive, I've added a guide directive that's wrapped around a step directive. The guide directive acts as a container for your steps and steps map out to your content displayed in the learning center and tasks. To start adding images and instructional text in your learning center, you'll need to include a ContentAndMedia directive inside your step.
So I got this party started by adding a dance floor, a nice welcome message, and the prose for the learning center. Marcus, do you wanna keep this party going? Marcus: Definitely. What a dope dance floor for our creatures to party on. While this effect is really cool, I think it might be too much for someone who is still learning. To help explain this code, we can use walkthrough tasks. Let's start with a one-page walkthrough. Later, I'll show you how to fill out the rest. Stephanie already showed you the beginnings of our guidebook, as well as a helpful welcome message. We already have the first directive you need to create tasks, the step directive. The step directive is where our walkthrough content will live. To make a step, you need to fill it with two other directives. Here, we've already added a content and media directive. This directive contains markdown that goes into the learning center on the right hand side. The body of this directive can contain any form of markdown text. This is the place to put longer prose and larger images that might help cover your topic. Here is the content and media directive being displayed in Playgrounds. While the area seems small in this example, this view can extend further down and is contained in a scroll view. This makes it a great place to write longer bits of prose and show complex content such as diagrams. Once you have your content and media written, we can add in the second required directive, tasks. We add our tasks into another directive called a task group. Task groups are an optional directive you can put inside of steps, if you want to collect a group of tasks together. You might consider this if you have content which covers the same topic across multiple files or different types of tasks. Within the task group, we can add a short bit of text. This will be displayed in the learning center as a subtitle.
Here is how a task group displays in Playgrounds.
Now that I have my task group and my subtitle, I can start adding task directives. Tasks have a few parameters The first parameter is type. This lets the instructional system know what UI to generate when displaying this task. Next, every task needs an ID. IDs are strings that can be anything you want. However, every ID in the guide must be unique. The title parameter is also a string. This can also be whatever you want and does not have to be unique. This title will be rendered by the task card UI. Finally, the file parameter tells the learning center which file in the project to open when the learner starts this task. Here is how a task displays on Playgrounds. The title sits inside of a button, and the file of the walkthrough is listed above it. Now we have our walkthrough task written. Let's add our first page. Page directives go inside the body of a task and have the following mandatory parameters: The ID parameter behaves just like the ID for a task, so they must be unique for the entire guide file. The title parameter behaves a lot like the one for tasks. However, when you leave the title string empty on a page, this lets the instructional system know to use the task's title when displaying this page. Inside of a page, you can add any markdown text, similar to the content and media directive. However, the task view is much smaller than the learning center. Keep your text short and avoid using complex images like diagrams because they may be hard for the learner to read. This is the first page of our walkthrough as rendered by Swift Playgrounds. We're almost done with our first walkthrough, but first I need to show you how to highlight the code as shown in the last screenshot. For that, we need to add some markers to CreatureDance.swift. When my walkthrough is shown, I'd like to highlight the first custom modifier, the animatedScalingEffect. To add highlights to a line, I'll add a pair of comments on the lines before and after the code. We start with the multiline comment syntax, /* Inside the comment, we write #-code -walkthrough, followed by a pair of parentheses. Inside the parentheses, we write the ID of the page directive we want to highlight. In this case, 1.modifier.
Now, let's test this out in Playgrounds. Let's open the Emoji App project.
When you open the project, you're greeted with the source editor on the left and the preview on the right. Above the source editor is our welcome message, where our buddy Byte gives you an overview of what learning content there is to do. I'll tap on the Learn More button.
The preview on the right is swapped out for the learning center. At the top is the prose we wrote in the ContentAndMedia directive. Below that is the task group, as well as the button with the title of our walkthrough. Walkthroughs are denoted in the learning center as buttons with pictures of another one of Byte's friends, Expert.
Tapping on this button does a few things: First, the learning center is once again swapped out for the preview. Second, if it isn't already open, the file specified in the task's file parameter is opened in the source editor. Third, the task view drops down above the source editor. Finally, the source editor highlights the code marked in the code walkthrough comments. If the content is not on screen, the source editor will scroll until the lines of code that need to be highlighted appear. And that is how you write a walkthrough in Swift Playgrounds, but I think it's fair to say you're probably curious what a walkthrough with multiple pages looks like. To do that, I'll open the project in Xcode to fill out the rest of my walkthrough.
Now with the guide file open in Xcode, I'd like to add a couple more pages to my walkthrough. I've explained a little bit about what a view modifier is, but I'd like to explain more about how to build a custom view modifier. I'll go ahead and add those pages.
Great. Now we have our walkthrough for custom view modifiers.
I think now is also a good time to explain the ViewModifier protocol. This way, learners can try to make their own ViewModifiers if they want to. To do this, I'll add another walkthrough to our task group.
We now have a fully fleshed out pair of walkthroughs. I'll switch back over to my iPad to see how it looks.
When we open our project, there are now two walkthroughs in the learning center. I'll start by tapping on the first walkthrough.
Just like before, the line with the view modifier highlights and our task view drops down to explain what this code is. Now I can tap the next button.
The source editor now scrolls down to the modifier struct and explains what this struct is for.
Tapping on the next button again moves to the final page of this walkthrough, which explains more about the body method inside the modifier struct. In the bottom corner of the task view, there is a button marked Next Walkthrough.
Tapping on this automatically begins the next walkthrough task. This functionality is given to you for free by the instructional system as long as there is another task to progress to. Now that I am here, I will tap through the rest of this walkthrough.
And that is how you build walkthroughs in Swift Playgrounds. Next, I'd like to show you how to create a different kind of task that will allow learners to try adding code themselves and seeing what happens. So at this point, we have a good party going. Our creatures are dancing, and they have some lights in the background. While it basically looks like a nightclub in there, I think we can do a little bit better. I think it would be great to add some colors to our creatures so it looks like they're dancing under the strobe lights of their little club. But that's just me. What would you do? This is where experiment tasks come in. Experiments are optional bits of code learners can add if they are feeling extra curious or if they want a way of making the app unique to them. Back in the guide file, we can add our experiment task to the same step we were already working on. I've created a new task group to hold our experiments which I've named "Experiments." I've populated it with a subtitle as well as the beginnings of our first experiment task.
The first difference between an experiment task and a walkthrough is what goes into the type parameter. The other parameters follow a similar convention to walkthrough tasks. Page directives work the same way they do in walkthroughs. However, for experiments, we add one optional parameter, isAddable. The isAddable parameter allows experiment tasks to add code directly into the source editor. When isAddable is set to true, an add button appears in the learning task card next to the code snippet. The code in a page directive must be wrapped in a code block using the triple back tick markdown syntax. It's best practice to keep your code blocks to ten lines or less. While the task view can show longer code snippets if need be, it's better if learners don't have to scroll. Here is how the code view displays in Playgrounds. To the right of the code snippet, is an add button, since the isAddable parameter was set to true. That is almost everything we need to write an experiment task. But remember that isAddable parameter? This allows the experiment task to add code to the source editor, but we need to tell Playgrounds where in the code to add the snippet. Here we are again in CreatureDance.swift. I want learners to add the color modifier right below the opacity modifier. So that's where I'll add my experiment task comment. Experiment task comments are single line, meaning they start with a double slash. Then, we write #- learning-task. After, comes a pair of parentheses, and inside we write the ID of the experiment task. Now we have everything we need to test out our experiment task. Once again, I have all this already written in the swiftpm project Stephanie and I are working on. Let's check it out. We're back once again to the learning center. This time I want to focus on the bottom task group, where our first experiment is. Experiments are noted in the instructional system by another one of Byte's friends, Blu. Let's tap on the experiment task.
What happens next should seem familiar. The task view drops down. However, this time, the task view contains a code view. On the right of the code view is an add button. Tapping on this adds the code right to the source editor.
With the code now added, I'd like to check out what changes that made to the CreatureDanceView. Let's start this party! Sweet. Now we can see the lights hitting our creatures. This is pretty groovy, but I think we can take it up one more notch by using a timer to give the creatures a random color every few seconds. To do that, we'll need to add another experiment, so let's take this project back to Xcode and add our new task. Before we add in our second experiment, I think it's a good idea to add a page to the experiment that's already there. For a learner, it can be confusing to add a block of code and not know why or what it does. To help with that, I'll add a page with some text before our code page.
Now, we are ready to add our second task. Again, I want the learner to add some code to their project, so I'll add a page explaining the code followed by an addable code snippet.
And with that, we have made a new piece of content to teach learners about some of the things you can do with custom view modifiers. Hey, Stephanie, you ready to show them what we've built? Stephanie: Yeah, let's do it.
I'll open the final version of our content on my iPad and check out how my changes and Marcus' changes flow together. When I first open the project, the welcome message animates in, introducing us to Creature Party. When I tap on the Learn More button in the welcome message, it opens the learning center for me. Awesome. Our learning center does have my description at the top and the four tasks Marcus added. Let's tap on the first walkthrough.
Here, Marcus used my AnimatedScalingModifier as an example to explain how to use custom view modifiers.
When I tap on the Next Walkthrough Button, the second walkthrough animates in.
Marcus used the View Modifier protocol as an example to describe how protocols work. After finishing the second walkthrough, when I tap Done, the first experiment task segues in.
The Dancing in the Strobe Light task tells me I can add some color to our creatures by adding this code snippet containing a colorMultiply modifier. Let's remind ourselves what this dance party looks like before adding the code snippet.
All right, cool. I'll add the code snippet by tapping Add and tap Start the Party again to check out the changes.
Nice, the creatures changed color. I'll complete this experiment task now and transition to the last one.
The Switch It Up experiment task tells me I can customize the color of the animals with a tap gesture and a timer. I'll add the code snippet and start the party once more.
And now when I tap on the animals, they change color. Nice. I'll complete this last task and head back to the learning center.
Now, all tasks are marked as completed in the learning center, which means we've completed this sample.
And that's how you take advantage of our new content features in Swift Playgrounds 4. We hope you enjoyed today's session and we're so excited to see what sorts of learning experiences you'll build. Don't forget to check out the other Swift Playgrounds session, Build your First App in Swift Playgrounds. Enjoy the rest of WWDC. Marcus: And now, if you'll excuse us, we have a party to attend.
-
-
1:27 - Dance Floor
let numOfTiles = 100 let squareLength = 150.0 // Dance floor ForEach(0 ..< numOfTiles, id: \.self) { index in let i: CGFloat = CGFloat(index / 10) let j: CGFloat = CGFloat(index % 10) let x = (squareLength * i) - (squareLength) let y = (squareLength * j) - (squareLength * 2) Rectangle() .frame(width: squareLength, height: squareLength) .border(.black, width: 3) .position(x: x, y: y) .randomizedColorEffect(startAnimation: startParty) } .blur(radius: 15) .opacity(startParty ? 1.0 : 0.0)
-
1:47 - Dance
ForEach(data.creatures) { creature in Text(creature.emoji) .resizableFont() .animatedScalingEffect(startAnimation: startParty) .randomizedOffsetEffect(startAnimation: startParty, x: midX * 0.6, y: midY * 0.6) .animatedRotationEffect(startAnimation: startParty) .opacity(startParty ? 1.0 : 0.0) }
-
2:08 - Disco Ball
Text("🪩") .resizableFont() .animatedRotationEffect(startAnimation: startParty) .opacity(startParty ? 1 : 0)
-
5:12 - Guidebook Directive
@GuideBook(title: "Creature Party!", icon: icon.png, background: background.png, firstFile: CreatureDance.swift) { }
-
5:28 - Welcome Message
@WelcomeMessage(title: "Welcome to Creature Party!") { In Creature Party, you'll take this app of dancing creatures to the next level with the help of colors, shapes, animations, and plenty of emoji! }
-
5:37 - Guide and Step Directives
@Guide { @Step(title: "Pump up the jams") { } } }
-
5:53 - Content and Media Directive
@ContentAndMedia { Tonight, the creatures are gonna party like it's 2022. 🐙💃🦝🕺🦦 }
-
7:15 - Task Group Directive
@TaskGroup(title: "Walkthroughs") { Here are the walkthroughs! These will help explain all of the new code. }
-
7:57 - First Walkthrough Task
@Task(type: walkthrough, id: "partyMode", title: "Setting up the Party", file: CreatureDance.swift) { }
-
8:44 - First Walkthrough Page
@Page(id: "1.modifier", title: "") { This is a [view modifier](https://developer.apple.com/documentation/swiftui/viewmodifier). Modifiers let you create unique versions of a view in SwiftUI. }
-
9:48 - Walkthrough Highlight
ForEach(data.creatures) { creature in Text(creature.emoji) .resizableFont() /*#-code-walkthrough(1.modifier)*/ .animatedScalingEffect(startAnimation: startParty) /*#-code-walkthrough(1.modifier)*/ .randomizedOffsetEffect(startAnimation: startParty, x: midX * 0.6, y: midY * 0.6) .animatedRotationEffect(startAnimation: startParty) .opacity(startParty ? 1.0 : 0.0) }
-
11:56 - First Walkthrough extra pages
@Page(id: "1.struct", title: "") { Custom view modifiers are structures that contain code for explaining how to modify whatever view the given modifier is attached to. } @Page(id: "1.body", title: "") { The body method allows you to add custom view modifications. For example, here you're adding a scaling animation that grows and shrinks the `Creature` over a certain period of time. }
-
12:18 - Second Walkthrough Task
@Task(type: walkthrough, id: "protocol", title: "A Little More on Protocols", file: CreatureDance.swift) { @Page(id: "2.protocol", title: "") { All custom view modifiers implement the `ViewModifier` protocol. } @Page(id: "2.body", title: "") { The `ViewModifier` protocol requires all structures that implement it to write the `body(content:)` method. } @Page(id: "2.usage", title: "") { After you've written content for your `body(content:)` method, you can use it on any view you want. Here you'll use it on each `Creature` to add a rotation animation. } }
-
14:21 - First Experiment Task
@TaskGroup(title: "Experiments") { Time to set this party off! You can use experiments to add some extra pazazz to the dance floor. @Task(type: experiment, id: "colors", title: "Dancing in the Strobe Light", file: CreatureDance.swift) { } }
-
14:48 - First Experiment Page
@Page(id: "3.code", title: "", isAddable: true) { ``` .colorMultiply(creatureColor) ``` }
-
15:55 - Experiment Task Comment
ForEach(data.creatures) { creature in Text(creature.emoji) .resizableFont() /*#-code-walkthrough(1.modifier)*/ .animatedScalingEffect(startAnimation: startParty) /*#-code-walkthrough(1.modifier)*/ .randomizedOffsetEffect(startAnimation: startParty, x: midX * 0.6, y: midY * 0.6) /*#-code-walkthrough(2.usage)*/ .animatedRotationEffect(startAnimation: startParty) /*#-code-walkthrough(2.usage)*/ .opacity(startParty ? 1.0 : 0.0) //#-learning-task(colors) }
-
17:42 - Experiment Text
@Page(id: "3.lights", title: "") { Next, add some colors to the creatures so it looks like they're dancing under the lights! }
-
17:55 - Second Experiment Task
@Task(type: experiment, id: "timer", title: "Switch it Up", file: CreatureDance.swift) { @Page(id: "4.lights", title: "") { Now that you have some colors, you can add some code to change the color of the creatures using a timer. Let's add one! } @Page(id: "4.code", title: "", isAddable: true) { ``` .onTapGesture { if let timer = timer { timer.invalidate() self.timer = nil } else { creatureColor = Color.randomColor timer = Timer.scheduledTimer(withTimeInterval: 2.0, repeats: true, block: { timer in creatureColor = Color.randomColor }) } } ``` } }
-
-
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.