[MUSIC] Stanford University.
>> All right, well, welcome to
Lecture number four? No. Yes, four of
Stanford CS193P, Fall of 2017. Today, we're gonna continue
to talk about Swift. This will be the last we talk
about Swift. After this, we're pretty much gonna
kinda assume you know Swift. Obviously you'll be learning
on the fly as you go, but starting next week, we're gonna dive right
into iOS, UI kit and all that. So I'm gonna do the
quick demo that I postponed from Wednesday of this
mutating keyword that we have to add when we make something
a struct versus a class. Then we're gonna talk about two
very important topics today. Protocols and closures. Functions as types of general
those two and in between, I'm gonna talk about string which
is important in a way but it's not like the monstrous
important topics like prototypes and closures are.
So start right into that demo that I had. And remember, we
were talking at the time about making things a struct and
that it's a little different then a class because that copy
on write. Swift needs to know when a function might
actually modify the thing so that it knows to actually
make a real copy. So let's go back here to our app,
Concentration, where we were. Let's go to Concentration,
which if you recall, we made be a class.
A class Concentration. And this could have just as
easily been a struct. I really made it a class
just so when we're doing the initialize and stuff,
you could see the difference between Concentration and card
because card was a struct and Concentration was a class.
But there's no reason Concentration
wouldn't have been a struct. It's probably, it might even
be better as a struct here. Now we don't pass
Concentration games around and all that, it just kinda
sits in our controller. Not really a pointer to our
model but this would embed the model there. So it doesn't
really matter too much. But when I change this from
class to struct, notice here that I get an error. And let's
scroll down and here they are, here's the error and
this error is saying, cannot assign to property,
self is immutable. Why is this saying self is immutable? I'm
trying to change the card here to say that they've match,
for example. Well, self is immutable
because this function right is not marked mutable, and so
it is assumed to not mutate, not change this
Concentration object. So of course we need chooseCard
to change the Concentration object cuz it's the main
thing. The change of the game, so all we need to do is add
mutable right here, that marks, sorry, mutating, not
mutable, you add that here and that says that this is
a mutating function, all the errors go away,
and we're all good to go. Question?
>> [INAUDIBLE] >> Okay, the question is why does this not need it? This is a var. And
this var is get and set, so Swift already knows this is
mutating, cuz it's settable. If this was get only, then
it wouldn't be mutating and Swift would know that.
So for vars, Swift knows. And if it's not a computed var,
like look at this var, how does this one know? That's
because, well, this one is read only externally but
for us it's writable. Here, anything that's a var it
assumed it's writable. If it were a let, a let, then it
would think it's not writable. So that's how you do mutating,
or mutable. If for vars is whether they're vars or let,
or for computer property, whether it has both get and
set. Where we got all that? So it's only funcs that we
have to put that on. Yeah, question.
>> [INAUDIBLE] >> Yes, so the question is, why is this you need to a
struct and not a class, why do I need to struct? And that's
very important to understand, very good question. Structs
are value types, not reference types. So structs don't live
in the heap. As we pass the struct around since it's
a value type, it gets copied. Constantly copied everywhere.
Pass through a function, a copy is made. Put in a var,
a copy is made. Well, soon, that would be incredibly
inefficient if it actually copy the bits of this entire
thing every time. So instead, Swift is smart. It only
actually copies the bits when you mutate it. That's called
copy-on-write semantics. This is why structs
are different, because they have that
copy-on-write semantics. A class doesn't have that, cuz
when you pass a class around, the class just lives
once in the heap, and you're passing
pointers to it around. And there could be 20
pointers to the same object. Totally different kinda model
for memory. So that's why, it's because it's a value type
that copy on writes stuff, that's why we care
that it's mutating. Let's go back to our slides
and talk about the first very, very important topic
today which is protocols. It's the fourth pillar of
building data structures inside of Swift. You already
know a lot now about class, struct and enum. And
a protocol is something where its a very simple concept.
A protocol is basically just a list of methods and vars
with no implementation. But it's the use of having
protocols. It's so pervasively used throughout
the Swift language and the runtime that makes it so
powerful. So first I'm just gonna go through a little bit
of why we have protocols. Then I'll show you
the syntax of protocols. What it looks like to type in
a protocol and define one. Then we'll start talking
about the use of protocols. Where do we use them?
Why are they so valuable? So let's start with,
what protocols are all about? Protocols are essentially
a way for callers to call an API
with anything they want. Any struct, enum, class, it can pass anything they
want in. And at the same time the receiving method can
specify what it really wants. So both sides get to do what
they want. The receiving thing gets to say what it wants
the thing you're passing and to do and the thing passing in
can pass whatever it wants as long as it does that thing.
And to do all this all a protocol is, is a list
of vars and functions. That's all a protocol is.
It's just a list of them. Not an implementation or
anything, just a list of them. And it's how we use them in
the API that let's us get this behavior of having
the callers and the callees. Get to express things
the way they want. Now, what are protocols good for? They
make APIs very flexible and expressive, as
you're going to see. They're super good for blind
structured communication. Like, remember back
to my MVC talk. The communication between
the view and the controller. All those will, did, shoulds.
And the data at count, those kind of communications
between a view controller. That all had to be blind,
because the views are generic and the controllers are very
specific, and protocols is how we make that work. It's also
great for mandating behavior, for example, in a dictionary,
a dictionary's a hash table, raise your hand if you know
what a hash table is when I say that. Okay, everybody.
Good. So it's a hash table. So the keys of a dictionary
clearly have to be hashable. You have to be able to get a
hash value otherwise you can't hash them and put them in
as keys of this hash table. Well, protocols let us define
dictionary in a way so that we don't use any keys
that aren't hashable. So mandating behavior
passibility, for example, of the key it's great for
that. It's also great for sharing functionality
between very disparate types, types that you would never use
object oriented inheritance to make them share a base class,
but they are very similar, for example, string, array,
countable range. They are all collections of things. Now
they're completely different. Come one,
a countable range and a string are completely
different. But they are both collections, a string is
a collection of characters, accountable range is a
collection of integers if it's an accountable range of int.
So they do share some and protocols has a mechanism to
allow you to share that kind of similarity without having
to have all those thing inherit from some kind common
base class that knows about collections of things. You
see what I'm saying? And so in a way, protocols provide
multiple inheritance. Now, since protocols are only doing
the declaration of the vars and functions, there's no
storage of, of those things, so there's no
inheritance of data, it's purely just
inheritance of, inheritance of functionality.
All right, as I go through this explanation, it's
super important to understand that protocols are just
a type. Just like a class is a type, a struct, an enum,
a protocol is a type. A first-class type, just like
all the rest of these types, that's very important
to understand. All right, so let's dive
into what a protocol is. There's three parts
to a protocol. One, there's the declaration
of the protocol, just like you have a declaration of a class
or an enum or a struct. For a protocol,
that's just a list of methods with their arguments
and return values and a list of vars. That's it,
that's what a protocol is, it's a declaration. Second
part of a protocol though is a class or a struct or
an enum raises its hand and claims to implement those
methods in vars in that protocol. So there's the claim
to implement a protocol, that's the second part.
Cuz somebody has to actually implement those methods that
the protocol is a list of, and that's classes, structs and
enums, so they have to claim. And then the third part is
the code in those classes, structs and in in enums
that actually implements the protocol methods in vars.
Okay, so notice I'm mentioning vars.
The vars, the storage of the vars,
if they're not computed vars, has to be in some struct,
enum or a class. Because, that's the only places you can
have any storage, all right? Okay, so that's it, those are
the three parts of a protocol. Now I'm gonna take
a little aside, and not even gonna spend too
much time on this, but all the methods and vars in
a protocol are mandatory. If you're wanna
raise your hand and say, I implement
this protocol. If you're a struct or enum or
a class, you have to implement all of the methods and all
the vars in Swift. However, in Objective-C, that was
not true. In Objective-C, protocols could have
optional methods. Methods or vars in the protocol that you
could choose to implement or not. Now, that's quite a big
difference and the way Swift deals with that is it allows
you to put @objc, in front of you declaration of a protocol
to make it so that this is an Objective-C protocol
instead of a switch protocol. And the only difference
is that now, methods inside of your
protocol can be marked optional. Not optional like
you know question mark, exclamation point,
that kind of optional enum. I'm talking about optional
like you can optionally implement the method but you
don't have to implement if you don't want to. Okay, so that's
a special kind of backwards compatibility mode
to Objective-C, but in Swift, we don't do that. So
the only time we're ever gonna have this going on in this
class is when we're using iOS API that was designed back
in the Objective-C world. Specifically, delegation, which is that blind view to
controller communication. That is all Objective-C
style protocols and so when you start using that down
the road in the coming weeks, you're gonna see these
protocols that are marked Objective-C and some of
them are marked optional that you don't have to
implement a method. That's not Swift. That's
Objective-C compatibility. So just put that in
the back of your brain so that when you see it, you're
like, whoa. I thought I had to implement all the methods in
your protocol. Yes, you do in Swift. Only this exception,
all right, back to protocols. Okay, so how do we declare a
protocol? What does the syntax look like? Well, it looks
a lot like declaring a class. Protocol, SomeProtocol,
SomeProtocol is the name of that protocol just like
if you say at class whatever that would be the name
of the class. Notice that there's also
colon, InheritedProtocol1, InheritedProtocol2 actually it
can be any number of these. These are the other protocols
that SomeProtocol inherits. Okay, so there is inheritance
in protocols. It means a little bit different
thing then in a class. Okay, inheritance in
the protocol world means, if you wanna claim to
implement some protocol. You also have have to implement
InheritedProtocol1 and InheritedProtocol2. So it's
almost like it's a mandatory additional protocols you must
implement if you wanna say you implement this protocol.
We call that protocol inheritance. We call it that
because some protocol inherits the requirement to implement
these other protocols. Now, once you go inside
the definition of protocol, you can see there's no code.
Protocols have no code, they are not implementations, they are purely declarations.
And, in fact, when you declare that you're gonna have a var
be part of your protocol, all you get to say is
whether it's a read-only var, which would be get or a read-and-write var
with get set like this. So this one right here some
property would be get and set. So if you wanna implement
this protocol, your class or struct and you wanna implement
this, you would have to have a var that can be get and set.
Okay, that's what that means. Any methods that you
have in your protocol that might be implemented by a
struct and modified the thing have to be marked mutating
of course, right? Because again Swift
needs to know oh this method might change
the thing that implements it. Now there's a little trick
if you want, if you know this protocol's never gonna
be implemented by a struct, then you don't need the
mutating. But you have to mark the protocol to say this is
a class only protocol. You do that by saying colon class as
the first thing right after the protocol declaration.
You see how I put that yellow colon class up there? Okay,
so then if you did that then you would not have to put the
mutating because of course, you never have to put
mutating in a class, but then this protocol could not
be implemented by a struct. It could only be implemented
by a class, we rarely do this. Okay we don't usually make
a protocol, be restricted to a class and in fact 99% of all
the protocols you're gonna see in iOS could be implemented
by anybody. Okay, what else we got here. Yes, you can an even add an init to
your protocol. And that means if you're a class or a struct
that implements this protocol, you have to
implement this init. So, I have to be able to
initialize you. So, that was how we declare a protocol,
now let's look how a class or a struct says, raises his
hand and says yes, I will implement this protocol. All
it does is in a class after the superclass, you just say
comma all the protocols you claim to implement. It's as
simple as that, of course, for a struct there is
no superclass, so you would just say enum or
struct, whatever, colon the protocols you implement.
Okay, so that's how you claim. This is you raising your hand
and say I implement that. As soon as you say that the
source compiler is gonna check to make sure you actually
implement those methods and vars in that protocol. And if
you don't, you're going to get errors. It's gonna say you do
not implement this on protocol method whatever. And you're
have to go do that, all right? You can do any number of
protocols that you want. That's sometimes why we say
that protocols can provide multiple inheritance because
you can have as many of these ah, as you want going on
there. Ah, by the way, if there is an init
in the protocol and you're a class and you say,
implement that protocol, you have to mark there,
your init required. That's because you wouldn't
want a subclass to come along. Subclass you, subclass that
init away which is possible. It's possible to subclass and
make it so an init doesn't work anymore
from your superclass. You don't want to allow
subclasses to do that because then a subclass would no
longer implement this protocol and yet people might think it
does because it inherits from something that does. So, once
a class implements protocol, all it's subclasses have to as
well, so all init would have to be required.
Cuz init kind of special, in that it's possible in
your subclass, to make it so you no longer implement
a certain init with certain arguments. By the way, in your
reading, there's a whole bunch about making inits in classes.
It's probably the most complicated long part
of your reading for this week. You gonna have to fight
through it cuz it's pretty complicated. Inheritance
makes initialization really difficult because you've got
your own vars, you've got your superclasses vars. You've got
to get things initialized in the right order, all that,
it's quite complicated. So, unfortunately one of the
things the day you subclass can make it so you don't have
to form an init anymore. So that's why you have to
mark it required. One thing is you don't have to add your
conformance to a protocol. You don't have to add
all the methods, and vars in that protocol. In your
actual class declaration, or your struct declaration you
can add it with an extension, you know how we can extend
Int to add arc4random. Well we could extend Int to
conform to some protocol. Okay, that would
be perfectly fine. We would just say
extension Int :, the protocol we wanted int to
implement and then in there, and the extension we would put
all the implementations of the vars and
methods in that protocol. And this is actually quite common
to use in extension just for code book keeping. Well some
use an extension to implement a whole protocol. It's just
nice groups it all nicely. Okay, let's see an example
of using protocols like the type they are.
Okay, protocols are types. So let's see, here I have
a protocol called Moveable. It has one method in it
called, move to, all right? And I've created a class
called Car, which can be moved and another class called
Shape, like triangle or square, which can also
be moved. Now a Car and a Shape, incredibly different
things and one is class and one is struct but both of them
implement this same protocol. Okay, we're gonna see what
it looks like in the code, when you have this
kind of situation, which it's perfectly
legal to do this. And so I've created a couple of vars
down at the bottom there, a prius, which is a Car, and
a square, which is a Shape. All right, now I'm going to
declare a var, thing to move. It's gonna be of type
Moveable. Remember I said, protocols are types. So I
made thing to move be of type Moveable and assigned it
to prius, is this allowed? Can I assign prius to
the thing to move? Sure, because a prius implements
the Moveable protocol. It implements that move
to over there and it and it also claims to
implement the Moveable. So this is a perfectly legal
statement right here, but this has made a variable
that is not of type Car, or something like that, it's of
type Moveable. So, I can send messages like thingToMove.move
that's perfectly legal, but I can not say
thingToMove.changeOil, even though a prius is a Car
and Car implements changeOil. You see why I can't send
that to thingToMove? Because thingToMove is
a Moveable. It's not a Car, it's a Moveable. So even
though the car that happens to be in that variable, it can do
changeOil, I can't send it. Swift will not allow me to
send that message because thingToMove in Swift's mind is just a Moveable.
The only thing Swift knows that it can do is move to,
right? So this is the most important
thing on this entire slide, is that red thing. You cannot
do changeOil if you have a var which is of type thingToMove.
But what are some things you can do? Well, I can say
thingToMove equals square. Cuz a square is a Shape,and
a Shape implements Moveable. I could even have an array of
Moveable and put a prius and a square inside of it.
Cuz even though a prius and square are completely
different, one's a struct, one's a class,
one's a Car, one's a Shape. They're completely
not really different, they can be in the same array,
because they're both Moveable, you see that? And of course I
can have functions that take Moveables as an argument.
Here's slide, which takes a slider, which
is a type of Moveable. And inside I can say slider
move to positionToSlideTo, and then I can call
slide the prius, slide the square. Perfectly
legal to say because those are Moveables. Yeah, question?
>> Does that mean that move to function where Car and Shape have to do
the exact same thing, or do they just-
>> You mean, do the exact same thing like
perform the same action? Okay, so the question is, does this
mean that move to in Car and move to in Shape, do they
have to do exact same thing? And the answer is, no. They
can do anything they want. They can do whatever move
to makes sense for a Car or whatever move to
makes sense for a Shape.
>> If you assign it to squares. And you call move to, how does it decide
which move to to use? >> Okay, so the question is if I declare
this Movable protocol and then I say something
are movable. They are called move to, how
does it know which one to use? Well, because Swift underneath
knows which type it is, okay. When we say
thingToMove.something at that level we're
the programmer and Swift is enforcing that
it has to be a movable. But when it comes to actually
executing the code now Swift looks and says, oh it's a Car,
so I'm gonna use Car's moveTo. So Swift behind
the scenes is doing it. But the whole reason that
we're typing thing to move to be of type thing to move
is cuz we're telling everyone who's reading our code,
I'm just using this thing as a Moveable thing.
I don't care that it's a car. My code works on anything
that's Movable. So I'm just communicating to
people reading my code and to myself in all that but
Swift under the covers of course knows which move to
execute. It is possible by the way to have functions that
take an argument that has to implement multiple protocols.
So here the slipAndSlide is it's function it's argument x.
Those things have to implement Movable and they have to
implement something called slippery which is not shown on
the slide. So now I cannot say slipAndSlide(prius) because
while prius is Movable, it's not slippery. And so
this would not be allowed, Swift would complain here. But
that's how this &. That's how you make it so it requires
multiple protocol conformists. So you're getting a feel for
how we use a protocol init by declaring vars and
arguments to functions. So it's only scratching
the surface of protocols. Yeah, question.
>> [INAUDIBLE] The thing to move [INAUDIBLE] square.
>> Yeah. >> So when you call function [INAUDIBLE]
>> Here let me back up to Okay, so what was that? >> [INAUDIBLE]
>> Yeah. >> [INAUDIBLE] >> Yeah, up there. >> So when you call method on thingToMove. [INAUDIBLE]
>> Okay, so he's saying, look, I set thingToMove to be
a prius. Then I call move to, which one did it do? Obviously the prius. Then I
set thingToMove to be square. Now if I called move to,
it would use the square's one. So whatever the last thing it
was set to, that's what it'll use. Question?
>> [INAUDIBLE] have to take it up here, doesn't have to be
mutating actually? Are you allowed to just
drop that whenever valid- >> Great question, he's saying up here look in
Car, move to, no mutating. See, down here in Shape,
we've got mutating. Up here, we've got no mutating,
that's because Car is a class. Class is a reference site,
they don't need mutating, so it's perfectly fine. Any other
questions about this? Okay, excellent. So now let's talk
about the uses of protocol. Now we kinda know what
protocols are and how we can put them in our
API, how do we use it? Well, of super important to use is
this MVC delegation thing, right? We've got these
generic view things, like scroll views and table
views and things like that. And then we've got these very
specific controller things, how does a generic thing talk
to a specific thing without knowing anything about
that specific thing? Because obviously, when scroll
view shifts from Apple, it knows nothing about
Concentration games. So how do we do that?
Well, we use protocols, okay, so like simple, so this is
the will, did, shoulds and So let's take a look how
we do this, all right? So the data at. kinda six steps. First a view,
like a scroll view or table view, it declares
it's delegation protocol. That's just a protocol with a
list of all the well, dids and shoulds that it wants to send.
Remember protocols just a list of methods, right? So
it creates that protocol. Then the view, the scroll view,
it creates a var in itself, a public var, it's weak,
turns out. And so it's an optional and that is
of type that protocol. Okay, now that's great because now
the view whenever it wants to send will, did, should,
it just sends it to that var. And that works because that
var is of type that protocol, so of course it can
understand all the will, did, should. Then the controller
comes along says, raises its hand and says, I implement
that scroll view delegate protocol by putting it on this
little class line right there. And the controller sets
itself as that delegate var, it literally says that
delegate var equals self. So it's setting itself as
the delegate and that's legal. Because the controller
has claimed, hey, I implement that delegate and
so, and that delegate is of type that protocol,
all is good, okay. And then, of course, the controller has
to implement all the methods in the protocol. But
since this is an Objective-C protocol, it actually only has
to implement the none optional ones. Okay and
actually in delegation in iOS, almost all the methods
are optional. So usually, you only have to implement
the one you really want. But the controller implements
the methods so they did successfully fills out its
claim to have implemented it. And that's it. Now the view is
hooked up to the controller. The view can send will, did,
and should all it wants. It has no idea what kind
of class is doing it. All it knows about, it doesn't
even know if it's a class, could be a struct,
it knows nothing about it. All it knows is that that
thing implements its protocol, so it knows that it can
send well, did, and should to it through
that delegate var. Okay, make sense? So
let's look at the code for this with the example of
a scroll view, okay. Oh, yeah, sorry. Yeah, you find this
delegation everywhere in iOS, it was designed in Objective-C
world. In Swift, we have closure, which I'm gonna talk
about later in this lecture. And you could use closures for
some of this stuff. But even with closures, delegation
is pretty cool way to do this, have this blind communication.
All right, so let's look at the example of
UIScrollView. So this is what the var that the scroll
view would have in itself. Okay, this is a scroll
view var. It's a weak, why is it weak, by the way?
Because here the view is gonna have a pointers
to the controller, and we know that the controller
has a lot of pointers to the view. They'd be keeping
themselves in the heap. Okay, so what this view is saying
here is, I wanna point to whoever receives my will, did,
and shoulds. But if that will, did, and should guy
wants to leave the heap, that's fine by me,
just set this to nil and I'll stop sending messages
to it. Okay, so that way, the view won't keep
the controller in the heat. So anyway, notice this is
optional because it's weak. This is the delegate, it's of
type UIScrollview delegate. This is what that
protocol looks like, it's an Objective-C protocol.
It's called UIScrollDelegate, and it's got all
those funcs in there. ScrollView did scroll to here.
Give me the view for zooming over here. It's got
about 15 of them in there, if you go look at
the documentation. So now the controller comes
along, MyViewController, and it puts comma, UIViewCont,
UIScrollViewDelegate on the end and claiming I
implement that protocol. Then, in its probably
in its outlet setter, we'll see an example of this
today. When that scrollView gets hooked up on iOS,
in the didSet of that, it probably says,
scrollView.delegate = self. This is a legal statement to
say because delegate, which is this thing up here, is of type
UIScrollViewDelegate. And MyViewController says over
here that it implemented that. So it's legal for it to set
itself as the delegate. Okay, and once it does this,
they are hooked up, and now the view can send will,
did, said to the controller all at once. Okay, so
that's delegation, probably one of the most
obvious uses of protocols. What's another one? Okay,
remember, I talked about that key in the dictionary in
the hashable business, right? So of course, the key in the
dictionary has to be hashable. How do we enforce this
in our API? Well, there is a protocol
called Hashable. It inherits from another
protocol called Equatable, and that's because you know
anything about Hashtables. When you hash something, it's
likely to be a unique hash but it's not gonna guaranteed. You have to follow it up by
actually using equals to see if the two things are actually
equal, so we hash in works. So that's why Hashable
inherits from Equatable. So if you wanna be Hashable,
you also have to be able to check if you're equal to
something else. Now this is of super simple protocol,
it only has one var in it. It's a get only var,
which is the hash value. Some integer, that hopefully,
uniquely represents you, that's what this is. Now,
let's look at Equatable, okay, which, cuz that's another
protocol that is inherited and thus, required here.
What does that look like? That's a very simple
protocol as well. It has one function in it,
one method, which is static. Meaning it's
on the type, like get unique identifier was in card,
remember that? And its name is ==. In Swift, remember, names
of methods can be like emoji. It's not required to be
alpha numeric characters, so they chooses to be called ==,
which is kind of cool. And the arguments are a left-hand
side and a right-hand side, notice that the type itself,
this is a type method, so self is the type. So if this were
implemented by int, if int were implementing equitable,
self would be int. Okay, cuz it would be a type
function on int, and so the left-hand side and the right-hand side would
be ints of course. So this is basically saying, that
your type has to have an == method that compares to ints,
or whatever you are. And sees if they're equal, and returns
a bool, yes or no. Okay, now, this is, that's so that's what
you have to do to be Hashable. But what's really cool,
that the == operator in Swift, okay, when you say like x ==
y, that's actually not like built into Swift or anything.
All that does is look for this method, okay.
So any two, any class can use, can do == as long as you
implements this protocol. So this protocol is required to
make the Hashable work, but it also makes the == operator
work. And we're gonna see this in our concentration demo when
I go to implement this. So, now that we know that Hashable
means you can be a key in a dictionary,
how is this expressed? Well, dictionary which is a generic
type, right? It's got these two types, the type of the key
and the type of the value. It declares itself as
Dictionary<Key: Hashable, Value>, and that means that
key can only work there if it's Hashable. So this is how
we use protocols to constrain these generic types, right?
Now Value, right there in the dictionary, it doesn't
have any colon anything, so Value could be any type. You can put any type
whatsoever as the value in a dictionary. But the key
has to implement hashable. So let's go into concentration
and do this very thing. What do we have in a
dictionary? Well, we have one dictionary, it's that emoji
thing. It's, keys are int, ints are hashable so
we're good, and the values are the emoji. And the int
is our cards identifier. Remember that card identifier?
So, I don't want to do that. I want my keys in my emoji
dictionary to be cards. I want to look up
the cards directly, I don't want any of that
looking at the int thing, that's ridiculous. So, if I go
over here to view controller. Here it is right here,
actually. See, here is my emoji thing,
int to string, int keys, string values, this has to be
hashable, it is right now. But I don't want that, i just want to look up
the cards directly. I don't want to use
the identifier, I am just going to take this
identifier right off of here and when I do this,
I'm gonna get errors. And why am I gonna get errors?
Because card, see right here? Let's see if we can make
this a little wider so you can see it a little
better down here. It says right here that
we cannot substitute, oh, sorry. Okay, it says
cannot subscript a value of int string with a card.
Of course okay, I said that the keys were int,
and here I'm trying to do it by card. So
I'm just gonna say okay, that I'm gonna have them be
cards instead. So I'm just gonna have my dictionary, have
keys of card,values of string. So now, what do we got here?
It says type card does not conform to protocol
hashable, it worked, okay. I tried to make a card be
the key of my dictionary and it says, no, card does not
conform the protocol hashable. So the declaration dictionary
which you can go look up in the documentation, it says card, the key has to
be hashable and it wasn't. So let's go make card
be hashable, so we won't have this
error any more, so I'm gonna go over to card,
here it is. And here is card, I'm just gonna say,
okay, card is hashable. So I just made card hashable,
now of course Swift is gonna look at this and say whoa wait
a second errors here, card does not conform to hashable.
Card does not conform to equatable which it doesn't
because I don't implement those methods the hash value
or the equals equal thing. What's really cool in Swift,
look at the fix button here. Do you wanna add protocol
stubs? Oh, yes please. And look what it did. It added
a stub for hashable, and a stub for equitables. Now I
just have to implement these. So, making hash value,
which has to be get only, that's easy. I'm just gonna
return my identifier. That's an extremely good hash for
a card because it's unique for that card. And the equals,
equals is also really easy too. I just have to
compare these two cards. I'm gonna return if the left
hand side's identifier equals the right hand side's
identifier. So there we go. I've implemented hashable and
equatable. So now if I go back to
my view controller, will that error be gone?
Here's the error over here. So let's make Xcode recompile,
and sure enough look, no problems. It's completely
fine with that, okay, and that's because I made
this hashable. Now, we can get another
benefit of this. Let me go back to card. And I
told you before, I didn't like that this identifier was
public. I see no reason for it to be public.
And we know we don't need it now to look in our emoji
thing, so I'm just gonna make it private. And let's see
if that's gonna work. So is private gonna work? Let's build and see. Oh, no,
we have some errors over here. So let's go look at them.
Here's an error here, it says, identifier is inaccessible due
to private protection level. That's right,
I made identifier private. What's it trying to do here?
Oh, here I'm trying to see if two cards match, oh, well
cards implement equatable now. So I can just go like
this card matchIndex =cards[index] because I can
compare cards directly, now. They implement equatable,
right? Now if we build,
that's gone away. So we had a huge benefit by
making that equatable and hashable. We got to use it
directly in our emoji thing to make that code look nice and
look how nice this code looks. Compared to how it used to
look with that identifier, right, so we cleaned up our
code on the use side and we were able to go
back to card and make something that really
didn't need to be public, completely private.
Pretty cool with that? Okay, so you're starting to see the
power of protocols a building here. So let's go back and
look at even more protocol stuff that's cool.
All right, there is the demo, okay, let's talk
a little about this multiple inheritance kind of trick.
Consider countable range, remember countable range zero
dot dot less than something creates this countable range
of ints from zero that thing minus one. Countable range
implements a lot of protocols. If you go look at countable
range, it may have 12 or 15 different protocols. Well,
two of the most interesting ones it implements
are sequence. right? A countable range is a
sequence of numbers from zero Which makes sense, to something else.
And also collection, because accountable range is
also a collection of things. If it's accountable
range of int, it's a collection of int or
whatever. And so these two protocols define
a lot of fun things like sequence defines really
only one method, next, which goes to the next
thing in the sequence. But that let you do for in,
anything that implements the sequence protocol could
do for in, okay, just like anything that's hashable can
be a key in the dictionary. And then, collection
has even more methods, all the indexing, sub-scripts,
all that stuff is all in the collection protocol.
Now, why do we do this? Why do we have countable range
implement all these protocols? Well, because array implements
all those as well, and so does dictionary and set and
string. All these classes that are sequences of things
or collections of things implement all these
exact same things. And so I as a programmer only have
to learn these things once. Okay, I only have to learn
learn for in once. And all I need to know is that
the thing I wanna for in over has to be a sequence.
So I can just look to see does this thing implement
that protocol? Or I can even invent my own
things that I can for in over. All I have to do is implement
the sequencing, the sequence protocol which really only has
one method in it ultimately. So that's one good reason why
because I learned indexing, indexOf to get things and
I can now do indexOf on a string and find
a character on a string or I can do indexOf in
an array and find in there. I can even do indexOf on
accountable range not super valuable but if the count
of a range wasn't of int, then it might be interesting
to do index of to try and find something where it is
in the range, okay. But there's a bigger and
better reason why we do this. And that's because there's a
little bit of magic in, or not magic but really good design
implementation in Swift. Which is that for protocols it
is possible to provide default implementations of methods.
Default implementations of methods. So
this makes it possible for us to implement indexOf for all of these classes in
one place. So that's why, again, we think it's like
multiple inheritance cuz they're actually gonna inherit
implementation. So where do you put this implementation?
Cuz we know protocol is just a declaration method with
no implementation. Well, the answer is you put them in
an extension to your protocol. You know how we can have
extension int? You can also do extension protocol.
And in that extension you can implement as many other
protocol methods as you want. Now you're little bit
restricted there because extensions can't
have any storage. We know that, right?
No vars, so that's certainly a restriction. Also in
the extension, really the only method you could use to
implement it are the methods in the protocol or
in the protocols you inherit. So, like collection, happens
to inherit from sequence so a lot of the collection
methods can be implemented using the sequence by just
going through the whole sequence of the collection and
finding things, or whatever. So if you look here, sequence
has that one important method next goes to the next
thing in the sequence. But sequence also has all
these other methods, contains which finds whether
somethings in the sequence, forEach which kind of does
what for n does. Joined by separator, which takes all
the things in the sequence, converts it to a string and
creates a big string joined by a separator like
comma or space. It has min, find me the minimum thing
in this sequence, max, the maximum thing. Even has
functions that take closures, which I'm going to talk
about soon like filter and map, that do major
operations on the sequence. Well, all the implementation
of all those things contains, forEach, joined, min,
max all those things. Those are all implemented in
an extension to sequence that Apple provides so that if
you want to have a sequence, as long as you implement that
next thing to go to the next thing in the sequence you get
implementations of all those other things for free. That
also means that array, and set and dictionary, and countable
range are all sharing the implementations of min and
max and all these things. Now, these are only
default implementations. If an array you actually
implement min or max or something like that. Maybe you
can do it more efficiently than the default
implementation or whatever. You can do it.
But it's replace the default implementation that's
in the extension. Okay, but are you getting
the feel for what I'm saying? Why this is valuable to have
these default imitations. You can have these really
powerful protocols and you'll only have to implement
one of the methods and you'll get implementations for all of them. And then they're
shared across all these weird different classes like array,
string, countable range. They all get
all these implementations they don't have to implement any of
that and they get it for free. Now all of these that I'm
talking about generics, value typing,
var versus let immutability control, constraining things
by protocols, extensions or protocols. All these things
add up to support a kind of programming called functional
programming. How many people here have heard about
functional programming? About half of you, see? So I encourage you at
your Stanford careers, go take a class and learn
about functional programming. It's really kind of
the evolution, some would say, of object-oriented
programming. It's a little different way of thinking
about dividing things up. It allows you to get things
like multiple inheritance without things getting
completely out of control. Since things aren't
living in the heap, you don't have 20 pointers to
something and you're not sure who's gonna modify things, so
your programs are much more provable,right? You can prove
that they do what they do. You don't have to worry about
what pointers are messing this thing up in the heap lot
of advantages of it. Now, what's really
cool about Swift, it supports both these
programming models. Object oriented programming,
which is all I'm gonna use in this class because
that's the prerequisite for this class, and
functional programming, which is what pretty much
all of the foundation of foundation framework like
dictionary, array, string, all those things. Those are
all built with a functional programming model. Swift
supports them both equally, which is really great.
So it's kind of your best, it's a mix between
languages like Haskell, how many of you have ever
heard of the language Haskell? Okay, same people who know
about functional programming, right, so Haskell which is a
purely functional programming language, and you know, like
Java which is a purely object oriented language. It's a good
mix of two. That's all I'm going to say about functional
programming, just so that you know it's there.
That's it for protocols. When you see it all in action
it will make a lot more sense. Hopefully it makes
a lot of sense. Now it's really,
protocols are very simple. Just those lists but we can
use them in powerful ways, all right. Now let's go talk
for briefly about something that's not super important,
but strings are in almost every app. And I just wanna
talk a little bit about how we index in to strings,
cuz it is not what you would think it is at first glance.
In addition to string, the struct string, there's
another struct in foundation called character. Now,
a character is what we humans would perceive of as
a character. On the screen or whatever. However, a string is
not a sequence. A string is an underlying built with
character, it's built with unicodes. Okay, unicodes are
just little bite size chunks that can represent pretty much
any language on earth. Okay, it's international.
You know, like ASCII. We used to have ASCII, you
can only really do English. Unicodes do all All
the languages on Earth. So that's what's in a string.
So we have a little bit of trying to bridge the world
between Unicodes and characters. So let me
show you by example here, if we have the word cafe,
C-A-F-E, right? That we perceive to have four
characters, the C, the A, the F, and the E with
the accent on it. But it might be represented by
five Unicodes, because there's a Unicode which is put
an accent on the previous character, okay, so, it could
have five Unicodes. Well, So how do we deal with this?
Well, the main problem or the main ramification
of this is that string cannot indexed by int.
Because if we had a phrase, cafe pesto, right there. One
of my favorite pizza joints in Hawaii, Cafe Pesto.
The p is that in index 5 or is that in index 6? It
depends on whether that e is an e with an accent or two
Unicode here there's the e and the accent. So ugh,
what are we gonna do here? So what we do is we don't
index strings by int. Instead strings are indexed
by their own special type, String.Index.
And that's the only way to use subscripting
on a String, you need a String.Index, not
an Int. So this makes this, people don't like this. They
wanna index Strings by Int and I understand why, but
you can't do that. So what does that mean? Well,
we have to get an index, so how do we get an index? Well,
you can get the startIndex of a string, you can get
the endIndex of a string. You can also use index(of:)
a character to find the first index of that character. So
there's a bunch of different methods and vars on
strings to get an index. Once you have an index in
your hand, now you can offset that index by an Int. So
if you want to get the fourth character in the string you
have to get the startIndex and then offset it by three to
get to the fourth index, from the first to the fourth. So it
was let's get to the code that makes that work. So here's my
pizzaJoint cafe pesto here, I'm gonna get the index of
the first character by saying pizzaJoint.startIndex that's
not an Int. It's of type String.Index. Now I'm gonna get the fourth
character by taking the first character and
offsetting it by three. Now I've got the index
of the fourth character. Now I'm gonna get the actual
fourth character by using subscripting. PizzaJoint,
subscript, fourth character, index, fourth character index
of subtype String.index, not of type int. And
I can also do index (of:), so here I'm gonna say
The firstSpace character in cafe pesto is
pizzaJoint.index(of: " "). Now the reason I have
to say if let there is, of course, index(of: "
") could return nil. There might not be any
spaces in pizzaJoint. There happens to be but
there might not be. So it would return nil then.
And so I'm doing if let Now, I've got the index
of that space. Now I'm going to get the index
to the start of the second word, the word pesto there.
And we do it by indexing that
first space over by one. And I am gonna assume there is
one space in between words, go to the next one. And now, I
am going to get the whole word pesto by indexing string with
a range, okay. The index of a string doesn't have
to be an individual index. It could be a range of
indexes. Now you notice to make a range, I'm using
exactly the same dot, dot, less than thing that I
did when I made that countable range of INTs. Ranges are
generic types. They don't have to be of INTs. They can be
a range of string.index. Perfectly legal. As though we
saw before, we had a range, countable range, of double
precision floating points. Remember that at the beginning
of the last lecture? So range is generic type. So it is perfectly legal to use a
range inside subscripting, and that's true anywhere there's
subscripting of collections. These methods,
by the way, these index, offsetBy, those are not
methods in string, those are methods in
collection. Okay, the selection protocol,
string may or may not implement it itself,
it's up to the string. The collection protocol is
also generic, and so the index you use in the collection
is configurable. For a string it's a string.index.
By the way, another way to get the second word, and
probably what we would use, is a more complicated method like
component separated by. So components separated by, is a
collection method. You give it an element that would be
in that collection and it will create an array
with all the things, all the elements in
an array grouped and separated by that thing. So
this would give you an array of all the words separated by
a space. Then I would grab index one, which would be
the second word. Okay, so I'm showing you how to do
the indexing directly, but a lot of times we use
much higher level things in string.
Okay, so string, like I say, is a collection of
character. Just like an array is a collection of
arbitrary things, accountable range of int is
a collection of ints, and so all that stuff is coming from
collection. And collection is a sequence we know, so
of course you can do for c and if s is a string c will
be a tight character and you can iterate with a four loop
through all the characters. There's also really cool
initializer for array that takes a sequence, as
the argument. So you can say, array of any sequence and what
it will do is go through that sequence and take each element
and put it into the array. So it creates a big array for
you, cuz it's an array initializer.
And in the array will be all the
elements of the sequence. So since a string is a sequence
of characters, if you say array of string, you'll get an
array of its characters. And now you can use int to index
them if you want. So sometimes people, they get tired of
all this string index stuff. They just create an array
of the characters. And then now they can use ints to
get at the various characters. That's kind of
a trick as well. Ah, remember that a string is
a value type, it's a struct, we almost always use immutable
strings like let s equal something, we're working
with it immutably, but of course there
are mutable versions, mutable methods. There's another kind
of protocol called range replaceable collection. That's
kind of a collections that can be mutated. Ranges of the
things can be replaced. And so here's a method in that
insert the contents of a collection of characters,
which string is, at an index, so in this case it's
index of space, that's gotta be
a string.index right there. And so I can insert the word
foo into Cafe Pastel. So you can do this,
we don't do this that much. We tend to just use them
immutably, and we use plus to add them together and stuff
like that. So that's string, it's that little weirdness
about the indexing. Now, There are lots of other
methods in string, I want to show you an interesting
thing about one of them. So here is replace range,
which, my pointer doesn't work any more, but
if you look at replace range, do you see where it's red,
right there? That's that dot, dot less than thing, look,
I forgot to put something on the left. So what does
that range go from? Okay, we know it goes to s.endIndex.
Well, Swift is smart enough to know that that is a range of
str-, the strings indexes, so it will automatically put
start index at the beginning. And if you left off
the other side, it will automatically put
ending index at the end. So you can oh, leave those
arranged things open ended. But Swift has to be able to
infer the type of whats going on there. So
you can't always do it, but in that case you could.
Okay, so that's string. I guess
we'll go like this. So, to make this all understandable
let's go back to our, um, concentration.
And we're gonna make this thing right here,
this emoji choices, which is currently
an array of emoji strings. I'm gonna change that. I'm
gonna copy and paste it here. I'm gonna change it from being
an array, to being a string. So I'm gonna take off
the array things. And I'm gonna get rid of all
of these little commas. And now emojiChoices is
gonna be a string. And I'm gonna have
my code be the same, but instead of grab it out of
the array, it's gonna grab it out of the string. Now as soon
as I change that to a string, look what happens.
Cannot convert value of type int to expected argument,
string.index. Okay, remove at, right here, which we moved
to remove the thing, where do you think that's
declared? Anyone wanna guess? Is that an array thing?
A string? No, that's in collection, range
replaceable collection, right? So it's in a protocol
somewhere that both string and array implement. So you would think it
should just work here, because emoji choices, yeah,
now it's a string, but remove at is in range replaceable
collection, they're range replaceable collection.
Why does this work? Well, because strings are not
indexed by ints. And so this doesn't work. So we
have to create a string.index, I'm gonna do that. I'm gonna
create a random string index and I'm gonna do it by using
that index offset by things. I'm gonna say, emojiChoices
give me an index, I'm gonna use this first one right here,
index offsetBy, which let's us take a known index and
offset it by something else. The known index I'm going to
start with is the start index of this thing, so,
emojiChoices.startIndex, and I'm gonna offset by a random
int. So that offset, that can be by an int.
That happens to be because this particular collections
strides, or the distance between indexes is an int.
Even though the indexes themselves are string
dot index. So I do that. So now I'm gonna put random
string index in here and all should be well, right?
Because remove at, oh no, still doesn't work.
What's this error? Cannot assign value of
type character to string. Okay, this is a dictionary,
this is looking for strings. Okay?
Why is this a character? Well, because a string is
a collection of character. So if remove at, I'm removing a
character. So no problem here, we know that we can
probably convert types. And we're gonna do that by using
a string initializer that takes a character
as an argument and returns a string with just
that character in it. So now we converted it. So, that's
it. That's all we need to do. And I did this just so you can
see all that offsetBy, and all that stuff so you could
see how we would index this into a string instead of
doing here. So, hopefully this is still working. Yes,
it is. It's still working. Okay? Got it everybody?
Questions about this? So this is the important line
of coding in here. And also a little bit of
understanding that even though remove at is in collection
range, replaceable collection, the types matter,
because it's a generic type. Collection is a generically
typed protocol. All right, let's go back. Oops,
not there. Here, no. Here. All right, let's talk about
another class that has to do with Strings which
is NSAttributedString. For NSAttributedString is
a String that every character has a little dictionary
associated with it. And that Dictionary can have
lots of little keys and values that say how to draw
that character on screen. Okay so those Dictionaries can
have things like font, color, all these things. Okay that's
what an attributed string is, right? A String with
attributes of each character, right? The Dictionary that
each character can have. The keys are well known, you can look them up
in the documentation. The values depend on
the type of thing, so sometimes the values are UI
font, which is a font thing, sometimes the values
are colors, sometimes they're floating
point numbers, etcetera. Now a lot of times, we use the
same Dictionary for big long ranges of characters, right?
You know like if users select some text on screen and they
say make it orange, then we're gonna have one Dictionary for
all those characters so not like every character has to
have a different Dictionary. And in your homework, the entire String is going
to have one Dictionary. So I've given you a really easy
use of attributed String and you're gonna see why I did
that in a moment here. Once you have an Attributed
String with all those font and color for each character, now you can use to it to set
the text in UI label, or to set the title of
a UIButton. Or next week we're going to learn how to draw it
directly on-screen, in our own drawing classes. Okay?
Here's how you create and use an AttributedString,
and it's got red. Any time you see red on my
slides, it's kind of like oh, look out. And this is red
because it's really weird. Okay? And why is this really
weird? Well, it's really weird cuz this is an Objective-C API
that's trying to live in the Swift world, and
that requires a little bit of compromise. There's not
a lot of APIs like this. Every time iOS comes out, the
next one I always hope there can have
Non NSAttributedString, just regular AttributedString would
now fix this, but they haven't done it yet. So, when you're
declaring those attributes, the little Dictionary that
goes on each character, you have to give it
an explicit type. You cannot let the type
be inferred and that's because those
values could be font, colors, whatever. So, a source
can't infer what the value of the Dictionary is. So they
type that you're gonna use here is Keys
are NSAttributedString key. And you can just look that
up in the documentation, see what the choices are. It's
things like font, underline, strike through, all the things
you would think to be. And then the value is Any.
Okay, so Any is a special Swift
thing that means anything can go here, any
struct, any class, any type. So this is very non Swift
cuz Swift is strongly typed, and
here's there's no type. So you would never have an API
like this in Swift. Okay, if this weren't an Objective-C
API brought forward, so you would never have it. Can anyone think of what
we would have instead of any right there? Why type
of thing instead of any? How about an enum with
associated values? Right? If you had an enum, and one of
the enum things is the font, then the associated
value could be a font. Or what if the thing was
the color of the text? Then the associated
value could be a UI color, you see? How we
would do that in Swift? Well, we didn't have those enums
when Objective-C did this so, we're stuck with Any. Okay? Never use any in your data
structures. Any is purely so we can deal with these old
Objective-C things. Alright, so, now that I have declared
the type to be a dictionary with NSAttributeString keys
with Any as the value, then I can make my dictionary and
so StrokeColor is obviously the color that it strokes
the outside of the text. There's also foreground color
which is the color it puts the inside of the text,
the fill color. There's also background color which is like
if you had a highlighter, that's the background of it.
There's also StrokeWidth. StrokeWidth, if it's
a positive number, it outlines. Okay? If it's
a negative number its solid. The character is solid. Okay?
You'll see that. We're going to do a demo of that. Okay? So
you can do this. You can put these things in here for your
homework. You'll use stroke, color stroke with maybe a
couple others. In the hints I kind of tell you the things
you need to use. So, I don't want you wasting
too much time looking up all these things. Then you can
created an attributed string with that Dictionary for
all the cards in the string by just saying NSAttributedString
using the initialize of the text of string and the
attributes. So that creates an attributed string then
I can take that attributed string and like put it on
my flipCountLabel, right? Now my flipCountLabel, so
this particular ones since I'm using a positive stroke width
this would be outlined text. On my flip count.
So I'm gonna do a demo and see what that looks
like in a second here. I just briefly want to talk
about the peculiarities of NSAttributed strings
since there's an NS. You know, you see that NS at
the beginning and you know, hmm, this is kinda
of an older API. It's not a String. It's a
totally different thing, okay. An NSAttributedString is
a class that's completely different. Then String worked
completely differently. Because it's a class
not a struct, you can't make a mutable
one by just using var. You actually have to
use a different class. And it's mutable Attributable
String. If you actually wanted to go set
the dictionaries on certain characters individually, you
would need to use a mutable Attributable String,
a completely different class there. Also NSAttributedString
was built and constructed internally with
NSString in mind. NSString is the old Objective-C String.
NSString and String have a little different Unicode and
coding underneath. So, when you have wacky characters
like emoji or cafe in there, the indexes into them might
not quite match up. And there's automatic bridging
between string and NSString so if you have any iOS API
that takes an NSString as an argument,
you can just pass a string. It just automatically
works except, that this little encoding
doesn't always get fixed up quite right so the
bottom line here is if you're going to be trying to be index
into an NSAttibutedString and you have wacky characters like
emoji or cafe, the indexes might not line up very well.
Now in your homework you don't have to worry about any of
that cuz I'm only gonna ask you to make the entire String
have the attributes. So you're not even doing
any indexing into there. But in the future, if you're
ever using AttributedString, just be careful about the indexing in there if
you've got wacky characters. All right, the demo. So,
I'm gonna do this flip count thing and show you what this
looks like real quick here. Where do we do that?
That's right up here, right? Here's where we set
our flipCountLabel to Flips: \(flipCount). So I'm going to
put attributed text there. So I'm going to let my
attributes, which I have to type to be
[NSAttributedStringKey: Any] =. And now I can just
put the things I want, so I want the StrokeWidth
to be 5.0. Okay, that's a fairly
thick stroking width. And then what was the other
thing I said I wanted to, oh, the color. So the strokeColor
and I'll go ahead and use the color literal here.
Use our favorite color, which is orange.
Halloween color. Okay, so now if I wanna use these
attributes to draw the text on my label, I'm just gonna
create an attributedString. So I'm gonna say,
let attributedString equal. By creating an attributedString
with its constructor, I want the one down
here at the bottom. It takes attributes and
string. Okay, so what you're gonna do for your
Homework-2. I put the string, it's just this string
right here. And attributes is these attributes
I just created up there. And then instead of saying
flipCountLabel.text, I say attributedText. And for
a button, you would say set attributedTitle for
state instead of setTitle for state and that equals
this attributedString. Okay, so that's all you need
to do. So let's run this and we're gonna see something
a little bit kind of odd about this, though. A little
bit unexpected, I think. So here we go. Hey, it didn't
draw anything different. Look at that, Flips: 0. That looks exactly like
before we made this change. But, oh, look, if I click, now
I'm getting the outlined font. See how it's
outlining my Flips? So why when it first came up
didn't this work, right here? Yeah because this right here,
this little =0, this initialization, that
does not cause the didSet to happen. Okay, so that's
an important thing to know. When you initialize a var,
it does not invoke the didSet, only later settings
of flipCount. So how can we fix this?
Well, I'm gonna take this and put it in it's own little
func. Okay, private func, I'm gonna call it
updateFlipCountLabel. I'm gonna put that in there,
and I'm gonna call this
updateFlipCountLabel in here. updateFlipCountLabel, and where's the other place I need
to update the FlipCountLabel? Well, here's another thing to
learn that's kind of cool. This, right here,
is the label, right? If you remember in our UI over
here, that this right here is connected up to that,
right, it's connected. Now, this connection gets made by
iOS when you start up your UI, right, it makes
that connection. Well, when it makes that
connection, it's setting this. So guess what,
we can use didSet here. When you have an outlet, didSet does get called when
that outlet gets set by iOS, when this connection right
here gets made, that gets called. So here we can update
our flipCountLabel here. Okay, so now that flipCountLabel is
gonna be updated when we first connect that flip zero and
also every time we change it after that. Got it? All right,
back to our slides. So, you learned two things there,
right? You learned that that equal zero didn't cause didSet
to happen and you learned that on an outlet, you can didSet.
You can set things because that outlet just got
hooked up for you. All right, last really, really
important thing here. We need to really rush, because
we're running out of time. This is very important, this
is about functions as types. So I believe the supreme court
a couple of years ago that functions are people too.
And so functions get the full treatment of a type that
any other type gets. You can use a function as a
type anywhere, all right? And you do it just by declaring
an argument to a function or a var, whatever,
to be of type function. And you specify
the arguments and the return value of
that function. So it'll be a variable of that.
So you can do this anywhere a type is allowed. Let's look
at an example. I have a var here called operation. It is
of type function that takes a double and returns a double.
So the syntax here is awesome. It looks just like declaring
a function that takes a double and return it, just no names
of arguments, but the types are all in there. So this is
how you declare a function. You declare a var
that is a function. You can do it for variables,
passing to functions, parameters, everything.
You can always do this. So you can assign this var, it's
exactly like you would think. I'm gonna assign
operation = sqrt. Okay, sqrt is a function that takes
a double and returns a double. So it's perfectly legal for
me to say operation = sqrt, all right? And how do I call
it? Well, I call it just like a function. Operation of 4.0
is gonna call, in this case, square root of 4.0, because
operation is a function var, that's holding a functioning. That function is square root,
okay. Could not be simpler. Everybody cool with this? Now,
other languages have function pointers and all this stuff,
but it's all pretty tortuous to use. But in Swift, it's
a first class citizen, okay. Functions are a first
class citizen. They will be the argument
to many iOS methods, functions will be.
Okay, now a lot of times, the function you wanna past
doesn't already exist. So forces you to go write
a function to pass in. For example, let's say I had the
function changeSign, okay. So there's no thing like square
root that does change signs. So I have to write a really
simple little function here, And all it does, it takes
a double and returns a double, changeSign. just like square root. And
it returns minus the operand. So I'm changing the sign of
the operand here. So I could use it in the same way. I've
got an operation. I'll just say operation = changeSign
instead of operation = sqrt. But this is kind of
a lot of typing and annoyance to have to go
create a whole function to do changeSign when all I want is
for that thing to change sign. So we can do,
use what's called a closure. How many people have
heard the phrase closure, know what a closure is? Okay, about half of you again.
Okay, so closure is really like an inlined function. It's
a little special in that it captures the state around it
but it's essentially an inline function. So what does the
syntax look like if we wanted to take changeSign and just
drop it right into the middle here instead of having
a separate function? Okay, how would we do that?
All right, we're just gonna pick up all
of changeSign except for its name and move it down. Watch, okay, so I just put
it right in there. Now I do have to make one syntactic
change here, very important. I have to move that first
curly brace, the one right before the word return. I had
to move it to beginning and replace it with the word in,
so watch this happen. This moves to the front, put
the word in. That's it though. Once you make that small
syntactic change, you can drop any function right in there.
But it gets much better on this because we have type
inference in Swift. And Swift can absolutely go to town here
because it knows a lot about what's going on here. For
example, Swift knows that this operation thing returns
a double. It knows that, so you don't need to say returns
double there, so just take that out. it also knows that
the operand is a double, so you don't need to say that
that operand is a double, you can take that out, right? It also knows that this
function returns something, so you don't need the keyword
return minus operand there. So we can take that out as well.
Okay, and even more, Swift knows you want to have
these embedded functions all the time, and it's a little
annoying to have to think up a name for the argument
to this thing. Okay, that operand word that I had to
think up there. So it lets you substitute $0 for the first
one, $1 for the second, $3 for the third one. So I'm gonna
replace operand there with $0. And when I do that, I don't
need the in anymore either. Now you see the power
of the closure. Okay, I wanted to have an operation
that changes the sign. And I barely had to type
any more characters than the changeSign, just the curly
braces around it. And that's still gonna do, operation 4.0
is still gonna do -4.0. Okay, so you're gonna be using
closures all the time, and you're gonna be using
these $0, 1, $1, $2, as the argument names. And
it's gonna make it so that you have to type very little
code to put the things in. Now why do I want closures,
why do I want functions as arguments? Well,
it's usually because you, you're passing something to
a function that wants to know what to do. A function is
a great way to tell it what to do. Now why might it want
to know what to do? Well, maybe it wants to know what
to do if there's an error. I'm gonna do something, it
might generate an error, and I want to call a function
in case there's an error so you can handle this function.
Maybe it wants to know what to do if it's gonna do something
that takes a long time. It's gonna go on
the network and download something off in the
background and when it's done, it wants to tell you. Okay,
we call a function to do that. Another thing is, it might be
doing something repeatedly, it wants to do the same thing
over and over. So it's asking you to tell it what to do over
and over. So let's take a look at an example of that last one
there, doing something over and over. So array,
naturally, it's a collection. Well anyway, array has
a method called map. And what map does, it takes only
one argument, a function. And it applies that function to
every element in the array and creates a new array. So map returns a new array where
every element in the array you send it to gets operated
on by some function. In other words, it's a way to
pass a function into array and tell it to do it on
every element. Okay, that's what map does, so how
does that work? Here I have an array of floating point
numbers. They happen to be the first 5 prime numbers,
right, 2, 3, 5, 7, 11 here. And what if I wanted to have
an array of all negative 2, negative 3? Well I would
just say negativePrimes = prime.map, the only argument
is a function that does change sign, right? Just like we
had on the previous slide, change sign. And
now we're getting a new array, negativePrimes is gonna be
a new array with -2, -3, -5. You see how I used map there?
Now, list another example, maybe inverting the primes.
So I'm gonna propriate the closure, 1.0 divided
by $0. Now I get 0.5, 0.33, maybe I even want to
convert it to a string. Perfectly legal, doesn't
have to be the same type. I can return array of string.
Now notice the yellow things up there, changed a
little with each line. You see the first one map parentheses
the closure. The next one map open parentheses, closed
parentheses then the closure. And the third one map, no
parentheses and the closure. Okay, this is using what's
called trailing closure syntax, okay, we almost always
use this. The rule here is, if the last argument to
any function is a closure, you can move the closure
outside the parenthesis of the arguments. So see in the
second line, inverted primes? How I moved the closure
outside? And furthermore, if it's the only argument, then you can just get rid of
the parentheses entirely. That's the third line, okay.
And this is just to make our code look nice. So we don't
have extraneous parenthesis in there that we don't, beccause
we've got the curly braces, we kind of don't need
those parentheses. Okay, so you'll see that in iOS when
an argument to a function is a function, it'll usually
be the last argument. So that you can move it
outside if you want. It's optional,
you don't have to. All three of those are
perfectly valid syntax right there. Okay,
another cool use of closures, property initialization. What
if you have a property and you wanna initialize
it to something but it's not like you just can't
do one line thing, right. You can't say equals five or
equals something simple. You need to do two or three lines
of code to get it initialized. No problem, you're allowed
to set the property equal to executing a closure. Now that
closure is automatically going to be a closure that
takes no arguments and returns the right type to
initialize that property. So you can do anything you
want in that closure and just return something
of the right type. And then you execute it right away
with open parenthesis, closed parenthesis. See how there's
the little open parenthesis, closed parenthesis there right
at the end of the closure? That's just gonna execute
that closure right there. This is especially cool with
lazy properties. Because it means, this closure won't be
executed until someone asks for this property. Okay, so you're combining lazy
in the closures. So closures really cool for
initializing properties. Now, one thing about closures
to be careful of. They do capture their
surrounding variables. So if I have a closure, it's
just a function embedded right in my code. If there
are local variables, or instance variables in the
class I'm in, or whatever, and I use them in the closure
it works. And even if that closure sticks around, those
things still continue to work until the closure goes away.
What this means, though, since closures are types and
they can put in arrays and dictionaries, closures
are reference types. There's only two reference
types in Swift, classes and closures. So
what does that mean? It means this closure gets put
in the heap if you put it in an array. So that array
actually has pointers to this closure. Now this is a little
mind bending. I don't expect you to get this right at the
top. But it also means that, if you capture variables
in your closure from your surrounding code, they have
to go live in the heap too. Because we can't have this
closure not function, right? Can't if this function has to
be a function that executes. So here's an example of that. Here I have a local
variable called ltaue, life the universe and
everything, I think that stands for.
It's set to 42. Okay, and my operation, the closure
there, says ltuae times $0. So that closure is actually using
a local variable inside it. Now what happens if I put that
operation in an array? Okay, and then later go back and
get it out of the array, and execute it. Well, when it does
that, ltuae still needs to be there. Because otherwise,
the closure won't execute. So ltuau, the ltuae gets
captured into the heap and stays there until
the closure goes away. Now, this is awesome,
it all just works magically. You don't have to think about
it, except in one case, and that's a memory cycle. This can create a memory cycle
because you might capture the class that array of
operations is in. Okay, if you capture the class that
array is of operations in, then this closure has captured
that class into the heap. And that class through
its array is captured the closure in the heap. So they're pointing to each
other through the array, you see? They're creating a
memory cycle, that closure is keeping the class, the class
is keeping the closure. They're staying in the heap, they can never leave until
someone removes that closure from this array,
for example. So, we can break closure cycles
like these. We use that thing, I talked about last time,
unowned. Remember, unowned? When we talked about weak and
strong and unowned. And I said, I'll tell
you what unowned is. So I'm gonna put these two
things together. Unowned and the fact that we can have
these cycles. In a couple of weeks, I'll talk to you, how
we break these cycles. Okay, how we can create a closure
that will not keep something else in. Okay so,
let's see a demo of closures. I'm gonna improve the method
index of the one and only face card to be way better. And not
only am I gonna use closures, I'm gonna go back and use
extensions to protocols, and I'm gonna tie this all
together. Okay, so lets do that.
Over here, if we remember, our, we'll make it one
big window here. If we go back to our concentration, all
right, here's concentration. Remember we had index of
one and only face card, which, supposedly made
our code simpler, which it did down here.
It made this code really beautiful and readable down
here. But, it actually added quite a bit of code here. But
this is way more code than you actually need To find
the index of the oneAndOnly face card, if you use a method
that takes a closure. Now, the method we're gonna use,
it's kinda like map, its own collection, it's
called filter. So what filter does is it goes through every
item in the collection and executes a function that you
provide. Filter only has one argument. It's a function.
That function returns a bool. So it executes that function
with the argument being each item in the collection.
If that function returns true, then it puts it in
a new array, okay, and if it turns false, it doesn't.
So essentially filters the collection into an
array, but only the ones that return true from the function.
See what I'm saying? So that's why it's called filter.
Filters the collection, makes an array out of the ones
that return true. Well, that's great for this, because
index of oneAndOnly face card. How about I go look at all
the indexes of the cards and just find the indexes that
have face up cards? So I just need a function that says
whether a card's face up and I can do it. So let's do that.
I'm gonna let index, we'll say the let the face up card
indices I'll call it, okay, this is gonna be an array. I'm
gonna let it equal to my card indices. Card indices,
what type is card indices, anyone remember? Oh I just
showed you what it was. Right, right? It's accountable
range of int. It indexes into the cards array. So I'm gonna
filter those indices by providing a function.
And it's the only argument, so I don't need any
parentheses or anything, I'm just gonna put
it after. This function just needs to look at the index and
see if that's a face up card. So that's cards sub the index,
we'll just use $0, which is the one argumentative
function, isFaceup. Okay, so that's a Boolean function,
right here. This is a Boolean function,
executed by a closure, right in there that returns true, if
the card of that index is face up. So, now I've created
this which is an array, it's an array of array
indexes by the way. Let's take a look, see.
Array of array indexes. This type Array.Index,
remember, we had String.Index, Array.Index, is type aliased
or just set equal to be int. That's why arrays can
be indexed by int, cuz their Array.Index is int.
That's not true for strings. String.Index, unfortunately,
is not int. Okay, so this is an array of all the
indices. Now, if this only has one thing in it then we
have one face up card. We all agree with that?
So I'm just gonna return here, that if
faceUpCardIndices.count ==1, I am going to return
the faceUpCardIndices.first, first is just a collection
method that returns the first thing in the collection.
Otherwise, I'm gonna return nil, because we either
have zero face up cards or we have more than one, in
either case, we'll return 0. So I don't need any of this
stuff right here. Okay, so this is a lot cleaner
than this whole thing. Everybody agree with that? And
a little more readable too. Give me all the face
card indices by filtering the indices to show me the
ones that are face up. Okay, it kind of reads a little
better as well, but we can do better
than this even. And we're gonna do better
by creating an extension to a protocol down here. The
protocol we're gonna extend is collection. So we're gonna add
a var. Could be a function, but it's gonna be
a var to collection. So, I'm gonna be adding
it to string, dictionary, array, all these things.
And what it's gonna do, it's gonna be the oneAndOnly.
And it's gonna return the one and only thing in that
collection, if there's only one thing in the collection or
it's gonna return nil. Now, what is the type, of oneAndOnly gonna be? Well,
collection is a generic type. And it has one of its
generic things as element, which is the type of the thing
in there. So element is just like a placeholder, right,
in the array of element. You recognize that? For in
a generic type, it's the type. And of course I want
it to be optional. Because I'm only gonna return
non nil, if there's one and only one thing in this
collection, this string, or this array, or
this dictionary, or this countable range. And here's how I can implement
it, watch this. Return count == 1? first : nil.
Now how am I able to do this? Well count is a collection
method, tells how many things in there. first is a
collection method, it returns the first thing in there.
So since these are collection methods, I can use them in the
implementation of a collection var. Seeing the power here of
these extensions on protocols. And now, strings, arrays,
countable ranges, they all implement oneAndOnly.
So I can go back up here and just return, Return this,
remember, it's an array of the indices of face up
cards, dot oneAndOnly. Okay, and I don't even need
this. Okay, even simpler. By the way,
I could absolutely do, let ch = hello.oneAndOnly. Okay,
now that would return what? What would ch be equal
to there? Anybody? >> It would be nil, because oneAndOnly returns
if there's one and only one element and this string has
five elements. If I did this, what would this return? This
will return h, the one and only thing in that string.
So I got that by extending collection, which
string and array happen to be. What's the type of this,
by the way? ch, what do you think its type is?
It's an optional character. Optional character because
the elements of a string are characters. And a string is a
collection of characters. But if I click on it, it's gonna
say, it's a String.Element. So just like Array.Index
is type alias to be int, String.Element is type
alias to be character. So, you have to look in the
documentation to see that or you just have to know it. But if you're doing your Alt
clicking, I don't want you to be surprised when you
see that's not a character. Okay, all right, so a lot of
things went on in just this short demo here. We used
the closure right here. And we also extended a protocol
right here to create this method here. So hopefully you
understand both of those. If you don't, use Piazza, come
up and ask me afterwards or whatever. All right,
see you next time. Oh, wait, sorry one thing,
let me go back and just show you the coming up.
Because there is one thing to note here about the coming up.
Which is that Friday, we were gonna have
the optional section object persistence, we're not
gonna do that this Friday. We'll do it in
a future Friday, we will do object persistence
but in a future Friday. So, there's no Friday section
at all this week. Next week, I will be doing views,
gestures, multi-touch, multiple MVCs, all that.
>> For more, please visit us
at Stanford.edu.