Xcode 11 displays previews of your user interface right in the editor, streamlining the edit-debug-run cycle into a seamless workflow. Learn how previews work, how to optimize the structure of your SwiftUI app for previews, and how to add preview support to your existing views and view controllers.
Good afternoon. And welcome to Mastering Xcode Previews. You have probably seen Xcode Previews in multiple places through the week: in the keynote, in the State of the Union address, and multiple sessions focusing on SwiftUI. But today we are going to focus on previewing - on Xcode previews themselves.
My name is Anton. And a little bit later I will be joined onstage by my colleague Nate to talk about what Xcode previews are, how they work, and how to configure them to take advantage of them in your workflow -- whether you are working on a brand new application using SwiftUI or an existing app using UIKit or AppKit.
So let me start with a little story.
Let's say a designer comes to you and brings a mockup for a screen that they have been working on for your Disney application. You take this mockup, and you take it to your desk, and you work on it. You build it. And you bring it back to show your designer your work.
You run the code. You navigate through a bunch of screens that you have already built before to this new screen, show it to them. And they say, "Hey that looks pretty good. But now that I see this thing in the flesh, that button, that could be a little bit more blue." That - that's no big deal. You quit the app, you open Xcode, you make the edit that you want to make. You run it again. You navigate through a bunch of screens to the new screen that you have just built. You show it to your designer. And they go, "Hey, that's much better. But the padding on that text is - it could be a little tighter, don't you think?" You get where I'm going with this story. This sort of iteration, this back-and-forth, is a normal process. It happens to all of us. And this is how we get great-looking apps. But this is something that's also potentially time consuming. And the time-consuming bit here is not the iteration itself. It's not the making of the small changes here and there. And it's not the getting of the feedback. It's the building and running and configuring and navigating and getting your app to the state where you can verify that the changes that you are making have the results you expect.
So let me give you another example.
So let's say your designer - you and your designer are now done, and you are happy with the view that you got. And you take it to your desk. But your work as a developer isn't done yet because, as developers, we want to build well-behaved apps. And a well-behaved app means that your new view, it needs to look good no matter what configuration your user is running it in. So for example, it needs to look great in dark mode.
Or at a different dynamic content size. Or maybe even on a different device.
And there is a real tension between verifying that your UI and your app looks as good as it can possibly look under a variety of circumstances. And, you know, actually shipping your app to the App Store that we as developers are all familiar with.
So the Xcode team has thought about this problem for a while. And the solution that we got - came up with is Xcode Previews. Now Xcode Previews is a new feature of Xcode that allows you to - that institute - minimize the amount of time you spend building and running and configuring your views to verify the changes that you are making. And to - and lets you focus on the things that you love doing best, which is building great apps.
But the question is, how? How does this work? How does it do it? Well when you enable Previews in your application, Xcode actually builds your app in a special way.
Since Xcode knows what view and what file you are currently working on at all times, it can compile just that file, just that view, separate from the rest of your application -- and then inject that implementation back into your application using Swift's dynamic replacement feature.
And because the amount of code that needs to be recompiled for every change is significantly smaller than the entirety of the rest of your application, Xcode can continuously and repeatedly do this for every change that you make.
And that means the feedback on your changes is much, much quicker.
But there is a whole class of changes that Xcode can optimize even further. So for changes that involve only the literal values, like strings or numbers, no recompilation is required at all.
That - Xcode just injects the new value into the running application, and you get instant feedback much like you get from a visual editing tool.
And it's important to note here that the Xcode Previews is not a representation of what Xcode thinks your view will look like when you run the application.
It is actually building and running your code.
So all the context that is normally available within your running application is available to the view under Previews. If you have custom assets, you can use those within your preview and they will render. If you have custom logic in your app that you want to use -- which of course, you know, every app would have custom logic -- you could use that as well. And even runtime configuration, something like "Set user default," is available to your views on your Previews as well.
So now the question is, how does Xcode know what to show you? Well it turns out all you need to tell Xcode what view to preview and how to configure the data is a little bit of code. And all you do is you implement a small type that conforms to a PreviewProvider protocol, which is a part of a SwiftUI framework.
And that particle has one requirement, just a Previews property that you implement. And then you return some content. And really, what your return out of here is completely up to you.
And because this code lives as a part of your application as well, it's compiled alongside the rest of your application code. The same story applies for the configuration code as it does for the view. So you also can use custom logic in here and custom resources and even runtime configuration. There are several advantages beyond that to using code to configure your previews.
For instance, since this is just SwiftUI code, this configuration code is just SwiftUI. If you know how to create SwiftUIView, you already automatically know how to configure a preview for it.
Also, you can write a preview once, put it in your source control, and share it with your team. So that means you write a preview, and the rest of your team can benefit.
And then finally, as you make changes, the - our views are always evolving. Our apps are always evolving. As the view changes, and its API changes, the compiler can help you make sure that your preview stays in sync with the view so that you always know that you are previewing the right thing. So that's a brief introduction into how previews work.
To tell you more about how to configure them to take advantage of them to the fullest, please welcome Nate. Anton told you that previews are code.
I would like to show you how to write one.
The Xcode Previews team has decided to take a trip to the zoo.
There are a lot of animals at the zoo, and we want to make sure not to miss even one.
Naturally, being the app developers that we are, we couldn't help ourselves but had to build an application to track the animals at the zoo.
I have been working on that application. In particular, the main user interface for it is a list of animals at the zoo.
In that list is displayed a number of animal cells.
Let me show you what I have got so far.
In the canvas, you see a preview of the animal cell. Because the run destination of iPhone 10S is selected, that's the chrome we see around our animal cell view. But we want to make sure that our app - that our view looks great on all devices. In particular, my daily carry is an iPhone SE.
Now I could change the run destination to iPhone SE, but there is another mechanism provided by the Previews API -- specifically the preview device.
You pass to that method the name of the device you want to see a preview on.
And almost immediately in the canvas, you see the device displaying your view. Now as I am looking at the device, this iPhone SE showing the animal cell, I am seeing a problem.
The rightmost text is truncated. Let's go ahead and fix that.
To do so, I'll change this HStack into a VStack.
Immediately, the canvas updates, showing us exactly what we want.
Now Anton's daily carry is an iPhone 10r. To make sure that his device shows this cell well, we could change the argument that we are passing to previewDevice. But there is an easier way.
Specifically, we can embed this view in a group.
To do so, Command-click on the AnimalCell and scroll down to Embed in Group.
Then add a second instance of the animal cells to it, this time calling previewDevice with iPhone 10r.
Now we see, in the canvas, a preview for our cell running on two devices.
Scientific names of animals are often written in italics. Let's go ahead and make that change right now.
Now in the canvas we can validate that our change looks correct on both devices. This is so easy. It's great to be able to see our views previewed on the devices that our users are actually using. But for smaller views like a cell, it's also great to be able to focus in on the details we really care about and to strip away that extraneous chrome.
To do so, the Previews API provides the method "Preview Layout." Now the Preview Layout method can be passed three different arguments. First, you can specify the device. That's actually what's being used by default and what's displayed in the canvas right now. Secondly, you can use a specific size that you want your preview to be displayed as. This is super useful if you know that your view is going to be embedded in some larger view at a particular size. And finally, you can use Size That Fits. This is useful if you want your view to be able to determine the size of the preview based on its content.
Since an animal cell is going to be embedded in a list, and a list allows its entities -- the cells displayed within it -- to be resized vertically according to their contents, we are going to go ahead and use "Size That Fits." And immediately, the canvas update, showing us just what we want. I am going to go ahead and zoom in by clicking on the plus sign in the bottom right of the canvas.
You know, the two texts, the red fox and the family name below it, Canidae, are a little too indistinct. I want them to be visually distinctive, one from the next. So I am going to go ahead and make the red fox be bold.
To do that, I'll Command-click on the Text and scroll down to the Inspect action, then change the weight to Bold.
The source editor gets new code added to it, and the previews refresh in response.
This looks great.
A lot of users like to take advantage of dynamic font sizes. Let's take a look at what our view will look like when the user changes dynamic font size to extra-large.
Because the preview of API is part of SwiftUI, we can take advantage of all of SwiftUI when we are writing our previews. In particular, in this case we can use the environment modifier and specify the value for the Size category keypath to be extra-large.
Now it's great to be able to so easily change the size category that we are seeing our view at. But what would be even better would be to be able to see our view displayed at every single content size category at once. What we could do is embed this view in a group and, and for each different one, create a different animal cell. But that's very repetitive, and there is a better way.
SwiftUI has the for each feature. To use it, I am going to Command-click on the animal cell, scroll down to Repeat.
And now, for each of the integers from 0 to 5, we get a preview. But what I actually want is for each of the Content Size category cases to have a preview, and for each of those cases to specify into the environment the value for the size category keypath to be that case.
Just like that, you can see in the canvas your view displayed at every Content Size category. Now that we have done this, one problem I am noticing with the extra-small content size category is that the title -- the common name -- really isn't popping out as much as I'd like. Let's go ahead and bump that up a notch. I am going to Command-click on the view, this time in the canvas; again select the Inspect action; and change the font to Title.
Code is inserted in the source editor, and we get an update in the canvas. This looks great.
One problem you may encounter when you have a number of previews in the canvas like this is that it's difficult to distinguish between the different previews. You need to know which configuration in your code, in your implementation of the Previews static property correspond to which preview in the canvas.
To help out with that problem, the Previews API provides the method "Preview Display Name." In this case we want to use the name of the content size category we are displaying.
And right below each preview, we see the content size category. Yeah. This is really great.
The Previews API is concise and extremely powerful.
To tell you about how to use Xcode features to make the most of your previews, here's Anton.
So I am going to jump in into this demo and finish building the animal cell that Nate was working on. And the first thing I am going to do is I am going to focus on this Red fox image placeholder. Now when we shipped this application, we expected data to come from a network service that we are going to provide. But we haven't built the networking code yet, and so I don't have the data to populate my view. And so I am using placeholder data here, and it's easy enough for strings like we do for fox here. But for images the story is a little bit more complicated. That's because I could add an asset that's a placeholder asset for use in my previews.
But that would mean that I would have to then ship this asset with my application to my customers, and that's not something I really want to do. And I don't want to pollute my buyer . And so this year Xcode has a new feature that's going to help us with this. And it's called "Development Assets." And I'll show you this feature now. So I will open the Project Editor. And under Targets in general I will scroll down to Development Assets. You will notice that I already preconfigured Preview Assets as a catalog here which will hold all my images. If you are starting from scratch and you are using one of our new templates, this will come preconfigured for you. But if you are working on an existing applications, you can easily add this here.
So now that we know that I have these preview assets in an asset catalog, I can show you that under my Preview Data. Here's my asset catalog. And in it I already have an image for the red fox that I was going to use as a placeholder.
So I will open my animal cell and - for us to preview. And since the placeholder here was using an image name already, all I need to do is change this text to Image. And the image immediately shows up in all my previews. This is looking good. But I would like to give it some visual treatment to make it stand out a little bit. And I can do that with a little bit of SwiftUI code.
OK, so this looks good. There is some shadows and some overlays. This looks nice, but look what happened to my code now. Most of this view for the cell is now sort of dominated by this image treatment code.
I am probably done with this treatment, and I don't really want to lose the details here. So I am going to extract this by Command-clicking on the image.
I am clicking on the image and picking Extracted View.
And Xcode locates a new SwiftUI view in the same file down below, so let me rename it. I will call it "Animal Image." And I will give it some argument, which is my image name. And if I scroll down here, here's my animal image.
Easy enough. I will wire in my input. And I have refreshed my preview. Yes, thanks. I think this looks pretty cool, too.
So notice the preview really didn't change. But if I scroll back up and look at my - what my animal cell looks like, it's much clearer now what's going on here and I can focus on the details other than the image treatment. So now that this looks good, notice that we have so far focused on this view just by itself. But it never actually appears this way to our customers. The only way our customers will ever see this is in the context of the list that this will appear in. And so I would like to work on that now.
So I already started working on that in this animal list file. And we didn't have the animal cell before. So I just created a simple list that accepts a - some animals -- and for each one displays a cell here. So I am just going to replace this code with a new animal cell that I just built. And here it is; that shows up.
That's closer to what our customers will see. But there is really not a lot of zoos around with one animal. So I'd really like to populate this list with some data that's actually closer to what my customers will see. So how do I do that? Well if I scroll down to my Previews configuration code, you will see that I am populating my animal list with just an array of the one element. So, you know, one thing I have to do is simply - you know, I have this hard-coded thing here. I can just add a couple of these. And now three cells will show up.
That's closer. But then I can keep doing this to fill it up. But it's very uniform, and that's still not exactly what it will look like to my customers. Some of the animals' names are going to be longer and shorter, and the colors are going to be different. I really want to get a good idea of what it - it's going to look like.
So what are my other options? Well one thing I could do is I could - since this preview code runs as a part of my application, I could actually spin off my networking code, hit my network, and get some data and populate my preview. I could do that. There is a couple of problems with that approach. First, I didn't build the networking code yet. So I can't really do that. Second, if I did, that - still be a problem because that would mean every time I work on this view, I would have to be online and my service would have to be available to me.
And that's just not a good way to work. What, instead, I want to do is I want to get some data and cache it so I can visualize this without requiring all the rest of the components in my application. So I did this a little bit earlier. And I created a cache which is just a JSON file. Let me show you. Under Preview Data here is a simple JSON file that just lists some animals to populate this data. And so what I can do now - what do I - I don't really want to ship to the customers either. So a little bit later I'll go and add it to my development assets. But I'm going to skip that for now. So now that I can - that we know that we - this is here, all I need to do is just load this. I am going to add a little bit of code in my previewing code to load this JSON out of my main bundle, and use a JSON decoder to parse out my data. So now I have an array of data which I can -- let me close this so you can see -- I can replace with Self.sampleAnimals.
And when the previews refresh, here's my UI populated with my sample animals. Thanks. So this is a lot closer to what my customers will actually see, and I immediately see a couple of issues with this layout here. For instance, the "Snow Leopard" here is very long, and it's clear now that I probably should realign this. The other thing is, because these titles are kind of large -so you look now but there - it could be a little bit airier area here. I want to add some padding on the top and the bottom. So all of those changes belong in the animal cell. So I am going to navigate there to make those edits. And look what happens.
My preview that I was just looking at disappears. I am back to the context of the cell. And so my list went away. What I really want to do is I want to make my edits to the cell while looking at it in the context in which it will actually appear, which is the list.
One thing I could do is I could just add another preview here that just previews the list. Like I told you before, you could really provide any content as a part of your preview.
But that would mean anytime I am looking at the - at anything that has to do with this, I would have to duplicate the same code over and over again. So there is a better way.
And let me show you what you can do. I will navigate back to Animal List, and I will see the preview for my list like I saw before. And it'll use this little Pin button in the bottom left corner. And I'll click it. And what this will do is it will pin the current preview to this preview; and as I navigate in a different files, notice what happens.
This preview stays up. Thank you.
There is a little header here that tells me "Hey, you are not looking at exactly the preview that you have from this file before. This comes from Animal List." So just so you know. And if I wanted to get to my original previews from this file, I can just scroll down and all of them are still here. So I didn't any context at all. So now that I know what I need to do, I can Command-click on my VStack and call Inspect. And it can inspect and change my alignment here, give it some padding.
Let's see what happens.
This - this is a little bit too much. So I am going to get back to this and give it a little bit less padding, maybe.
And it looks great. I like how this looks. So I am going to keep it this way. The next thing I want to work on is a list of actions that you can do - that you can use on each cell when it's selected. Now I already started working on the selected cell implementation in another file called "SelectableAnimalCell." Because I don't want to pollute my data -- my animal cell model data -- with something that comes from the - something that's transient from the UI, I added a little wrapper that just wraps any data and gets us - and adds a selected fit to it. And I am using this, and it's another view called "Selected" - "SelectableAnimalCell." And all it does is it accepts a model, displays my animal cell, and then checks the selected bits and shows my actions. And those are, you know, little icons that will let me get more information and take a picture. And if I unpin this preview, you will see I have already built two previews for this cell: one for the selected state and one for the unselected state - flip: one for unselected, one for selected.
So this is a great way to work. But again, I want to see these in the context of my list. So I am going to add Selection to my list now with - so I am going to navigate to Animal List.
And the way SwiftUI handles data like this is with this - with a State variable. So I am just going to add a space here that stores some data about what's currently selected in my table -- and then another little bit of code to translate, because this data is specifying the selected model but each cell takes a boolean whether that cell is selected or not. I am going to add that here as well.
Now that I did these two things, I am going to replace my animal cell with this selectable animal cell. And that's really all I need. I can refresh my previews, and you can see nothing really changed.
Well why is that? It's because the selection here is optional. By default nothing is selected in my list, and I can't see the things that I just did what I did. Now of course I can click the Live Vote button, take this UI.
And if I click around, the selection will appear -- just like I expect.
But what if I wanted to build a static - thank you. But what if I wanted to build some static previews here that - to remind me that there is such a thing as a selected state for the cell, so that when I make changes to all of this I don't forget to verify what my selection looks like with my changes? So I could make a static preview here, but this would require some changes to my code. And the reason is because this bit of data is private. I can't really modify it from my preview. So what can I do here to actually display this preview? Well what I am going to do is I am going to promote this to a binding that I can then set. And I am going to do this by introducing a new view. I am going to create a private view inside this animal list. It's just going to house all of my implementations. I am going to call it "Content" because it's private and it's a - only available inside this file. And then I am going to take all of this implementation that I just built, and I am going to move it inside the view that I just declared. And then the final bit is I am going to add the implementation of body for my animal list in terms of the content. And what this does is just substantiate the content view that I just declared - that's just the animal -- and creates a binding to the selected state.
Now what I can do is I can change my previews to instead of previewing Animal List, previewing Animal List content.
And when I refresh, you will see that the selection actually appears; and I get a static preview with my selection. Now I tapped Animal List Selection - or Animal List Preview here just so I can also verify what my list looks like without the selection. And also I can still use Live Vote here to verify my transitions and animations and work with my previews that way. Thank you.
So, so far we have been looking at Xcode Previews and how they work with SwiftUI. But they can do a lot more for you for your application. And to tell you more about that, please welcome Nate. Thank you Anton.
Not all of you are going to be writing brand new applications starting this week using SwiftUI.
Many of you have apps in the App Store and apps you have been working on for years.
Because SwiftUI has rich integration with UIKit, AppKit, and WatchKit, and because the Previews API is part of SwiftUI, Xcode Previews works great with all of those frameworks as well.
In our zoo application, when you tap on the Info button that Anton added to the bottom of a cell, we are going to display a detail screen for the animal that's selected. It turns out that that screen is written as a UIViewController called "Animal View Controller." Let me show you the code for it. First, I am going to go ahead and close the canvas by pressing Command-Return and then switch over to the Animal View Controller.
I'd like to show you what this view controller looks like. But rather than building and running, I am going to go ahead and create a preview instead.
Now previews are just code, so we absolutely could go ahead and create the preview right alongside this UIViewController in this file.
In this case, though, I do want to create a new file. I am going to open the project navigator pressing Command-0 and then create a new file in the group that I want, right-clicking Selecting New File -- SwiftFile -- and entering the name I want: "AnimalViewControllerPreviews." The Previews API is part of SwiftUI. So to write a preview, you will need to import that framework. To create a preview that's displayed in the canvas, you need to add a conformant to the preview provider protocol.
In this case, I am going to create a brand new type to conform to that protocol, namely, "AnimalViewControllerPreview." Now the preview provider protocol has a single requirement that you must implement, namely, the Static Previews Property.
From this property, we need to return some view. And in our case we want some view that embeds a - our AnimalViewController.
SwiftUI has rich support for embedding UIViewControllers into SwiftViews in the form of Representables. To learn more about that, check out the session "Integrating SwiftUI." In our case, because we want to represent a UIViewController, we want to add a conformant to UIViewController representable.
And for expediency, I am just going to go ahead and add that right alongside PreviewProvider to the AnimalViewControllerPreviews type.
Now for our purposes, the UIViewController representable protocol has three requirements that we need to implement. The first is to specify the type of UIViewController that's being represented.
In our case, that's the AnimalViewController.
Secondly, we need to describe how to create one of these AnimalViewControllers from the context it exists inside. That context includes things like whether the user is running in light or dark mode and what the dynamic font size is.
In this case, I have already added a little wrapper class around UIStoryBoard; and I am loading an AnimalViewController from it.
The third requirement that we need to implement is updateUIViewController.
This allows us to change the appearance of our view controller based on changes in the environment, among other things.
In our case, we just want to create a preview.
So I am going to go ahead and leave this implementation blank. Now that we have a conformants to the UIViewController representable protocol, we are ready to implement that Previews method.
Specifically, we'll return an instance of the type that conforms to UIViewController representable, namely, AnimalViewController Previews.
With just those few lines of code, you can see a preview for your UIViewController in Xcode. Let's take a look at it. I am going to close the project navigator and open the canvas by pressing Command-Option-Return.
Because we added new methods in the source editor, we will need to rebuild.
And there, in the canvas, is a preview for our UIViewController written in Swift. Thank you. Yes. This is really fantastic -- a preview for our UIViewController written in Swift against UIKit. But as I am looking at this ViewController, I am noticing a problem. This text is awfully difficult to read.
There is an underlay view beneath the text above the image. It's semi-opaque, so that the text should be readable. But clearly it's not opaque enough. So let's go ahead and crank that opacity up enough. I am going to pin this preview so that we can keep looking at it as we make the change, and switch over to the AnimalViewController file.
And in this file I'll change the alpha of that underlay view to 0.5.
Immediately, the canvas updates, showing us our preview for our ViewController with that change.
Previews in Xcode work not just with SwiftUI but with all Swift code. And moreover, we get all of the hot swapping and incremental updates without recompilation for literal values that Anton told you about -- whether you are working with SwiftUI or UIAppKit, or WatchKit. Thank you.
Anton showed you the AnimalActionViewController. I have been working on that, so I already added a preview for it. Let me show it to you. I am going to go ahead and unpin this AnimalViewController preview, clicking the Pin button in the bottom left of the canvas, and then switch over to the AnimalActionsViewController representable.
And there is a preview for this AnimalActionViewController.
The team is discussing the button on the far right there, and we feel that IImage is not quite appropriate. It's not quite consistent with the platform. Let's go ahead and fix that now. I am going to open the AnimalActionViewController file.
And, you know, I was taking a trip down Memory Lane when wrote this, so I went ahead and did it in Objective-C. Thank you. Thank you. It's a great language.
And I'll change the system image that we are populating the Info button with to info.circle.
And I'll switch back to our preview and refresh the preview. Because this is written in Objective-C and not Swift, we will have to - Xcode will tell the build system to kick off a normal rebuild rather than using incremental updates or hot swapping. But still, that's usually very fast. And specifically, it's much faster than rebuilding and running and navigating into your application.
Xcode Previews works not just with SwiftUI and not just with Swift files, but even with your Objective-C code written in UIKit, AppKit, and WatchKit. And moreover, it works with all of the source files that Xcode understands.
You have seen how Xcode Previews can really accelerate your workflow.
But I would like to talk to you now about how you could tweak the application - the architecture of your application to make Xcode Previews work even harder for you.
Specifically, I would like to talk about one of the types that appeared in the zoo application, namely, AnimalCellModel. What were we doing with that thing? Well we were populating our AnimalView -- our AnimalCell.
Because of SwiftUI's fluent API and - we are able to implement this body method very concisely.
And thanks to using a cell model, it's extremely explicit what data is going into what field on the view.
With that said, we could have actually used an animal model object and passed that directly into the view.
We didn't, for two big reasons.
The first is that using a view model makes it way easier to add new previews.
A good pattern to consider for your PreviewProvider implementation is to add a static array of view model. And in your implementation of the Static Previews property, to iterate over that array of view model and populate your view.
This is useful when you encounter a new data configuration that will result in a different view layout.
All you need to do is create a view model and add it to that array.
Then you'll get a new preview in the canvas showing your new view layout.
Before getting to the second big reason that we decided to use view models in the zoo application, I want to wax a little philosophical for a moment if you'll bear with me.
The model layer of your application includes a rich object draft.
Even in our zoo application, we have a zoo model object.
And that zoo model object is only one among many of the animals in a clade. On the other side, that animal model object has within a genome. How are you going to display that information to your users? Well, not all at once. For example, you don't want to add a genome to your animal cell. That's too much information.
Instead, you are going to extract just the most important pieces of information from your model object and create your view using them.
And you won't just extract them in a one-to-one manner.
Often, you'll perform some transformations on the data.
Here we took two of the fields from the animal model object and combined them to create a single text containing the genus and species.
That transformation and extraction process is error prone.
But using a view model makes it explicit.
Here is a view model -- our AnimalCellModel in particular -- being constructed from a model object.
We can clearly see the data being pulled out of the model object and combined so that we can show exactly what we want to, to our users.
And having this be explicit is invaluable because it makes it so easy to test. Whenever we encounter a new flavor of model object that needs to be represented to our users in a different manner, all we need to do is leverage XCTest. Add a new test case, and in that test case create one of our model objects; create from it a view model; and validate that our view model has exactly the data that we want to show to our users to represent that model object.
This is the second major reason we decided to use view models in our zoo application.
What does this mean for your overarching application architecture? Well a big part of building great apps is to take this rich model object graph that exists in your model layer and to represent it to your users, pulling out just the most important pieces of information and showing it to them in a beautiful fashion.
Using a view model can make this so much easier.
The reason is that you can use the following flow: First, for your model objects construct view models.
That process is error prone. So you test, and you test, and you test, and you make sure that your view models contain exactly the data you want to show to your users. Second, for each flavor of view model representing - resulting in a different view layout, you add a preview. And you can verify, just in a canvas, that your views are displaying exactly the data your users want to see.
Finally, you can use XCUI testing to validate the way that your application works in production. I'd like to take a moment to talk about performance. We have told you that previews are just code.
One consequence of this is that you don't want to run arbitrary operations when your previews are being rendered. You know, you don't want to calculate the millionth prime.
Similarly, Xcode Previews will call Applicationdid /FinishLaunching /WithOptions.
Consequently, you don't want to do a bunch of extraneous work there. And specifically, you don't want to set up your UI hierarchy when all you want is to see your - a preview of your view rendered in the canvas.
Instead, make your application scene aware, conform to the UIWindowSceneDelegate, and implement the scene willConnectToOptions method and set up your UI hierarchy there.
And look: This isn't just beneficial for you, the developer, OK, it's faster previews as a result. This is also a boon for your users.
When your application launches into the background, it won't be doing a bunch of extra work.
It'll just do exactly what it needs to and go back to sleep, avoiding wasting your user's battery life. Let's recap here.
You have seen how to write a preview. The preview's API is part of SwiftUI. It's really powerful.
And you can use all of SwiftUI when you are writing your previews.
You have seen how to use advanced Xcode workflows, like preview pinning, to keep your state when you are switching between different files.
And you have seen how to use development assets, allowing you to have rich and interesting previews without shipping them - without shipping test data to your users.
Finally, you have seen how to use previews not just with SwiftUI and not just with Swift, but with all of the source files that Xcode understands written against UIKit, AppKit, and WatchKit. Well that's Mastering Xcode Previews. Thank you all for coming. Come see us in the lab.
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.