(regal music)
- [Announcer] Stanford University.
- Welcome to Stanford CS193p.
This is spring quarter of 2020.
I'm Paul Hegarty, and I'm
going to be your tour guide
for this adventure into
developing applications
for iOS using SwiftUI.
And it's gonna be quite
an adventure this quarter
because none of you are
here on campus with me
which is kind of sad for me.
It's gonna be a little bit strange.
I'm sure it's gonna be
very strange for you all.
And it's not really
gonna affect us that much
in this course, believe it or not,
because most of the communication
that happens between
you and me is happening
via our class forums, Piazza.
So you definitely wanna get on Piazza
and ask questions there.
Don't be shy about that.
We get very quick responses here.
See what your fellow
students are asking also.
And definitely be
looking for posts from me
because all course
materials, all announcements
are gonna be made through Piazza.
So don't miss that part of it.
Now, all the lectures
are gonna be pre-recorded
just like this one.
And that's gonna be
convenient for you I guess
because after they come out,
you can watch them at your leisure
and fast forward and rewind.
And especially since I'm going
to be doing a lot of demos,
that is gonna be really valuable.
I guess it's also gonna
have a great benefit
which is that as we do
many times in this course,
we release the course out
into the world for free.
So people can watch this.
Spy on us basically here
as we're doing this course at Stanford.
And we'll be doing that again
after we do the standard closed captioning
and other post-production work.
So if you're watching me outside Stanford,
welcome to you as well.
Now, of course you're not
gonna be able to participate
in the forums with the rest
of the Stanford students
because you're watching
this a little bit later.
But there are community-based resources
for asking questions
out there in the world
like Stack Overflow.
So definitely go and
check those out as well.
This SwiftUI thing is brand new,
literally just a few months old.
So you are on the cutting
edge of technology
when it comes to developing
applications for iOS.
And as you learn this,
some things are gonna be familiar to you.
There's a little bit
of object-oriented programming in there.
This language that you're
gonna learn is kind of C-like.
It shares some syntax with
languages like Java as well.
So that'll be familiar.
But you're probably gonna be learning
some pretty much brand
new stuff for you as well.
For example, you're gonna be learning
a new programming language, Swift.
Entirely new for almost
all of you, I'm sure.
And this programming language,
while it supports
object-oriented programming,
it also supports a different
kind of programming
called functional programming
or protocol-oriented programming.
And that I'm not gonna assume you know.
So I'm gonna be teaching you
all about that along the way.
The user interface paradigm is
also what's called reactive.
And it's declarative
rather than imperative.
And I'm gonna explain what all that means
so that's gonna be new to you.
And there might be some other
miscellaneous new topics.
For example, I'm hoping to get to cover
iOS's object-oriented database
which is really cool technology.
But in any case, all that you're
gonna learn in this course
is really just kind of a survey course
or a collection of real-life application
of stuff you're learning in
your other CS classes here.
For example computer-human
interfaces, obviously.
API design and language design.
Animation, persistence,
networking, multithreading,
all these things listed here
are absolutely going to be things
that we include in this course.
And so you're gonna see it all together
in a real-world environment.
So let's talk a little bit about
the mechanics of the course
like how it's actually gonna play out.
And in these pre-recorded lectures,
I'm gonna try and spend
most of my time doing demos.
I really believe in demos.
I think they make the
concepts very concrete.
So I'll probably spent around
2/3 of the time on demos.
But occasionally I do have
to go to the lecture slides.
And this is to cover concepts
that if you didn't understand them,
the demos might just not make any sense.
So for example, at the
beginning of lecture two,
I'm gonna be covering
MVVM, the design paradigm
we use to organize our code.
I'm also gonna be covering
the type system in Swift
'cause that's just so
critical to understanding
how Swift works, to
understand its type system.
But again, if I can, I
always lean towards the demo
because we wanna spend our time
actually doing iOS development.
That's what we're here to learn.
For your part, you are going
to have some reading to do
the first three weeks.
This is mostly to learn
the language, Swift.
There's an online Swift
programming reference manual
that I'm going to guide you
through those first three weeks
to try and get you efficiently through it.
Because it's a rather large document
and I don't want you to be spending
an inordinate amount of time there.
But by the same token,
you do have to know Swift
to be able to do SwiftUI.
There will be programming
assignments of course.
They are usually about a week long.
There's four or five of them.
This quarter's a little bit
short, starting a week late.
So these four or five
programming assignments
will be in the first six weeks.
And the reason for that is
I leave the last three weeks
for you to work on a final project.
And this final project is the culmination
of everything you've learned,
so we won't have any
programming assignments
during that final three weeks.
If you go to the online
forums, the class forums,
there's a lot more
information there as well.
And the syllabus is posted there
so I encourage you to read that
if you haven't already.
Hopefully, you have.
Let's get started here
building our first app.
I have a little video that I've made
of what this first app that
we're gonna build looks like.
So this is what it looks like.
It's a card matching game.
Those of you who have
seen this course on video
in the past will recognize it.
But of course this one's
all written in SwiftUI
so it's a completely new app.
And it's got a lot more
features than the previous ones.
Some things are really
easy to do in SwiftUI.
So you can see this
has a lot of animation.
These cards are flipping over.
Numbers are flying up and down
as you match and mismatch cards.
You've got that little colored ring
that's kind of spinning
down in the background.
So we're gonna be doing a lot
of animation in this course.
Animation is very important
to a good mobile UI.
You can also see that
this application's got
multiple themes, different colors.
We had that Halloween version.
Here we have these flags.
Our cards are just showing emojis.
Obviously my little video there
is going very quickly clicking around,
but you are watching it on video
so you can just back up and pause
and take a close look at
some of the animations
and the things that are going on there.
So that's what we're gonna spend
the first really two
or three weeks in demo
and in your homework, working
on building this application.
By the way, you're
probably not gonna see me
on camera much the rest of the quarter
'cause we're gonna be
doing screen records.
So you'll just hear my disembodied voice.
By the way, my disembodied voice
will sound a little different
because I'm gonna use my AirPods
when I'm doing the screen recordings.
It picks up less of the mouse clicking
and the keyboard tapping and all that.
So if you don't see me again
the rest of the quarter,
just enjoy my disembodied voice.
So all the work that we do
is going to be in an
application called Xcode.
It's the development environment for iOS
and it's all inclusive.
It includes the source-code
editing compiler,
debugger, it's all in one place.
And you can get it for
free from the App Store
if you just go on your
Mac to the App Store
and search for Xcode.
You're gonna find it's
probably the very first match.
you can see it right here.
And when you launch Xcode,
it's gonna look like this.
Now, your first required task
of your very first assignment
is to reproduce everything that I'm doing
in the first two lectures.
So you might wanna pause this
video and go download Xcode
and be ready to follow along with me.
Or you can just watch this
video at your leisure,
kind of soak it all in and then go back
and rewind, fast forward through it
to satisfy that first required task.
So here this splash screen,
it has two sides to it.
This is Recent Projects
which will build up
as the quarter goes along.
And then over here is essentially
how we get into a project.
And we're gonna choose
this second line right here
to create a new Xcode project.
Now, Xcode can be used
as we mentioned earlier,
not just for iOS apps but also Watch apps
and Apple TV apps, even just Mac apps.
We're gonna focus on building
iOS apps in this course,
but everything that you're
learning about SwiftUI,
it's applicable to all these
other platforms as well.
Now, these icons here
are all just templates.
Essentially, prepackaged
little bits of code
to get you started with
apps of certain types.
But we're gonna be
choosing Single View App
I think for every app this quarter.
And it's the simplest of the templates
and you're gonna see it
really doesn't generate
a lot of code to get you started.
And so let's just double-click on that.
Now, it wants to know
some interesting things
about your app, most notably the name.
So I'm gonna call it Memorize.
For those of you who
might be watching this
who have seen previous videos
of the quarters in the
past of this course,
you'll know that I did this
memory matching game before.
Not in SwiftUI obviously,
because that's brand new.
And I called it Concentration
instead of Memorize.
And I did that for a reason
here, used a different name
so that if we wanna refer
to it, I can mention it.
And when I say Concentration,
I mean the old non-SwiftUI version.
And doing the same app is
gonna allow those of you
who maybe already know some UIKit
or have some experience doing that
to compare and contrast what it looks like
to do it in SwiftUI.
So this second thing right here, team,
that's your development team.
In this case it's a
personal team, just me.
And when you launch Xcode,
this is probably gonna say
something like Add Team.
And when you click on it, all
you'll need is any Apple ID
and you can create your own team.
If you worked for a company,
it could possibly be a
company development team here.
The organization name,
this is gonna be your name.
This is going to appear
at the top of files,
written by CS193p instructor.
So you wanna put your name there.
This wants to be a very
unique identifier of you.
So if you're a Stanford student,
I recommend using this
reverse DNS notation.
edu.stanford.yoursunetid
is probably good enough
to identify you.
You could put the CS193p
in there if you want.
If you're a Stanford student,
it probably doesn't matter.
If you're not a Stanford student
and you're watching this,
maybe your email address reversed
or your company's address reversed.
You just wanna make sure it's unique.
You wouldn't wanna pick something
that someone else might also be choosing.
You could see that Xcode
makes this unique identifier
for your app out of these two things.
And then we're gonna choose the language.
The underpinnings of what is in iOS,
it was all written in this
language, Objective-C,
which is this
object-oriented version of C.
But about five or six years ago,
Apple came out with this
new language, Swift,
which is awesome because
it's completely compatible
with Objective-C.
So all their existing
libraries all just worked.
But it introduced a lot of
modern language features,
especially support for
functional programming
as opposed to object-oriented programming.
Swift does both and you're
really gonna see that
in SwiftUI because SwiftUI is based on
functional programming, not
object-oriented programming.
So I don't have time to
teach you two languages,
I barely have time to
teach you one new one.
So we are only going to
be programming in Swift.
And of course SwiftUI is all
using Swift, based on Swift.
This other user interface choice
is if you wanna develop in the old way,
the non-SwiftUI way.
I'll often call this
UIKit instead of SwiftUI.
We're gonna do all our
development in SwiftUI,
although the last week
or two of the quarter,
hopefully I'll have time to show you
how to integrate the
old stuff into SwiftUI.
Because SwiftUI doesn't
cover all the ground
that the old UIKit does.
So it's nice to be able to
kind of glue it in there.
And so there's some good glue in SwiftUI
to glue the UIKit stuff in.
These other switches in the bottom,
we might get time by
the end of the quarter
to do this Core Data.
It's an object-oriented database.
I'm hoping to have time to do that.
Unfortunately, I'm probably
not gonna have time
to do this testing framework.
Which really I don't want
that to make it sound like
testing is not important.
It's super important.
It's just that you can't
really test something
until you know how to develop for it
and we're just gonna run out of time
before I can go back and cover that.
So for our first assignment,
we're not gonna be doing any of these
so you can leave all of that unchecked.
So I hit Next here and
now it wants to know,
"Okay, great, you wanna create this app.
"Where do you wanna put it?"
And I strongly recommend
putting it in a folder
called Developer in your home directory.
That is kind of the canonical
place that we put things.
And I recommend strongly that you do that.
Down here at the bottom
you can see this Source Control thing.
Source control for those of you
who are not familiar with it,
it's a way for you to manage
the changes to your code,
kind of check them in and check them out.
It's especially valuable
when you have a team
because a team's working on the same code,
their changes might conflict or whatever.
So you wanna have a mechanism
for arbitrating those changes.
And so you can have this checked or not.
I'm not gonna have it checked here.
Later in the quarter I'll try to go
through some of the features
of the source control integration
in Xcode 'cause it's pretty cool.
But for now, either way is fine.
So this is gonna create our first app.
Now, many of you are not familiar
with this whole user interface here,
this Xcode user interface.
So I'm gonna go through it briefly
and I'm gonna start in
the upper-left hand corner
right here, this little row of buttons.
This is essentially how we're
going to run our application.
Now, if you click on this button here,
you can see that you can run
your application on a device
if you have it actually
connected to your Mac
which I don't currently have.
Or you can run it in a simulator.
And these simulators are pretty amazing.
They're a pretty full simulation
that I'm gonna show you in a second here.
And I'm in fact gonna pick
iPhone 11 as my simulator.
And I can just run this SwiftUI app
that this template created
for me by clicking Play.
So when I click the Play button,
it's gonna launch the simulator.
Now, the first time you do this,
it's gonna take a little bit of time
because the simulator
fully simulates the device.
And in fact I'll show you that.
So here's our app, it
says, "Hello, World!"
You can probably can imagine
from this code over here.
And that's what all first apps
do, they say "Hello, World."
But if we swipe up from the bottom
just like on a real device,
you can see that there
is the App Chooser here
and I can go into Settings.
Here's the Settings app
and I could set settings
about my device, et cetera.
It's not 100% a full device,
but it's the parts of
the device you would need
to make sure your app is really working.
I don't know if you also
notice that this device
is similar, it happens to be in Dark Mode
which is new in iOS 13.
And I'm gonna leave it in Dark Mode
because we're gonna wanna look at our app
in both Dark Mode and non-Dark Mode.
And SwiftUI handles most of that for us.
So I'm just gonna go back
to the App Switcher here
and switch back to our app.
Here it is, it says, "Hello, World!"
Let's go back and see how we got to this.
How we get to this whole
Hello, World business.
So I'm gonna press this square right here.
This is Stop, that stops the simulator.
And let's continue looking at Xcode's UI.
On the left here, this little blue area
which we can resize to what we want.
It's called the Navigator.
And it lets you navigate by file.
So you can click on files in here.
But it also lets you navigate
in all these other ways along the top.
Like you can navigate by searching.
You can navigate your
breakpoints in the debugger.
You can navigate through
all your old builds,
the outputs from your
old builds, et cetera.
So you're really gonna get
used to using the Navigator
to move around inside your app,
find the things you want, et cetera.
Now, in the right-hand side,
there's this gray area here.
This is called the Utilities window
or the Inspector sometimes we call it.
And we really don't need that very much
for our first couple of lectures.
So I'm actually gonna use this opportunity
to show you how to hide it.
If you look at these three
buttons in the upper right,
they let you hide and show these panes.
And I'm actually gonna
leave Utilities hidden
because like I say, we don't need it much.
You see there's a third button here.
This actually brings
something up from the bottom.
This is your debugger and
your console output down here.
I tend to leave those off screen
just because they're
wasting screen real estate
if I'm not actually debugging.
Also, they'll often come up
automatically on their own
when you for example, hit a
breakpoint in the debugger
or some output comes out on the console.
And this area is the main editing window.
And you can see that it's
currently split in two.
And you can split this area in
many, many ways if you want.
That's what this little
plus button over here does.
It lets you add another, a
third editor on the right.
You can even add an editor on the bottom
if you'd like your split to
be top-bottom, et cetera.
So that's kind of our
tour of the Xcode UI.
Of course there's tons
of menu items up here
and we'll learn about them
as the quarter goes on.
But let's look at our main editing window
and see what we got.
We got this ContentView.swift
you see right here.
This is a Swift file.
So this is your first look
at the Swift language.
And I'm gonna start with
this little piece of code.
And what this little piece of code does
is it provides some glue
between your code that you're writing
and this little area on the right
which is called the Preview window.
See, it says automatic
preview updating paused.
And of course we have the simulator.
We can always run that
to see what our apps
are gonna look like.
But we also can see what's
happening in our UI,
kind of in real time, approximately,
by hitting Resume here.
And what this is gonna
do is compile our code
and essentially run it right here.
It looks like a little iPhone.
Notice this is not in Dark Mode
so the text is black on white.
And that's automatic, the SwiftUI
takes care of that for us.
And we can resize this too.
Maybe make it over here,
it'll have less space.
And this code that is
essentially gluing to it,
I'm not really going to
pay much attention to it.
In fact, I'm going to
move it out of the way.
And when you make a big edit like this
or you're moving a lot of code around,
sometimes this preview will pause.
You see, it's paused again.
No problem, you can just resume.
But a lot of changes you make,
like if I change this "Hello, World!"
to be "Hello There, World!"
you could see it's actually
changing it in real time.
So it depends on the kind of change
you're making to your code
as to whether it will update in real time.
And again, you can
always run the simulator.
Especially if you wanna see it
on lots of different devices
and things like that.
The simulator is always
available to you up there.
So let's take a look at the Swift code.
This is the entirety of the Swift code
that's specific to our app.
There's a little bit
of boilerplate up here
in these two delegate things.
We'll look at those later
but this is really
pretty much all there is
when it comes to doing this
Hello, World right here.
So let's look at the Swift code in detail.
First we have this import SwiftUI.
I'm sure you can all imagine
exactly what that is.
It's like include or
import in other languages.
This imports what's
called a package in Swift.
This package is SwiftUI.
So that's the big ol'
package that Apple provides
that makes all this SwiftUI stuff work.
So anytime we're doing UI stuff,
we're always gonna be importing SwiftUI.
Sometimes we're going to be writing code
that is not UI stuff.
In fact, intentionally UI independent.
And in that case, we will
not be importing SwiftUI.
We'll be importing a different package
called Foundation.
Foundation is kind of arrays
and dictionaries and strings.
And the SwiftUI package
depends on Foundation.
So if you imported SwiftUI,
you get that one too automatically.
So this whole thing is really
just three lines of code here,
not including the curly braces.
So let's see what it is.
This first key word here.
Notice all the Swift
keywords are in magenta.
We can always tell the
difference between Swift
and the things we do.
So this is a struct.
This is just declaring a struct.
Now, I'm sure you know a
language that has structs.
C and C++ and these
languages all have struct.
And the struct in Swift, just
like the other languages,
is a container for some variables.
But structs in Swift are
much more powerful than that.
Structs in Swift not
only can have variables,
but they can have functions
and they can also have behaviors
which we're going to see very clearly
right off the bat here.
So this struct, its name is ContentView.
So the name of our
identifiers are in green.
And Swift's identifiers are in purple
and the keywords again are in magenta.
So the name of this struct is ContentView.
And this is a very interesting little part
of this struct's declaration.
It essentially means that this struct
is going to behave like a View.
Or some might say it's going
to function like a View.
Or some would even say it "is a" View.
Although if I use the
statement that it "is a" View,
some people will think that's
object-oriented programming
like the superclass or something.
But it's not.
This is not object-oriented programming.
This is functional programming.
And that's why we may be more likely
to say something like a
ContentView functions like a View
or it behaves like a View.
And this is super important.
This behavior, View, is crucial
to how all of SwiftUI
works as you will see.
We are going to be using so many Views
in the next couple of lectures,
you can't even imagine it.
And we're gonna talk
all about the concepts
behind this behavior specification
using slides at the
beginning of lecture two.
In the meantime, we're
just going to understand
that this means that
ContentView behaves like a View.
A View is just a
rectangular area on screen.
So here I've put my mouse on this Text
and it's selected it
over here in the preview.
And you can see it's put this
blue border around that Text.
That blue border is bordering this View.
So a Text, it also behaves like a View.
It "is a" View.
And so you're seeing it right here.
So that's all Views are.
They're rectangular areas on screen,
both for drawing and also for multi-touch.
For swipes and taps, pinches,
those kinds of things
happen in these rectangles.
So that's what a View is.
So when something behaves like a View,
it's a rectangular area on screen.
So our ContentView which
behaves like a View
is this entire rectangle.
The whole rectangle that fills the screen
is our ContentView.
All right, if you are somebody,
some struct like this ContentView,
and you want to say that
you behave like a View,
you must in your struct
have this var right here,
this var called body.
I'll select it for you here.
So here's the declaration of var body.
So Swift variables, they
have the keyword var,
short for variable.
Although we don't tend to call variables
inside a struct like this vars.
We call them properties.
So you're gonna hear me use
the word property all the time.
It means a var inside a struct or a class.
Also, if we're doing
object-oriented programming,
we call them properties.
So the syntax for a property,
really easy, you got a var.
This is the name of the property.
Again, it's green 'cause
it's something we chose.
And this right here is
the type of this property
or of this var.
Now, this is a pretty
interesting type even in Swift.
Because it's got a little magenta keyword
in the middle of it.
Normally this type might
look like this is an Int
or maybe it's a String.
Or it might be a Boolean value
or it's an Array or something.
But in our case, it's this some View
which is kind of a interesting type.
What this essentially means
is that the type of this
variable, this property,
is any type, any struct, as
long as it behaves like a View.
As long as it is some View.
So that's what this kind of odd thing
means right here.
So if you wanna behave like a View,
you have to have a var called body
that also is another thing
that behaves like a View.
And you might be kind of thinking, "What?
"That's weird.
"So to be a View I have to have a var
"that is another View as my body?
"That makes no sense."
But you could kind of
think of Views as Legos.
So of course there are some brick Legos,
Legos that are like the
basic thing like Text.
Text is like a brick Lego.
But then you can put Legos
together to make something else.
And that thing that you put them together,
you could call that a new Lego.
It's like a combined Lego
or a more powerful Lego.
For example, if you were
building a Lego house,
you probably would make
little Lego furniture.
A Lego couch, a Lego kitchen table,
all those things out of other Legos.
And if you consider that
couch and the kitchen table
as Legos, then you're
putting those together
to make the house.
You could even then
have the house be a Lego
that you put together to
make a Lego neighborhood.
Or the Lego world, Lego universe.
That's the way we wanna
think of making these Views.
Now, the difference
between a Lego and a View
is that there are special kinds of Views
that are used to combine Legos.
We've got these basic
brick Legos like Text.
And then we've got other kinds of Views
that are combiners, Lego
combiners, View combiners.
And you're gonna see us
use those in detail here.
That's why our body just returns
one single some View of some sort.
It might be returning a combiner.
If it returns a combiner View,
then it could have tons
and tons of Views inside
that are combined.
Now, this is also a kind
of an interesting var here
in that it's got this
curly brace thing after it.
This var, its value is
not stored in memory.
Instead this var is computed.
So every time someone, namely the system,
asks for the value of this var,
this code in this curly
brace gets executed.
And whatever the value that's returned,
that's what the value of the body is.
And every time it's called,
it gets called again,
it's constantly calling this thing.
Now, this code in here
seems kind of minimalistic
but it'll probably make more sense
if I put this keyword back.
This keyword, return, you see is magenta.
It's a Swift thing, it means return.
Return a value from this piece of code,
this little curly-braced piece of code.
And it wasn't there because
Swift loves to leave things out
and make it so you have to type
as little code as possible.
You're gonna see that later.
And so if you have a
one-line function like this
that returns a value,
so that's what this is,
a one-liner returns this Text thing.
Then you can leave this return out.
It'll just infer that.
But I'm gonna leave it in
because I want you to realize
that that's what's going on here.
We have this little piece of code,
it returns this Text thing.
And that's going to always
be the value of body.
Anytime someone asks me
for the value of body,
it's gonna execute this and
return this Text right here.
Now, this return type,
remember, is some View.
So if you're saying, "It's returning Text.
"What's going on here?"
Well, of course Text, it
says it behaves like a View.
So this is "some View".
In other words, somewhere
off in Apple's code
there is some line that looks like this.
"struct Text behaves like a View."
And that's why we can return
a Text as the value of body
'cause it's some View.
In fact, we could even
just type Text right here.
That would be valid because
to behave like a View,
your var body just has to
return something that is a View.
And of course, Text is a View.
So why don't we type Text?
Why do we do this some View?
Well, as our body of our
View gets more complicated
and we start using these View-combiners
and things like that, it's
gonna be constantly changing.
Which kind of View we're returning.
Right now we're returning a Text.
But eventually we're gonna be returning
View-combining Views.
And we wanna let the compiler
figure it out for us.
So this some View is basically
saying to the compiler,
"Go look in my code right here.
"Figure out what it's returning.
"Make sure that it behaves like a View.
"And then use that as
the type of the body."
You're gonna find as we
go through this course
that Swift is what's called a
very strongly-typed language.
That means every variable
has a specific type.
It's not a language
like JavaScript or other
where it's like well, the variable
depends on what you assign to it.
That's when it gets ...
No, in Swift, every
variable has a specific type
and always has a value.
That's just fundamental
to the way Swift works.
So let's look at this Text right here.
We create this Text.
Anytime we're creating
anything, any struct,
so a Text is just a struct
that behaves like a View.
And anytime we create
one, we in parentheses
give it whatever information
it needs to create itself.
Now, obviously for a Text,
it needs the string that it's
going to use to create itself.
and that's why you get
this "Hello There, World!"
And notice if I edit this,
it's not changing this.
That's because it's paused up here.
But if I resume, then it's going
and now I can for example
type, and it's tracking it.
So this is always the way we
create new structs of any kind.
We put parentheses after it
and then we give it
whatever arguments it wants.
And some things when you're creating them,
can take different kinds of arguments
depending on what they are.
And you're gonna see
that pretty soon here.
So let's see if we can start building
a UI that looks a little
bit more like a card.
Right now it just says, "Hello, World!"
Let's remind ourselves what
that's gonna look like.
Kind of a screen capture of the game
that I showed you earlier.
And our cards, they kind of
have these rounded rectangles
around the edge, you see that?
And this is a line.
Then kind of a white background.
They have an emoji on the front.
So let's take care of
each of these things.
Let's start with the emoji.
That's gonna be really easy
'cause an emoji is just a piece of text.
So I'm gonna delete that.
I don't know if you know
this but in any Mac app,
if you go to the Edit menu,
go down to the bottom,
there'll be Emoji & Symbols.
You see there, I can get an emoji.
You can even search ghost.
There it is, ghost.
Let's double-click on it.
Now we have a ghost.
And sure enough, there's our ghost.
He's really small.
Although we can zoom in and see him there.
And we're gonna have to
fix that he's so small.
It's not gonna work for our cards
for him to be so tiny like that.
But one thing at a time.
The next thing we need
is our rounded rectangle.
So we need this rounded
rectangle, it goes up there.
So let's comment this Text out.
So I'm doing Command
slash by the way there
to comment that Text out.
And instead put our
rounded rectangle in there.
Luckily, Swift has an awesome thing here.
RoundedRectangle which
is exactly what we want.
And again, just like when we created Text,
we have to provide some information
to create a RoundedRectangle.
You can by the way, easily
find out what that is
by just doing the open parentheses
and Xcode will show you the options.
Now, RoundedRectangle,
there's four different ways
to create a ZStack.
You can specify the corner
radius or the corner size,
width and height of the corner.
I'm gonna do the cornerRadius.
So you can hit Tab here or
you can double-click on this.
Then it wants the radius
and this is in points.
So I'm gonna do 10 points.
A point is the same as
the point size of a font,
like 10-point font.
It's also, just to give you an
idea of the size of a point,
a typical iPhone is maybe
four or 500 points wide
and maybe seven or 800 points high.
An iPad might be 1,000
by 700 and something.
Now, I'm being intentionally vague here
because we never write our code
trying to know what the
sizes of screens are.
SwiftUI helps us write code
that works on any size screen.
It just adapts and adjusts
to any size screen.
You're really gonna see
that with our Memorize game
when we start having rows
and columns of buttons.
On big screens they're gonna be large.
When we're in portrait mode
it's gonna kind of resize things
one way and landscape another.
So we always wanna write our code
so it works in any size screen.
But that's the approximate size of a point
to give you an idea of a point.
Now, one thing that's
kind of strange about this
is this right here.
It has put a label on
this piece of information
that it needs to create a ZStack.
And that didn't happen with Text.
This doesn't say the string
or something like that.
Text doesn't have one of these.
But this is actually the norm.
It's unusual to have something like Text
where there's no label
here for an argument.
You have to go out of your way to do that.
And I'm gonna show you
how to go out of your way
although we're not gonna
do that very often.
Instead, when we create things,
when we pass the information,
we almost always label each piece.
And this is true when we
call functions as well.
So one of the things that maybe takes
the most getting used to in Swift
is that almost all the arguments
to all parameters of all
functions are labeled.
And this is something that was
inherited from Objective-C.
It's actually quite nice
because if we didn't have this
and we had ZStack 10,
I mean, is that the width and height
or what exactly is that 10?
But as soon as we put this
cornerRadius in there,
it's like all of a
sudden, "Oh, I'm creating
"a ZStack with a corner radius of 10."
It's kind of hard to see if
our rectangle is rounded there.
That's because I actually have
this line of code selected.
So we've got this blue line around here.
But if I just click away,
then you can now start
to see the ZStack there.
And if I zoom in, you can see
the ZStack quite clearly.
Obviously, we're gonna
have to do something
about the visibility of this.
Our cards don't want to be
smashed up against the edge
and they don't want to be black either.
But first things first.
We'll get to that in a bit.
The next thing I wanna do is combine
this RoundedRectangle
and this Text together.
Because remember, our cards
have both a RoundedRectangle
and a Text stacked on top of each other.
And I'm gonna have to do
that by returning some View.
So the View I'm gonna return
here is called a ZStack.
So a ZStack is just a struct.
It behaves like a View
just like RoundedRectangle is a struct
that behaves like a View.
So is Text.
Actually, so is our ContentView.
A ZStack does need an
argument to be created here.
The argument we're gonna use
is content then in curly braces
The list of the Views to
stack on top of each other.
And obviously we don't
need the returns in here.
So this is the list.
RoundedRectangle in the
back, Text in the front.
First thing in the list
and this is two items
but we could have five
or six or seven items
all listed in a row and
it would stack them all
on top of each other.
ZStack is just some View.
And in fact, this return type here
would be some sort of ZStack here
instead of a Text or a RoundedRectangle
or something like that.
But Swift is going to
automatically realize
that it's returning this.
And so it's gonna do that for us.
This kind of combiner View
is really important obviously
to building complicated Views.
And we're gonna build our
own combiner View next week.
So you're really gonna
understand how these things work.
Now, we're getting there.
But there's still some things,
we'll kind of resume here,
that this doesn't look much like our card.
First of all our cards
aren't filled with black.
They're kind of stroked with
orange around the outside.
Also we have a problem where
it's hard to see the edges
'cause they're all the
way out to the edge.
So let's fix both of those.
First of all, let's stroke
this instead of filling it.
And the way we're gonna do that
is we're going to call a function
on RoundedRectangle called stroke.
RoundedRectangle, we know
that it behaves like a View.
It has to behave like a View
or we wouldn't be able
to put it in the ZStack
like with this Text.
But RoundedRectangle also
behaves like another thing
which is a Shape.
Other Shapes are Circle,
Capsule, bezier paths,
these are all Shapes.
And Shapes can all be stroked
by calling this function
on them called stroke.
And you can see that it's
exactly what happened.
It stroked a line around the edges
of this RoundedRectangle.
And stroke is an interesting function
in that it returns something.
Can you guess what it returns?
It returns a View.
It returns something
that behaves like a View.
And it has to because whatever
this is has to be a View.
Otherwise again, we couldn't
put it in the ZStack.
So this right here is like a stroked
RoundedRectangle View
or something like that.
So there's two Views still in the ZStack.
A stroked RoundedRectangle is one of them
and Text is the other.
So this pattern of calling
a function on a View
or a Shape
to return another View is very common.
This is the first time you've
seen a Swift function call.
Really simple, it's just dot
and the name of the function.
And then in parentheses, any arguments.
Now, stroke actually can take arguments
but it works perfectly
fine without its arguments.
Without its arguments,
it just kind of does this default behavior
of stroking kind of a one-point line
around the edges of the Shape.
So what about the color
of this RoundedRectangle's edge here?
It currently looks like
it's kind of either
maybe dark gray or something like that.
What if we want that to be orange?
So how do we do that?
Well, this is a View
right here, some View.
And all Views, anything
that behaves like a View,
you can call the function on
it called foregroundColor.
It takes an argument which is a Color.
I'm gonna use the Color orange.
And that will set the
color that it uses to draw
to be this Color, orange.
And sure enough, look at that.
It's changed it to orange right there.
Now, this function can
be called on any View.
We could call it on Text.
You can see, no errors.
Of course, it doesn't
really help with Text
'cause it's an emoji.
But if Text were the letter X instead,
you'd see that that's
an orange X right there.
Again, it doesn't make
sense to call it there.
The other thing we could send
this orange to is our ZStack.
What does it mean to tell this View
to have its foregroundColor be orange?
ZStack is just a View.
It behaves like a View
so it has foregroundColor
just like every other View in the world.
And what it means for
this is "Tell every View
"inside of me to use
foregroundColor orange."
So this works as well.
Look at that.
See, that's orange.
Again, if we change this ghost to an X,
that X would be orange as
well because the ZStack
is essentially setting the environment
that all of the Views inside of it
are going to use to draw.
Now this can be overridden.
We could then say,
foregroundColor(Color.blue).
And then we'd get a blue one out here.
But this would still be orange.
Because I only overrode
this foregroundColor blue
for this View right here,
this stroked RoundedRectangle.
I didn't override it
for this X right here.
So by scoping the calls
to these functions,
so we can control what color things are.
And this is really valuable for the ZStack
because we're eventually
gonna make this ZStack
so it does the front of the
cards, the back of the cards,
everything in all those cases
that wants to be orange.
So we want orange for everything
so it's really nice to be able to set that
for the entire ZStack.
And we usually put these modifiers
for these combiner Views
on a line by themselves,
just to make them stand out a little more.
Whereas we might leave
them on the same line here.
Although occasionally,
we would even put these
on a separate line as well.
But we're not gonna do
it here, but you might.
It's kind of a style thing.
However you think your
code is most readable
and most understandable.
So we're making good
progress on our card here.
One thing, it's really
hard to see the edges here.
We kind of want some
padding around this edge.
And we can do padding
with another View function
called padding.
And here notice when I put
padding around my Text,
oop, it popped out, it got
some padding around it.
Now, I could do the same thing over here
to my stroked RoundedRectangle
since it's a View.
padding, ooh, that's nice.
You see, it put padding around there.
Again, I could even put the padding
around my entire ZStack.
It put the padding around the whole ZStack
which is probably what I want
'cause I don't wanna
have to put the padding
on every single
RoundedRectangle separately.
Like the RoundedRectangle we'll use
for the back of my card
eventually and things like that.
So this is nice to be able
to put these on the outside.
Now, notice the subtle
difference between padding
and foregroundColor.
Padding pads the ZStack.
The entire ZStack gets
some padding around it
and everything in the ZStack is drawn
in that little padded area.
Whereas foregroundColor
doesn't really make sense
for a ZStack.
A ZStack doesn't have a color.
All it does it contain other Views.
So it gets passed down
into the environment
for all the Views on the inside of it.
All right, let's see,
we've been looking in our preview here.
But let's go take a look in our simulator
to see how this looks.
And ah, kind of interesting.
It's working but it doesn't
look very good here.
That's because our
simulator's in Dark Mode.
And really we want this
background to be white.
Even in Dark Mode we want it to be white.
We don't want it to be black
cards on black background.
Let's say we want it to be white.
So how would we make that white?
Unfortunately, there's
no function you can send
to a RoundedRectangle that
says stroke it with one Color
and then fill it with another Color.
But that's no problem because
we're in a ZStack right here.
So I'm just gonna create
another RoundedRectangle,
and this one I will
fill instead of stroke.
And remember, it's in the back of this.
And resume right here.
Now, that's gonna fill it with
whatever the foregroundColor
that's set as the environment
here, so I can't even see.
But luckily fill takes
an optional argument
which is the Color to fill with.
And that overrides this
foregroundColor here.
So now this is white.
If I click on here, it
is filling it with white.
And there is that orange
border, kind of hard to see.
It's only one point wide,
especially near the black.
It's really kind of hard to see.
So let's fix that.
A stroke I told you could
take other arguments.
One it can take is line width.
So let's make it a line width of three.
Again, we could resume here
to see it in Light Mode.
There it is.
And we can run over here
to see this in Dark Mode.
And by the way, the dark and
light mode of your simulator
can also be controlled right here.
You see this little button down here?
You see this Interface Style?
You can turn that on and go
from Dark Mode to Light Mode.
Now if you go back to your simulator,
it's in Light Mode.
That's really kind of a cool
way to switch back and forth.
So we are making some great progress here.
Our little emoji is still really small.
I'm gonna make it bigger.
I can do that with Font.
Font, Font.largeTitle.
Now I'm using a pre-canned
Font here called largeTitle.
There's some other ones called subheadline
and body and other things.
largeTitle happens to be
the largest one I can find.
If we resume, you are gonna see
it's gonna make our emoji larger.
Still not large enough.
Next week we're gonna
learn how to scale the Font
to an exact size that
fits nicely in our card.
But for now at least it's
getting a little bit larger.
One of the reason I did this
is I also wanna show you that this Font,
while it seems like this
would be a Text only thing,
it can actually be put on the ZStack
and it will set the font
environment for all Texts.
So if I had multiple
Texts inside the ZStack,
then it would use the Font,
that Font for all of them.
And you could easily
imagine building something
that had multiple pieces of Text
and you want them all to be the same Font.
And so setting it on the
ZStack is a really cool feature
for that as well.
So I've kind of got the card
somewhat looking like I want.
Now I want multiple cards
'cause I only have one card here
and I'd love to have multiple cards.
So how am I gonna get multiple cards?
I'm gonna do that by returning
a different kind of View,
another combiner View.
This is called a ForEach.
Now, a ForEach, of course
it takes arguments.
The first argument, it
takes two arguments.
The first argument is like an
iterable thing of some sort.
We'll talk about that in a second.
Then the second argument, just
like a ZStack, is content.
And that content is
going to be this ZStack.
What ForEach is gonna do
is it's going to iterate
over this iterable thing.
And for each of those things,
it's going to build one of these Views.
So it essentially is a way
to make multiple Views.
Kind of a View replicator
or a View iterator
if you wanna think of it like that.
So what is this iterable thing
we're talking about here?
Well, usually this is
going to be an array.
So you're gonna give it an array of things
and it's gonna, for each
of the things in the array,
it's going to create a View.
I'm not gonna use an array here.
We'll eventually switch to using an array
but I'm using another
iterable thing called a Range.
So this is the Range from zero
up to and not including four.
That's what this Swift
syntax right here means.
It means a Range from some
lower bound to some upper bound,
not including the upper bound.
If you say dot-dot-dot, then
it includes the upper bound.
But I don't wanna include the upper bound.
So, this is going through for
zero, one, two, and three.
It's going to create this ZStack in here.
So it's a repeater.
Now, one thing that's also cool,
so it's got the same thing
where it's got the content.
And this could be a list of Views.
We only have one View
in here, it's a ZStack.
But we could have multiple things.
Just like in the ZStack,
we have multiple things.
The ForEach, its little curly-braced thing
could have multiple things.
And one thing that's kind of
cool also about the ForEach
is it actually has this little thing here
which we're gonna talk about
this "in" meaning at some point
But essentially this is
the iteration variable.
So in this case, this index is zero.
And then it's one and then
it's two and then it's three
as it makes the four of these.
Now, one thing about ForEach,
you'll see in the preview it actually made
four separate previews of it.
And that's because ForEach
is not a layout View.
It's not like ZStack.
ZStack positions the Views on screen
on top of each other from back to front.
ForEach doesn't do that and if you run,
if we were to run this in simulator,
it would probably put the View somewhere
because it obviously can't
simulate multiple iPhones.
But really the preview
is actually showing more
of what's happening where it's
creating four of these Views.
So it's showing you each of the four Views
that it's creating.
Which is really cool for debugging.
It's like, "Oh yeah, this is the one now."
Our four Views are exactly the same
so not so interesting for us.
But what we really wanna do
is put all four of these on
the screen at the same time.
And for that we need another stack.
Now, we don't wanna stack
them on top of each other
and we wouldn't be able to
see the ones in the back.
Instead we want a different one
which is we're gonna return an HStack.
Our HStack just like ZStack has content.
And you put in the content the things
you want to arrange like this.
And HStack instead of arranging
things from back to front,
it arranges things horizontally.
That's what the H means,
from left to right.
So look at that, left to
right, it arranged them there.
Now, I told you that Swift
likes to not type things
that you don't need to type.
And that return I can still remove
because believe it or not,
there's still only one
View being returned.
Being used as the value of this body,
some View, it's one View.
It's a HStack of ForEaches that
make ZStacks that's padded.
It has a foregroundColor and a font.
That is the one View
that's being returned.
Of course, HStack is a combiner
so it's combining things.
And ForEach is a combiner
and ZStack is a combiner.
But those are all inside the HStack.
Now, notice also that the HStack
when it sees a ForEach
'cause this could be a list,
we could say Text("hello")
So it puts hello in there.
And then this ForEach
is kind of like listing
each of these ZStacks separately.
That's why it does this.
So each stack is smart.
It knows if it has a ForEach inside,
then it wants to lay out
each of the ForEach things separately.
Now, another thing to
notice here is the spacing.
There's a little bit of
space between these things.
Is that this padding right here?
No, that padding is this padding
around the outside of HStack
'cause the padding function is
called on the HStack itself.
That spacing is actually
something the HStack is doing
and it has an argument
that lets you set that.
So here I've set the spacing to zero.
Or I set it to 50 or I
leave it unspecified.
A lot of times we're leaving
those things unspecified
'cause we want this spacing
to be standard spacing.
The same in all apps.
Same thing with padding down here.
We didn't specify anything
here but in padding
you can actually even do
padding on the top is 100.
Or padding is 10 on all sides.
But usually we go for standard
padding as much as we can.
Standard padding and spacing.
Now, I told you, remember
we took away this return.
There's another thing we can take away.
In Swift, if the last
argument to a function
or the creation of
something, so here's ForEach.
It has two arguments.
This and then it has this
as its second argument.
If the second argument
is a curly-braced thing,
then believe it or not, you
can get rid of the label
and put it outside of the function call.
So here's the ForEach.
So basically the creation of the ForEach.
And here's its second argument.
It's floating outside of the parentheses.
So this seems kind of crazy.
But actually, look what it
makes our code look like.
I'm gonna do that for all of these things
'cause this only has one argument.
So I'm gonna get rid of that.
And I'm gonna do the same
thing here for my HStack.
So that looks kind of a lot cleaner.
You don't have those contents in there.
And even more than that,
if you have no arguments,
in that case where you have
the little curly-braced guy
hanging out there, you
can remove that as well.
So that cleans up our code considerably.
And of course we're doing these ZStacks
and these HStacks all the time.
Now, if I wanna put this
spacing back, I could.
Spacing, zero, perfectly fine.
It's still the first argument.
This is still the second
argument to the HStack there.
So it's kind of a little clean up.
So our code is looking
pretty clean already
making these four cards.
We can clean it up even a little more
by factoring out some of our code.
This ZStack here is essentially one card.
Wouldn't it be cool if I
just created a new struct
called a CardView which
also behaves like a View?
Which means what?
It has to have a var
body that is some View.
And in here we just return some View.
I'm gonna return a ZStack.
Cut that out of there, paste it in there.
And then in ContentView here,
I'm just gonna create a CardView.
So this is a way we can use the Views
to factor out our code
to do encapsulation.
To make our code look
even simpler and nicer.
Somebody looking at this ContentView,
it's really easy to see what
this is gonna look like.
And similarly down here.
Simple to see.
Now, the last thing I'm
gonna do here in this lecture
is do fronts and backs of cards.
'Cause right now, we only have
the front of the card shown.
We don't ever show the back of the card.
So how would we draw
this back of the card?
Well, it's just a ZStack as well
but it's filled with the foregroundColor.
So we could actually go ahead
and just copy and paste here
a RoundedRectangle to be the back.
And for the moment I'll comment this out.
And of course we don't
wanna fill with white.
We just wanna fill with whatever
our environment Color is.
Which it's going to get from here.
Notice that this foregroundColor
not only applies to
everything in the HStack
that ForEach passes it on down
so that it applies to the CardView
which passes it on down.
Which applies to the ZStack
which passes it on down
so that it applies to all these things.
So now if we resume.
We can see backs of cards.
See, here's the backs of our cards.
Right here, RoundedRectangles
that are just filled.
But of course, really, we
want to have fronts of cards
and backs of cards somehow be conditional.
Or the card is either
face up or face down.
So we need an if-then or something.
Well, the amazing thing
here is that this ZStack,
which allows you to put
a list of Views in here
also lets you put ifs.
So you can put simple ifs like
if, let's say, isFaceUp for now.
And we'll put these three
things in the isFaceUp case.
And then, otherwise if it's face down,
then we will put this right here.
So we can actually put an if.
Now, we got all kinds of
errors and warnings here
because I just made up isFaceUp.
That's not a thing.
I just made that up.
What do I want isFaceUp to be?
Well, I'm just gonna have it be a var.
It's gonna be a normal in memory var.
It's not gonna have any curly
braces or anything on it.
It's just a normal in memory var.
And you see that fixed up
all of our code down here.
CardView is perfectly,
internally self-consistent.
It's referring, it's saying
if my isFaceUp var is true,
then this way, otherwise this way.
But it did cause an error up here.
And what is this error about,
missing argument for isFaceUp?
Why is it saying that?
Well, I told you earlier.
Swift, all variables, it's strongly typed
and all variables have
to have an initial value.
So we could set an initial value
by just saying it equals false.
And if I say that it's going
to fix this error right here.
Face up is false, does that.
And if I change thisFaceUp
to be true and resume,
now we get face up cards.
So that's one way to set this.
And we often will set
this if it makes sense.
But if we don't set it in here,
we don't give it a default value,
that's when we get this error up here.
Because Swift is saying this CardView
that you're trying to create right here,
it has an uninitialized variable.
So you have to initialize it.
And here's a cool feature.
If you click on this little
red button of an error,
a lot of times you'll get
this little Fix option.
And the Fix will fix the error for you.
Now, it's not always gonna fix it
exactly the way you want so
it doesn't work all the time.
And there's not always a Fix option.
'Cause sometimes it can't figure out
how to fix what you're saying.
But here it does work.
If I hit Fix, it actually adds an argument
to the creation of CardView
to set this variable isFaceUp.
So if I say false right here,
it's going to initialize this variable.
Let's resume.
And there it is.
False, face up is false.
This gets initialized false.
All these are not face up.
Now, hopefully this pattern
is looking familiar to you.
It's the same as this pattern.
Here I'm creating a ZStack.
It has some var probably.
Not necessarily because
there are other ways
to pass these variables.
But it might have a var
called cornerRadius.
And so it's forcing us to set it,
to define it 'cause we
can't have unset vars.
This is the same kind of thing.
Okay, that is it for this lecture.
In the next lecture,
I'm gonna actually start with some slides,
some conceptual stuff.
Then we'll get right back to this demo.
Normally I would tell
you a little bit more
about what's gonna happen
in the next lecture.
Except for that I'm releasing lecture two
along with lecture one.
So if you really want to
see what's in lecture two,
just go watch it right now.
- [Announcer] For more, please
visit us at stanford.edu.
