SwiftUI allows a whole new world of possibilities when developing watchOS apps and notifications. From custom animations to providing an intuitive feel with Digital Crown haptics, SwiftUI helps you build exciting and immersive experiences for Apple Watch. See how easy it is to create custom elements with animations, embed gesture-driven animations within notifications, and learn about the enhanced debugging support to make watchOS app development faster than ever.
My name is Matthew Koonce and I'm an engineer on the watchOS Frameworks Team.
I'm thrilled to be here today alongside my friend and colleague, Josh Weinberg, to talk about how we can use SwiftUI to build some really amazing experiences that happen right on your wrist.
Since our humble beginnings in watchOS 1, the Apple Watch has evolved significantly, and watchOS has evolved right along with it. Over the years, we've been adding some really great APIs and features that have enabled all of you to build some really awesome experiences. But that is just the beginning, because with watchOS 6, we've brought the platform to a whole new level.
With independent apps, we've decoupled the iOS and watch OS experiences.
And for the first time, your apps can be downloaded right on Apple Watch with the new app store.
We've also added support for some new APIs like streaming audio and extended runtime sessions. But we've also brought something super awesome, and that is the full power of SwiftUI to watchOS.
With SwiftUI's powerful declarative syntax and its integration right into Xcode's canvas, it's never been easier to express the views that you want to build.
And on watchOS, it means that we have a whole new UI framework.
And with it comes some really awesome features, APIs, and just a whole new world of possibilities for things that you can build. For example, with Lists, we finally brought support for swipe to delete and drag to reorder.
And with SwiftUI's powerful animations, you can create completely complex, interruptible, and custom animations happening right on the watch. Now, SwiftUI is fully integrated with WatchKit, which means that you can have SwiftUI views that live right alongside your existing WatchKit interface controllers.
And we have enough support for embedding some WatchKit provided interface objects directly within your SwiftUI view hierarchies, like the brand-new sign-in with Apple button.
In fact, SwiftUI has really robust support for integrating with all of our existing UI frameworks. And we have a whole talk dedicated to the topic that I highly recommend that you check out.
All right, so SwiftUI is fully immersed in the watchOS experience, which means that you can use all those great APIs you have with watchOS 6 and all those awesome things we've been adding over the years. Whether you're using a streaming audio app, you're using our new extended runtime features, or you're creating something completely new for the platform. For the first time, we have a UI framework that works on all of our devices.
Whether you're developing for the 38 mm Apple Watch or creating something for a 65 inch television, all of it can be done with SwiftUI. But the Apple Watch is a unique device and it has its own considerations when building an application.
Creating for Apple Watch is more than just creating a miniaturized version of your iOS app, it's about distilling down what makes your experience great. An Apple Watch is a highly personal device and provides easy access to vital information. So while your iOS app may be used for a couple of minutes while you're waiting in line at the grocery store, on Apple Watch, the interactions are typically a bit shorter.
People see information at a glance and respond with just a few taps. All right, so creating for Apple Watch may start in your application, but it's about building an experience that spans all these different touch points we offer throughout the system.
Whether you're creating a dynamic notification, using Siri relevant actions to show timely information right on the watch face, or using complications.
So that experience is really important to Apple Watch and defining that experience is even more important. That experience should be a couple of different things. And it should be contextual and timely.
Showing the right information to your users at the right time. It should be highly legible, showcasing beautiful design right on the watch screen. And it should prioritize quick interactions, getting things done with just a few taps. Which brings us right back to SwiftUI, because SwiftUI, you can finally build those experiences that you've always wanted to build for Apple Watch. So today we'll take a look at a couple of different ways we can use SwiftUI on watchOS.
First, we'll see how we can use SwiftUI in an application, build an engaging and highly interactive notification, and harness the power of the digital crown to create tactile experience.
So let's get started by looking at an app. And let's look at an app that Josh and I have been working on.
All right. So here I am in Xcode and the app Josh and I have been working on is called Pop Quiz.
It's a flashcard app that lets me take my studies with me anywhere. And what I have open right now is my interface controller.
If you've used WatchKit before, interface controller will feel familiar. And this is an interface controller, but it's a bit different.
First, this interface controller is subclassing WK hosting controller, which is a type of interface controller that is provided by SwiftUI. Now, because this is a type of interface controller, I can use this interface controller wherever I'm using other interface controllers.
For example, this one is set up in my storyboard to be the initial interface controller for this app, making this interface controller the entry point for my application.
But, for example, if I wanted to use a paging interface, in my storyboard I can set up my interface controller here to be paged among side other WK interface controllers. Which gives it a nice interoperability between WatchKit and SwiftUI. I can also push to this interface controller using the same WatchKit interface controller push APIs.
Or I can use SwiftUI's navigation button and give a destination of an interface controller name and push to WatchKit from SwiftUI. Okay. So let's go back to this code.
So one other thing you might notice here that's a bit different from using WatchKit is that there's no IB outlets.
That's because with WB hosting controller and SwiftUI, our entire applications interface controller's views are defined with SwiftUI.
That's so awesome.
So something else that's new here is this use of the body property. And this is a type of property that's provided by WK hosting controller. And we override it here. This body property lets us specify which SwiftUI view we want associated to our interface controller. And in this case, it's our topic list, which is just a list of topics I have of different flashcards. And you can see a preview of it over here on the right side. All right. So let's go dive in to our topic list. So as the name implies, our topic list is a list. And this list says, for each model -- sorry, for each topic in my model, I'm creating a navigation button that's going to go to another SwiftUI view, in this case, the flashcard list.
And it creates a topic cell.
And I'm using this list row platter color modifier, which is what's putting that color on each of these cells. If I go down here to my topic cell, you can see that's pretty simple right now. Right now it's just using my topics title and it's being modified to use the System Rounded font, which looks gorgeous on Apple Watch. Okay. One other thing to note here at the top is this use of app object binding.
App object binding is how I'm letting SwiftUI know that this model is the source of truth for this view and that my view should be updated whenever this model changes. Now, understanding how data flows through SwiftUI is a fundamentally important topic, and we have a whole talk dedicated to it called Data Flow in SwiftUI, and I highly recommend that you check that out.
For now, what we need to know is that this is how I'm keeping my model and my list in sync. So let's go ahead and make this preview live by pressing the play button here. The application comes alive and I can scroll it right within Xcode.
And you can see, by default, we have a pretty well-behaving application.
Just by using list, we're doing some really cool things under the hood, like giving this really nice animation here as the list scrolls, we collapse the cells and it feels like your content is just flowing right off your wrist.
All right. So there's been a couple things I've been meaning to do to this list, let's go ahead and do those now.
First is I have -- I want to add a label to each of my -- in my topic cell here that just shows the count of cards I have for each of my topics.
Just so I can see at a glance like how many morphemes do I need to study. Let's go ahead and just do that now. And to do that I need a VStack.
Well, the editor in Xcode makes that super easy to do.
I can just command click on my text here and bring up the quick action menu.
And I can select the embed in VStack option. The code is inserted.
And now below this text, I can add the count of cards I have.
And you can see the preview updates right away.
Now, you can see that VStack aligns center by default. And in this case, I want a leaning alignment.
Again we can do that with the editor. If I select this VStack, command click on it, and select the inspector, I get this window that shows me different kinds of modifiers I can apply to this VStack.
And in this case I want to use the leading alignment option. You can see the preview updates again right away. It's just super fast.
Now, we're using the leading alignments and trailing alignments in SwiftUI, because by default we support right to left languages.
If I have a localizable strings file on my project that has Arabic or Hebrew, by default, this entire app's user interface will flip and will support right to left languages by default.
All right. So there is actually something else I want to add here, which is like a symbol I have associated to each of these topics. And I want that to appear on the leading side of both of my texts. And I can do that with an HStack.
So, again, let's use the editor here, command click on this VStack, and select the embed in HStack option. And then I can insert my symbol, which is just an emoji.
Now, that's a bit small, I'd actually like that a bit bigger. So, again, I can return to our now familiar inspector, command click on this text, bring up the inspector. And you can see I now have different contextual options.
And I can select here on my font a title font. And these inspectors make it so easy to learn and like how to add and modify this code, because it's adding real code into my code base. It's not changing a POS file on my storyboard, it's actually teaching me how to write good SwiftUI code. All right.
So with just a few lines of SwiftUI code, we've built out this cell a bit more. And, again, this entire time it's been running live right in Xcode. I have a need to build and run this app.
This kind of iteration is just so powerful on Apple Watch.
We can finally get this really quick design flow going and I don't need to like do a round-trip to the device. Okay. So there's something else in SwiftUI that's brand-new with it and is unique to Apple Watch, and that's an entirely new way, new style of scrolling.
So to do that, let's first add a bit of height to each of these cells. I'll go back to the inspector here and go to my height, and out of height, elect 100. The preview updates.
And now I can go to my list.
And this new style of scrolling is called the Carousel style.
With just one line of SwiftUI code, we've completely transformed the way that this list scrolls.
If I go ahead and scroll it down in the preview, you can see that each of these cards now centers right in the middle of the screen.
This gives our users a nice sense of place. As they're scrolling this content, it let's you bring focus onto each of the elements in the list.
Now, I decided to use this type of scrolling for Pop Quiz because we only have a handful of elements in this list. And I can make each of them big by using a frame. And this is where the Carousel style really shines.
The Carousel list style is also a great option if you have interactive elements within each of your cells, because it gives that nice bit of focus and lets our users have nice big tap targets.
Okay. So there's also two new types of interactions we've added with List, both of which are brand-new on Apple Watch.
The first is the ability to drag to reorder.
Now, we've made this really simple with just one modifier.
You can add on move and pass on move closure that tells it how to update your data store. In this case, just moving from one element to the next. And I can add a delete, a swipe to delete.
What used to be a bunch of delegate methods and setting up swipe actions is just one line of SwiftUI code. So now I can scroll this list, we have this really beautiful animation, these really nice built out cells, all this code is right here, right next to my preview. And I find a cell I want to delete, I can just swipe it right in the preview and delete it. It's just so cool and so powerful.
All right. So that was how we could use SwiftUI in our topic list and to build out these really great interactions that were just not possible before on Apple Watch.
But that's just the start of what's possible on SwiftUI and Apple Watch.
To see how to take your experiences to the next level, I would like to invite Josh Weinberg to the stage.
So Matthew talked a lot about how we can use the power of SwiftUI to create amazing interactions just for Apple Watch. But, as we said, the experience on Apple Watch is about much more than just the application.
To build an experience on Apple Watch, first I want to dive into interactive notifications.
Now, interactive notifications on Apple Watch are the primary way which you have to provide timely and contextual information to your users.
On Apple Watch, notifications are made up of two primary parts. The first is the short look.
Now, a short look appears immediately upon registries and will provide information directly from the notification payload. This also includes your application icon automatically.
After looking at the short look for a period of time, it will transition automatically into the long look. The long look is a scrolling interface which allows you to specify both a custom body as well as these notification actions below.
Here you're seeing a beautiful implementation from Yelp where they've created a complete reservation confirmation experience completely within their notification.
To see how we can extend Pop Quiz to really live up to its name, let's add a notification which will place a flashcard right into the body to make sure I'm keeping up on my studies. So where I want to start is in the notification controller. The notification controller was provided by the template when we created this project by just selecting the include notification checkbox.
Very much like the interface controller, this is new, it's part of SwiftUI, but it comes from the WK user interface controller underneath.
So this new user notification hosting controller provides the entry point for interactive notification and allows us to provide SwiftUI content. If you've used these before, it'll be very familiar and you have the same did receive method coming from the user notification framework. The did receive method allows us to extract information from the notification, store it within our interface controller, and also provide those notification actions that appear below the notifications' body. And then much like the interface controller, we have the same body property.
The one difference here is, after did receive is called, we'll automatically invalidate and reevaluate your body property so that your notifications' view is completely up-to-date.
To see what this notification looks like, let's jump in to the notifications' view.
Now, here we'll have a preview of what this notification appears, but it's not really super useful if I'm trying to study. Here at the bottom we can see a flashcard view which is showing you both the question and the answer.
To build this out and make it feel a little better, I want go and pin this preview, using the pin button in the bottom left of the canvas, and then go look at the flashcard view.
And so now we can see I'm building my flashcard as well as seeing what it will look like in context when I'm all done. So I've already created some views which I can place into my flashcard to make this look a lot better.
So the first thing I want to do is replace both of these texts by wrapping them within this side. Now, a flashcard has two sides. So let's wrap this in both sides. And we've gotten this really nice chrome around my flashcard and all the default styling that I'd want for any content which I put inside of them. But I can still see both sides at once, and I'm really trying to get better at studying.
So I want to replace this VStack with a custom container which I've written, which I call Flip View.
Now, the Flip View provides all of the transformations as well as the interactivity, which we're going to look at in a second. And all of the source code for this will be available after the conference, a sample code.
So to see what the notification looks like in practice, we can build and run using the notification scheme which was included as part of the Xcode project. And we'll see that the short look appears. We transition directly into the long look, which looks exactly like our preview like we'd want. We get these notification actions below. And as promised, the notification is fully interactive. I can tap the flashcard to flip it over or even drag to interact directly within the notification body.
Now, that was really easy question, and I definitely got that one right. So we've seen with taking advantage of the same technology that we've used to build the application, we can create rich and interactive experiences within the notification.
Finally, there's one more piece of the Apple Watch which I'd like to talk about, which is the digital crown.
Now, you've already been able to take advantage of the digital crown within your WatchKit applications using the WKCrownSequencer API.
This API allowed you to do some very limited things with the crown, digital crown, but we want to be able -- we want you to be able to create interfaces much more like the ones which we create in our own applications. With Apple Watch Series 4, we introduced a brand-new haptic crown. The haptic crown, digital crown, provides feedback as content scrolls on the screen. This provides some really nice resistance and weight to each element, allowing the content to feel more connected to the digital crown than it ever had before.
We've already taken advantage of this all across our own applications. For example, in workout, where we have this beautiful list where the elements have a lot of weight to them as they scroll by. Or when you're creating a custom timer and you have these pickers where as you fly through, it provides haptic feedback.
In SwiftUI, we've provided system components that give you this all out-of-the-box. For example, list, picker, and scroll view will do all of this for you.
But if you're going to build something a little more custom, we have some new modifiers as part of SwiftUI that allow you to realize these visions.
The first kind custom interface I want to talk about is what we call a free scrolling interface. In a free scrolling interface, you don't really have concrete stops between your content, and instead you want to be able to stop anywhere along the sequence which the digital crown is moving.
To implement something like this, you use the new Digital Crown Rotation Modifier, provided by SwiftUI. In its really simple form here, it only takes three parameters. The first one we need to worry about is the binding.
Now, like any other SwiftUI component which takes a binding, this provides the source of truth for how this modifier interacts with the system. In the case of a free scrolling list, we might want to bind to the offset of the view which we're moving.
We also need to tell the digital crown where we're going from and where we're going through. These define the limits of the sequence over which the digital crown will move.
To see what this looks like in action, we can see that now as we move the digital crown represented by the red dot, we'll provide linear feedback as well as some nice haptic feedback in a rubber banding behavior at either limit of the digital crown's sequence. Next, if you wanted to build something a little more custom -- for example, like this picker, which we've provided as part of -- which is included in the new calculator application. We're here, we're no longer scrolling content. Instead we're picking between discrete elements and using the digital crown to move from one to the next. We can use the exact same Digital Crown Rotation Modifier with many of the same parameters. Now, these parameters -- instead of going over scrollable lists, we're selecting between the number of people and going from 1 through 15 of them. Now, the new parameter here is the by parameter. And by allows us to specify the stride along which the sequence, the digital crown sequence, will provide that haptic feedback and settling behavior for you. To see what this looks like, we can see now that the digital crown will provide haptic feedback as we go from one element to the next, and it's just that easy. And finally, if you wanted to do something even more custom -- for example, the interface that we've had in alarms. Where here the digital crown is controlling a view which is moving around a circle. It's no longer limited at either end of its sequence.
Here, we come back to the familiar Digital Crown Rotation Modifier. This modifier takes all of the same parameters which we've already been talking about, as well as I want to introduce two new ones. First is sensitivity.
Sensitivity allows us to specify how much rotation we need to apply to the digital crown to move from one element to the next. And finally, we have the continuous parameter. This parameter allows us to specify that we no longer want the digital crown to stop at either limit of its sequence. In action, we can see now we have this really nice interaction where we can go all the way back around and continue moving the digital crown in one direction. There's no more limits to the sequence.
Finally, to let the digital crown get its input, we need to tell the system how to get it there. To do that, we introduce the focusable modifier. Now, focusable will feel very familiar to you if you've used other UI frameworks which we provide. This is a lot like the UI Focus Engine in UIKit or First Responder, which I'm sure everyone here has used before.
And by just adding this focusable modifier, we can now use the digital crown to update the people picker, tap over to tip, and continue using the crown to update the now focused view.
To learn a lot more about how focusable works, please go check out the SwiftUI in All Devices talk.
To see how we can take advantage of this and to build some really awesome interactions in Pop Quiz, let's go back to demo. So Matthew had already shown the really nice card list at the entry point of the application -- or the topic list at the entry to the application.
But I want to build out a really cool card list. Now, we already have a nice list here where we're seeing these flashcards in a scrollable view, which we can still see in the preview. But wouldn't it be nice if we had a really cool, custom interaction driven completely by the digital crown? To do that, what I first want to do is replace the scroll view with a ZStack.
Now, we'll see that flashcards are no longer stacked vertically but instead stacked one on top of each other. But I promise you that every one of them are still there. Next, because we're going to be building something using the digital crown, we need to supply the focusable modifier as well as the Digital Crown Rotation Modifier.
Now, there's a lot of parameters here, but these are the exact same parameters which we've already just talked about. So let's go through them one by one. First we're going to need to supply a binding, or the source of truth where the digital crown will be updating its value.
To do that, we need to provide some state.
Here, I want to provide the current index into which this custom interaction will be moving between. Next we supply that binding to that state back into the digital crown rotation. Here, I want to use an animated version of that binding, so as the digital crown updates, it refreshes this view in an animated fashion.
Next we need to specify how far we're going from as well as where we're going through in the sequence. Here, we're going to the second to the last card in the sequence to achieve the effect which we're going for.
Next we need to specify the by value, which is the stride between each of these cards, as well as the sensitivity. Which here, I want to specify a low sensitivity so that each card gets a lot of weight to it and I can make sure I'm looking at the card which I want to look at. And then finally, to actually get the nice effect we want to look at, I've already written this Card Transform Modifier. Now, this is a custom view modifier which I've already written, which will provide all of the 3-D effects and allow the card to update with the current index. And we're passing in the current index into this modifier so that it's updated every time this view is re-rendered. And that's it.
Now we can go build and run this application. And as it comes up in the simulator, we'll see the same view which Matthew had already showed us at the beginning.
We can go through this list, select what we want to see, and see that now we have this nice interface which we can scroll through using the digital crown.
And that's it.
And if anyone does know the answer to this question, please let me know, I'm really looking to get the answer.
So that was awesome. We've seen how we can build some really great experiences using SwiftUI and to take advantage of everything that the Apple Watch has to offer to get us there.
But there's more to it than that. Now we have the same UI framework across all of our platforms. You can take the same concept you've learned building your awesome iPhone application and apply them directly to building experiences on Apple Watch.
We really can't wait to see what you build now that you can make the interfaces you've always wanted to make on Apple Watch.
There's a ton of great sessions to talk about SwiftUI at the conference this year. And to learn more about how to make those 3-D animations, definitely go check out the Building Custom Views with SwiftUI talk. Enjoy the conference. Thank you.
[ Applause ]
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.