Transcript
[ Music ]
[ Silence ]
[ Applause ]
>> 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
[phonetic] 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.
[ Applause ]
>> 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.
[ Applause ]
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.
[ Applause ]
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.
[ Applause ]
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.
[ Applause ]
>> 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 [phonetic].
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 [phonetic] my
input.
And I have refreshed my preview.
[ Applause ]
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 [phonetic]
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
[phonetic] 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.
[ Applause ]
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 [inaudible] 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.
[ Applause ]
Thank you.
[ Applause ]
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.
[ Applause ]
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 [phonetic] 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.
[ Applause ]
Thank you.
[ Applause ]
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.
[ Applause ]
>> 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
[phonetic] 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.
[ Applause ]
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.
[ Applause ]
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.
[ Laughter ]
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.
[ Applause ]
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.
[ Applause ]
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.
[ Applause ]
Well that's Mastering Xcode
Previews.
Thank you all for coming.
[ Applause ]
Come see us in the lab.