Stanford - Developing iOS 11 Apps with Swift - 4. More Swift

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[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.
Info
Channel: CS193P
Views: 21,380
Rating: undefined out of 5
Keywords: iOS, Swift, Stanford, CS 193P
Id: Y8ss6118RQY
Channel Id: undefined
Length: 87min 28sec (5248 seconds)
Published: Sun Dec 31 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.