[MUSIC]
Stanford University. Welcome to Stanford CS193P,
this is Developing Applications for iOS,
winter of 2017. This is the only lecture in this quarter
where I'm not going to do a demo, so this is all slides,
and that's because I'm pretty much going to try and
get you fully up to speed on Swift. Everything that I'm
going to talk about today is covered either in your reading
assignment one, which was due today, so hopefully you're
done with that, or it's in reading assignment two,
which is going out today and is due next Wednesday. So,
why am I even covering this? Because I know that reading
through that reading assignment can be a big slog
through a lot of information, and I want you to understand
the things in there that are really important. As I go
through the slides today, if you see something and
I explain it and you're like, my gosh I didn't get that,
just make a note about whatever the topic is that I'm
talking about and then you can go back and
read about it again in your reading assignment to try and
understand it. If you still don't understand it,
you can of course ask on the class forums.
You can think of this as kind of the highlights of the fist
two reading assignments. Some of these are also highlights
of the first two lectures I did in the demos,
so there's a little bit of that in there to.
But, this is really kind of the important stuff in Swift,
that I can teach you at this point.
There's more important stuff in Swift to come, okay,
but I'll teaching it kinda as we go in the next two weeks.
Okay, so lot of Swift topics,
let's start with the first one, probably everybody's
favorite topic when they first learn Swift,
which is optional. Optional is interesting in Swift,
because not a lot of other languages have optional, so
I'm sure, in fact I've heard, that some of you are like,
"Hmm, I'm not sure I really understand that optional
thing". And that's probably okay, but by the end of next
week you should feel very confident with Optionals,
because you're gonna see that they are absolutely everywhere
in iOS. Now, I mentioned this in the lecture but
I wanted to show it to you from a code,
kind of a code point of view, which is that optional
is nothing more than an enum, it is fact just an enum,
this is the enum that is an optional. It's a generic type,
kind of like array, how with array when you say the type,
you say the type of the thing you're putting in the array.
You should all be familiar to that, with that from Java for
example, same thing in Swift.
So, with optional it is the same way,
you put this type and the type that's associated there, or
not that's associated, but
the type of the generic type that we're talking about,
that "T" in an optional,
is just the type that's going to be the associated value.
When you look at this enum Optional,
it only has two cases: "none" which is "not set," and
"some" which is "set". When it's in the some case, look:
"(T)", you know what that is from the calculator demo,
that's an associated value, just like when we had
operation in calculator it had associated values, like unary
operation had an associative value of a function, and
constant had an associated value of the constant's value.
Same thing here, with optional, in the "some" case,
the "set" case, it just has an associated value, and
it's whatever type the option was, so
if this is an optional string, then it would be a string.
Now what makes optional like, " I don't really get optional,
what is it?", it's because of all those question marks and
exclamation points. But, all those question marks and
exclamation points, are just sugar,
syntactic sugar, to make your code look a little simpler and
straight forward, because it's so common to use them. So,
I'm going to map this enum to that sugar.
Let's take a look, here's the first one: If I say let x:
String?=nil, that's exactly the same as
saying let x = Optional.none,
just like by saying array, I'd say
Array that would mean I want an array of String,
here when I say Optional , that means I want
an Optional String. And I'm picking the none case, and
in just the same way, if I say let x = Optional=
hello, I'm just saying x = Optional.some
with associated value hello. Everyone got that? That's all
that's happening there with the question mark thing.
And then here is the unwrap. When we do that exclamation
point all we're doing is doing the switch to get things out
of an optional, so we're switching on the optional,
and it's in the "Some" case, then we're going to grab that
associated value just like we did in the calculator, and
we say "let function" or "let value" to grab the associated
value, we're saying "let value" here and
it's grabbing that value that's associated with it.
If it's in case none, where it's not set,
then it raises an exception and crashes your program,
we haven't talked about how to do that, it's quite easy, but
that's what happens here,
that's all exclamation point is, it's basically a switch.
And then finally, If we do the "if let",
"if let" is also just a switch, but
in the none case of an if let, we don't raise an exception,
we just break, break out of the switch, do nothing. Okay,
does that help you a little bit understand what optional
is? Optional is a type, it's an enum, it's just like any
other type, it just has this interesting behavior, and
it's got all the question marks and
exclamation points to make all the code look a little
simpler. Now, there's even other stuff about optionals
that are interesting. Optionals can be chained.
Now what does this mean? Well, this is best shown by example.
So, let's say I have a Optional UILabel like my
display in the calculator and it's got the text, that's also
an optional, Optional String, we all know that. And
let's say we want to get the hashValue which is just a var
on string, which hashes the string, gives you some integer
for it, I want to get that hashValue of what's in
the display. So then I would probably do some code that
looks like this, this is probably similar code to what
we have in our calculator where I've got my display,
that's my IBOutlet probably that display UILabel?, and
I'm going go do "if let" here, because I don't want to crash
so I don't want to do exclamation points,
I'm going to do "if let", so I'm going to say "if let
some temporary value equals to the display" (now temp1 is
the UILabel), and then I say, "if let temp2 = temp1.text",
I'm sending text to the UILabel and
now I'm getting back a String, not an Optional String, but
a String, because I did "if let", then I can finally say,
then "let x = temp2", which is the String,
the hashValue. Okay, this is a lot of code to have to type to
just get that dang hashValue, out of that UILabel's text.
With optional chaining, this same code looks like this,
the first line of those two lines.
"If let x = display?.text?.hashValue,
then do something with x". In that case of course, x will
be a non-optional, because I'm doing if let on it.
If I take the "if" off, and just do "let x =" all that,
then x will be an optional Int. Now, how is this working?
What's happening is that every time that you have one of
those question marks after an Optional, it's saying,
if this is in the set case, then grab it and keep on
going, send the next thing. If it's in the nil case, then
just return nil for this whole expression, this entire line,
boom, it's just gonna return nil. If I'm if letting it,
then obviously the code won't get executed.
You can do as many of these as you want in a row, and
we do this all the time, this optional chaining is going to
be all over your code, and it makes perfect sense because
again, we're using this question mark and exclamation
point. Here we happen to use the question mark when we're
accessing it versus when we're declaring it. That's what it
means to use a question mark when you're accessing it,
it means go get it, and if it's not set,
then just return nil from this whole expression,
it will just bail out of the whole thing,
It will never even execute hash value because we'll never
get down to that end of it, if any of these things are nil.
Another cool optional things we can do is optional
defaulting. So what if we had wanted to put a string in our
UI label like in our display, but we know that if that
string is nil, we don't want to put nil in there because
what'll happen to our label if we put nil in as the text?
It'll collapse down, right, probably most of you saw that.
You have to put something in there,
at least a space. So let's say that I want to do that,
I want to put space in there if it's nil. This is the code
that it would look like here: I'd have my string, I would
say if the string is not nil, then put it in the display,
otherwise, if it is nil, put space in there. This looks
a lot cleaner with optional defaulting, which looks like
this, display.text = s That means if s is nil, use
this other thing. It's like the default in case s is nil.
We will use this all the time as well. Everybody got that?
Just optional defaulting. Alright, so I bring those out
of the optional world just to highlight them,
because there's a lot in the reading that you're doing
about optional, but these are kind of the highlights.
There are a couple more things actually I'll show you later,
but basically that's it for optionals. Alright, tuples.
So I had you skip tuples in reading assignment one, but
you'll be reading about them in reading assignment two.
What is a tuple?
A tuple is super simple, it's just a grouping of values,
and you can use it anywhere you're using a type. What does
a tuple look like? It looks like this. This var here,
this constant, actually x, I'm setting its type to be a tuple
with a string and int and a w, that's its type. A tuple
can have any number of things in there, but realistically,
we probably only have three or four, sometime only two, but
it can have any number. I can set x equal to parentheses,
those three, any value of those three types. So here,
I've set it to hello, 5, and 0.85.
Now I can get the values out into individual variables by
saying let parentheses three different variable names equal
x. And now it will pull out the three different values
into the three separate variables, word, number, and
value. So it's kind of mirror image there,
you can kinda go either, either direction,
putting things in or out. We actually, believe it or not,
don't do it that way most of the time,
because it is also possible to name the elements of a tuple.
So here, I define x, same thing, string int double, but
look, I've named each of them,
I called the string part w, I called the int part i, and
I called the double part v for some reason. Now, I still set
it equal in exactly the same way, no difference there. But
now I don't need to pull it out into separate variables.
I can just say x.w and get the w, the first component of it.
You see that? This is how we do tuples, with names.
I strongly recommend you almost always use
names with tuples. It's just a little easier to read,
clearer to say what your intent is as a programmer.
Because those names
of the parts of the tuple can have good variable names.
You're gonna see in your programming assignment number
2, I'm gonna have you write a function that returns a tuple,
and it's going to specify the names of the parts. Now even
if you name them, like we have x with the named ones,
you can still do that thing where you just say, let (word,
num, val) equal x, and it'll still pull it out into
individual ones, so naming them doesn't stop you from
pulling them out by individual variable, if you want.
Also, in tuples, you can always put underbar for
any of the names, like here I could say let word_ val or
let word,_, val equal x, and then I would get word and
val into individual variables, and I would just ignore that
middle one. Underbar in Swift always kinda means ignore
that, I'm not interested in that particular thing.
We've already seen this with function parameters when we
want to ignore the external name of a function parameter.
Alright, so using tuples that return values,
like I said, you're gonna see this in the homework.
Nothing special here. It's a type like any other type, so
it can be the return type of a function. So, this is how you
can have a function that returns multiple values.
In some languages, it's very difficult to do this.
You have to create a structure or something to return.
However, it's very, very, very easy in Swift.
You literally just do this. And
when you get the value that comes back from that function,
you just access the elements by name. Notice that when I
returned it in my code there in getSize, I didn't have to
give the names; I could if I wanted to,
I could have said return weight:150, height:, but
I decided, eh, I'm just gonna return the values. Okay,
another thing: range, range is an important little
struct in Swift: all that it represents is two end points.
So a range is useful for, for example, a selection of text,
where the selection starts and where the selection ends.
It's also good for a sub-slice of an array. If you've got
array of 100 elements, you might want the 15th to
the 40th one, so you could specify that range, 15 to 40.
So range is this really simple little struct, which kind of
looks like this. I say kind of, because it's a little more
complicated than this, but it basically looks like this.
It's a generic type like array because of course, you can
have a range of ints. You could have a range of floats.
You could have a range of strings even. Okay, so
that T right there could be float, float, int,
string. T is restricted a little bit, and I haven't
really talked about how you restrict a generic type.
But that T is restricted,
and it has to be what's called comparable. That's because
range needs to make sure that the start index is less than
the end index. That's part of what a range is defined to be.
So that T has to be something that can be compared
to see if the start index is less than the end index,
minor thing there. So, for example, a range of int
would be great for specifying a range in an array,
cuz an array is indexed by integers, right, starting at 0
and going up to the number of things in the array.
Now there are other more powerful, more capable ranges,
like CountableRange. A CountableRange is
just like a range, except for that you can count through
intermediate values between the start and the end.
Okay, it's like a range of ints. A CountableRange means
the begin point and end point and one point each.
Now as you stride through that range, it depends on what
the type is. Striding through a range of ints is different
then striding through a range of floats, or striding through
a range of strings. There's a little difference there, and
we'll talk about that in a minute.
There's a special syntax for
doing range, which is "dot dot less-than" or "dot dot dot".
Dot Dot Less-than means that you put the start index and
the end index on either side of the dot dot less than.
And that means go from the start index to the end index,
but don't include the end index, not inclusive of it,
and the dot dot dot includes both ends. So for
example, in an array, let's have an array of four strings,
a, b, c, d, and let's say that I want to get the c and
the d out of there, I can say array sub, ( just
like how I can say array sub 5 or array sub 3 is the fourth
index of the array), I can also specify a range and
get a sub array, a slice of the array, it's called. So
I can say 2...3, which means index 2 in the array,
which is c because a is index 0, b is index 1, c is index 2,
dot dot dot index 3, which is the d inclusive, cuz it's dot
dot dot. And then the less than 1 would be exclusive,
so that's why the d wouldn't be included in that one. So
see how I'm using a range to get a sub slice of an array,
that's kind of cool. By the way,
if you say give me the array from 6...8,
that's going to crash at run time with an array index out
of bounds because this array only has four elements. So
just because I'm accessing this subscripting with a range
instead of with a number, it still has to be in bounds.
And then if I say array[4...1],
that also will crash at run time, because the range
is going to look at that and say I can't create a range
where the start value is greater than the end value,
can't be a backwards range, that's why that type like
int has to be comparable, of course you can compare ints.
One thing very important to understand: a string,
a subrange of a string is not a range of ints.
You might think it is, right, I've got this string,
100 characters, you might think strings sub 15 to 40
would be the 15th character to the 40th. And
it's not: a string subrange is a range of string.index,
which is a different little data type, and
I'm going to talk all about that in a few slides.
But I just want to make it really clear,
that it is possible to say string subrange start..<end,
but start and end are not ints, they're string.indexes,
and we'll see how that works. If the type
of the range that you create with dot dot less than or
dot dot dot, if that type is an int, (actually,
if it's strideable by int, they can be strided by int so
it's not gonna be float, but I'm not really prepared to
talk to you about strideable), but if the range has
ints on either end, so it's a range from 2 to 7, for
example, then it automatically creates that countable range,
that more capable range.
That range then becomes what's called a sequence, And
sequences can be iterated through or enumerated, and
the way you do that is with "for in", so Swift's for
statement, this is the only one there is. It's called
"for in" and all it ever means is, I want to go and enumerate
all the values of a sequence. I'm going to talk about what
things that can be sequenced. Countable range is 1,
because if an int goes from 0 to 7, and
it's a countable range, then it can go 0, 1, 2, 3, 4, 5, 6,
7. Arrays are sequences, sequences of their elements.
So you can "for in" through an array. Dictionaries are also,
and we'll see that a little later. So here's how you do
a normal C-like for loop, for (i = 0; i < 20; i++).
You cannot do that in Swift, that syntax simply doesn't
exist. Instead you would say for i in the range 0..<20,
because notice I said it's less than 20, not less than or
equal to 20, so 0..<20 means go through that. And so
that little block of code is going to be executed once for
0, 1, 2, 3, 4, all the way up to number 19, so
that's same thing as what you see there.
Now what about floats? Floats are a little weird.
What if I wanted to do for{i = 0.5; i <= 15.25; i += 0.}.
Whoa, how am I gonna do that? I can't, for
example, say for i in 0.5...15.25.
How does it know to go by 0.3? And the answer is, it doesn't.
In fact, a range like that, 0.5...15.25 is not a countable
range, it's just range. It only knows the start and end,
it knows nothing about what's in between, so
it cannot be a sequence, it can't be for in over. But
luckily, there's this great global function called stride.
Stride takes a from and a to, or a through,
(depending whether you wanna go through to the end,
or just to to and not including it), and
a by, which is, what it's going to step by.
So here, I say stride from 0.5 through 15.25,
because I want less than or equal to, by 0.3, and
that's gonna create a new object, it's a CountableRange.
Now it's a special CountableRange, it knows
how to count from 0.25 to 15.25 by that 0.3 step. You
don't need to worry about how it implements it, Stride just
creates you a CountableRange. Actually this will be called
a ClosedCountableRange because it counts through to the end,
so it's a dot dot dot kind of range of a dot dot
less than kind of range. So I have a CountableRange, and
it's a sequence, so I can for in it, so I say for i in.
Stride, I'd probably say for f in, or even better than f or
i. A lot of times, by the way, on my slides, I'm gonna use
variables like i or x or d because I want it to fit on my
slide, I don't want it to be wrapping. It's hard for you to
read the slide if the code's always wrapping around, so
just because you see me using i and d and f doesn't mean
that's license to have those kind of terrible names for
your variables. You need to have meaningful names. So
that's how for in works here,
because stride is gonna return a CountableRange,
ClosedCountableRange in this case. So
that's how you do your standard for loops from C.
Alright, let's talk about the data structures in Swift.
You've already learned about three of the four of them,
which are classes, structures, and enums. In our lecture,
we already did all three of those things.
We had a class, which is our view controller subclass.
We had a struct, which is our calculator brain.
And we had enum, which was our operation.
The fourth one is protocols, and
I'm not gonna talk about that until next week or
even the week after. Protocols are super, duper important,
I don't want to make it sound like they're not important,
But they're new to a lot of you, and so I'm gonna try and
ease you in with these ones that are more familiar,
although different in Swift probably than what you're used
to, they're still more familiar to you.
Let's just review quickly these three structs and what's
the same and different about them. So what's the same?
They're declared very,
very similar: they have different key word, but
they're almost exactly the same.
The only difference really is a class can specify a super
class, otherwise they're declared exactly the same.
They can all have properties and functions, So,
they're all very similar in that way. The only thing here
is that there cannot be any stored properties in an enum.
Enum keeps any data it has in associated values, so it can't
have any stored properties, but it can have computed
properties and they certainly all can have functions.
Another thing that's very similar is they can all
have initializers except enums. Enums don't need
an initializer cuz you just say the case you want, but
structs and classes have initializers. Okay, so
that's the same. Now what are the differences?
Well of course, inheritance is a big difference for classes.
Classes have inheritance. Structs and enums do not. But
the most important difference, which I mentioned last time,
is that structs and enums are value types, and
classes are reference types. So let's go into that and talk
about it just a little more. It's so important, I'm gonna
talk about it again. A value type, what does that mean?
It means that it's copied when you pass it as an argument
It's copied, even if you just assign it to another variable,
to a function.
it gets copied. If you assign it to a let, it's immutable,
that's very important to understand. If you assigned
a value type to a let, using let x equal that thing,
you just made it immutable, No matter how complicated it is.
If it's an array or dictionary,
you can't add any elements. If it's a calculatorBrain,
it means you can't call any of its mutable functions, So
you can't do perform operation on it. Now in assignment two,
I'm gonna have you add a method to the calculator brain
that is gonna let you use the calculator brain when it's
immutable, which is kinda cool. You still wouldn't be
able to perform operation but you can be able to something
very important, immutably. Now because of this, this kind of
copyright-on-write behavior of value types, you must,
of course, mark all the functions that are going to
modify it as mutating. And that's how the Swift knows,
"Oops, I've gotta make a copy of this, an actual real copy,
if someone writes to it". Now a reference class is pretty
different in that it gets stored in the heap somewhere
with a pointer to it, and when you pass it around to
a function or to assign it to another variable or something
like that, you're just passing a pointer around to it.
Now, by the way, when you say, let x equal a reference type,
you can still send it messages that will mutate it.
All you're saying is the pointer can't change, but
what it points to can always be changed.
Now reference types are what you're used to.
Most languages that have object-oriented reference
types. Reference types are a little bit Wild West- I
don't have time to teach you this, I hope you'll get to see
this sometime in your career at Stanford, but
there is a different way of thinking about programming
than you're used to: it's called functional programming.
How many people have heard the phrase,
functional programming? So about half of you. The idea
of functional programming is: you don't want this Wild West
where you've got these objects with multiple people pointing
at them, any of whom could modify it at any time.
That leaves you open to a lot of difficulty in verifying
the correctness of your program.
Whereas if you have a lot of objects that are immutable,
you know they can't change, and their APIs are basically
like mathematical functions where data goes in and
predictable data comes out, because there's
not all this side-effecting, data-effecting other objects
that someone else is pointing to in all this.
It's this really well-contained little thing.
Now iOS was not developed with functional programming in mind
at all. But the people who invented Swift,
they were thinking about functional programming, So
you can kind of mix a lot of the elements of
functional programming into your application,
if you're building an iOS application. Now that's a new
frontier in iOS, because for however long, 20 years,
really, or 30 years, really, if you consider all the way
back to the invention of the technology that led to iOS,
people have been programming with basically
reference types only, for their object-orienting.
But when you do this programming with structs and
enums, and especially when you throw in protocols, (which I
told you I was gonna tell you that are very important, but
I cannot explain to you yet, and generics),
when you start throwing those things all in there, you can
really do a good job of doing real functional programming.
All of the Swift foundation is really designed with a lot
of functional programming in mind. If you ever really wanna
kind of see a good example of how to apply this immutability
and generics, and protocols and all that stuff to build
an architecture, chase down all the things that
are going on in the foundation library: string and range and
all these things. Remember, that I said things like
the range is a sequence, or this thing is comparable,
remember how I said things like that? Okay, well,
how do you express that? You use protocols,
with these immutable types, that can be immutable and
use generics. So I can't explain all that,
I've already spent too much time on it than I have to even
talk about it in the lecture today, but just so
you know, there is a huge advantage of programming in
a different way; it requires a real mindset change.
So hopefully, you'll take a class where maybe the whole
class is functional programming,
and really you'll get a feel for it.
Swift does a good job of supporting for
the fundamentals you need to do that. That's kind
of the long-winded answer to which do you choose. If you're
doing more of a functional approach, even if you're doing
a reference-based approach like what you're used to, try
to lean towards immutability, try to ask yourself,
can this thing work and be immutable? Is there some
functionality in my program that I could share by creating
a generic, for example. At least those leanings
might help you build the apps that are more testable,
because it's a lot easier to write a test for
something that's like a mathematical function where
you know the data out is supposed to be the same for
the data in, than it is for something where there's
a lot of data that you have to set everything up. Set this,
set this and this and this, and now call the method,
that's a lot more difficult to write test cases for, etc.
So that's value and reference. Let's talk a little bit about
the syntax of methods. I'm gonna go fast on this.
Hopefully, you all understand this.
All parameter names have external names and internal
names. The internal name is used inside the definition, or
the inside the implementation of the function.
So here I have these two functions, foo and bar. Bar
calls foo. So you can see that inside foo's implementation,
it is using the first and the second, whereas bar,
when bar calls foo, it uses external first and
external second, as the names of the items. And you know
that you can put an under bar to make it so there's no
external name. We only do that 99.99% of the time with
the first item. Why do we do it? Because sometimes the name
of the method, and/or the type of that first argument, is
enough to make it clear what that thing is supposed to be,
so we don't need to put an external name.
It's kinda clear. All the other parameters,
we don't have the advantage of having the name of the method
there. So that's why we almost never put underbar for
the later ones. By the way, if you only put one
parameter name, then that's both the external name and
the internal name, and that's not that uncommon,
you'll do that occasionally. All right, so you know all
that. When it comes to doing overriding, subclassing, in
reference types and classes, when you override, you have to
explicitly let Swift know that you know you're doing that.
And you do that by putting the override keyword.
So if you override a method from your superclass you have
to say override func whatever. You can mark a method or
even a whole class final, and that means that it cannot be
overridden, subclasses will not be allowed to override it.
Most languages have that. Now on the topic of methods.
You've seen, actually you've seen both, but mostly seen and
understood instance methods. These are methods,
or vars, that are being sent to an instance of the class
struct or enum. In other words, one of them.
Like I create one of them: I create a double of 15.5,
I have one, and I can send it messages.
But types, like the type double, the type strength,
the type calculator brain, they can also have methods and
vars. Computed vars, no storage, but computed vars.
All you do to add a method or a var to a type is
you put static in front of the declaration.
So "static func whatever" means this is a function on
the type, not on instances of the type, on the type itself.
For example, let's think about double here.
Double actually has quite a few static or type methods and
vars. You access them by sending the message to
Double to the actual word, capital D-O-U-B-L-E. You
don't send it to an instance, you send it to that word. And
you've already seen at least one of these, which was pi.
Remember we said Double.pi and we got the value of pi?
Pi is a var, a computed var, on the class or
struct actually, double, but it also has other methods,
like abs. Abs takes a double value and
returns the absolute value of it. That is on the type.
You say Double.abs, Double.pi. As opposed to, for example,
if I have a double like x is equal 23.85.
So x is now a double. I can't say x Double.pi cause
x is an instance of a double. You see the difference?
We see saying x.pi, trying to send pi
to an instance versus sending pi to a type double.
So what do we use these type methods for?
Well, we can't access any instance variables because
we're not sending them to an instance.
We mostly use them for utility methods, things like that.
Constants like pi is a good example. Things that
kind of are functions that are associated with this type but
don't belong to a particular instance or
wouldn't really operate on an instance. Now for example,
you might have an instance method or
even an instance var called abs, A-B-S, with no arguments,
That you would send to an instance, and it would take
the absolute value of the thing you sent it to. But
of course the type one has to have an argument because
you're sending it to the type double,
so there's no double involved.
There's no instance of a double involved. Everybody got
that? Alright: properties. You know all about properties.
We saw computed properties like display value in
our calculator. There's actually some really cool
features on properties. One of the most interesting ones is
property observers. What is a property observer?
A property observer is a little piece of code that
will get executed when your property changes.
Anytime your property changes just a little piece of code
can get executed. Now you can actually find out
just before your property changes. Or
you can find out just after your property changes.
Or both. And the way you do this, and this works for
your stored properties, like userIs InTheMiddleOfTyping:
we could put a property observer on there, and
execute some code every time we change that. It also works
for inherited properties, so if you inherit something
from your superclass, you can put these properties in and
notice that it changed. By the way, if you have a property
which is a value type, (a struct or
something, like an array or an dictionary), this property
observer stuff will happen if that thing gets changed,
if it gets mutated. So if you add something to the array,
boom, the Property Observer will kick in and say,
"that changed". How do these Property Observers
work? They look a lot like a computed property. Remember
that the computed property displayValue had get and
then we had some code, And then we had set and
we had some code? This is similar except for
get and set it's will set, or did set.
By the way, you probably would never (I don't even know if
you can) use this in a computed property because
you've got the set clause there so
you can just put it right in set.
You don't need to find out when it's set because you set
it. But for stored properties and inherited properties,
that make sense, so you have willSet.
Now, willSet, first of all, you put it all in a curly
brace after the property just like if you were doing
a computed property but, this is not a computed property,
just adding a curly brace doesn't make it a computed
property, you have to put get or set in there, but,
if I put willSet, then, inside that code,
there's a special variable just like there in the set
case of computer property, called newValue, and
that is the value that that thing is going to be set to.
It's not set yet. Some property,
see some stored property up there? It has not yet
been set to newValue but it's going to be. And then didSet,
that code occurs after some property has been set and
the special variable in there is oldValue.
That's the value it used to have before it got set. So
you can compare if they've changed, for
example. So where do we use these things?
Probably the number one place that we use them is in our
Controller in view. Let's say I'm a button and
my background color changes. I inherit my background color
from my super class, UI view. Up the chain of super classes.
Every time the background color changes,
the button wants to redraw itself so
it will have var background color is a UI color,
open curly brace, didSet open curly brace
draw myself closed curly brace. Question? Okay,
so the question is, if someone changes some stored property
there, am I responsible for doing something about that
in willSet or didSet? Do I have to actually set it? And
the answer is no. Okay, that's being set somewhere else.
You're just getting a chance to run some other code that
you wanna run just before and just after it happens.
But you're not responsible for setting the actual value.
That's done for you. That's why you get to
see the old value and the new value in the two of them.
So we'll see this next week when we start drawing on
screen, where we're gonna be watching properties change so
we can cause ourselves to redraw. Next, lazy properties.
Lazy initialization is a really powerful tool.
It's gonna get you out of a lot of binds in this class.
What does it mean? A lazy var, a var
that you say lazy in front of, whatever it's set equal to,
that equals doesn't actually happen until someone accesses
that var. Until someone asks for the value of that var,
it doesn't actually do the initialization there.
It doesn't do that equal something.
So it's lazy. It's waiting.
Now why do you wanna be lazy? Well, one obvious reason
is like here, lazy var brain equals calculator
brain, what if calculator brain was really expensive to
create. What if it opened a network connection because
we're going to share the calculation with the internet
or something, you know what I mean,
do something that is expensive.
Well, you wouldn't want to do it,
unless someone actually tried to access the brain,
called set up operand on it or something. Then you would
want to actually do the work to create it.
Okay so one reason to be lazy,
it's not that important of a reason it turns out, but
one is to delay expensive operations. But
what's another reason to do it? Well, in Swift,
all vars have to be initialized. Remember that?
Remember we added userIsInTheMiddleOfTyping.
And we didn't say, equals false. And we got an error.
It said we had no init, because we hadn't initialized
that. Not only did they all have to be initialized, but
they all had to be initialized before you could even send
a message to that class, even internally. So
you can't invoke any of your own methods until you fully
initialize yourself. What if one of the things you want to
initialize needs to call a method on yourself? It's
impossible. Because if you need to initialize something
to be allowed to send messages to yourself and you need
to send a message to yourself to initialize something,
it's a deadlock. You can't do it. Okay, well lazy let's you
do it. Because you can say for example, the last one there;
lazy my property equals some method on myself. This is not
going to be executed until someone says my property and
no one's allowed to access my property until I'm fully
initialized. So by definition there's no way
that that is gonna try to get initialized until I'm already
fully initialized. And yet this lazy thing counts as this
thing having been initialized. So this is the big loophole.
Lazy var myProperty counts as having been initialized
even though it really hasn't yet. Because it's waiting for
something to actually access myProperty. But it counts for
the purposes of that rule, so now someone comes along later
and accesses it, now we can call this method. So
do you see how deferring the calling of this method in
order to initialize this gets us around that requirement
that everything be initialized?
It's really tricky. And then the middle one there,
someProperty, that's super tricky. Because you can
actually have a closure, (remember what a closure is,
right from calculator brain, it's just a function,
in line function), you can actually have a closure
to initialize your thing lazily. All you do is you
put the closure, open curly brace, close curly brace.
It obviously has to return something of the type
of that bar. And then just put the little open parenthesis,
close parenthesis at the end. When we open parenthesis and
close parenthesis at the end of the closure,
that means execute this closure right now. But
it's not gonna execute right now, it's gonna act lazy so
it's gonna happen later. And that means that that closure
inside could reference self. Because self will be fully
initialized by the time this closure gets executed,
since it's lazily executed. So
lazy will get you out of some of these tricky wickets,
because when you see me talk about initialization,
you're gonna be like, "I don't want to do that, ever".
You're going to try and avoid it, and
this is a good way to avoid it. So yeah,
it still satisfies all those things. Alright, on to Array.
Everybody knows what an array is. Everyone knows what
a generic array is. You just, when you declare the array,
you have to say what type of things are gonna
be in the array. There's a different syntax though,
which I didn't introduce when I did dictionary in the demo.
But you can declare an array, those two yellow things
up there are exactly the same thing. So,
open square bracket,
string close square bracket is exactly the same as saying
array angle bracket string. It's just kind of a special
way to declare an array and it actually seems to be
the preferred way. I actually prefer the other way because
it's a little clearer to you that are learning that this is
an array, because it says the word array and string.
But open square bracket, close square bracket,
we know that's going to be index into an array so
it looks kind of array-ish but
just get used to it. [String] means an array of string.
It's the declaration of the S and the name of that
type array of string. So if I had a string right here like
this giraffe, cow, doggie, and bird. Four animals in here,
four strings and I said animals dot append ostrich,
well append appends something onto the array, now the thing
you are appending has to be the same type obviously
as the type of everything in the array, however the type
was declared. Notice that animals is in inferred.
By Swift, to be an array of string. Because Swift sees
that you sent it to an array of things and
it looked at all the things and they were all strings.
So it said, the animals must be an array of string. Now,
this line of code animals.append("Ostrich").
That's very bad.
Can anyone tell me why that's not gonna work?
Yeah? >> That's exactly right.
We define this animal's variable with let, so
it's immutable. So when we say append an ostrich onto
an immutable thing, it's going to crash my program.
But actually, it won't even crash my program.
It won't even compile.
Like, Swift compiler's just gonna say, no way,
you can't do append of something or other.
So that's a good one over there, you got that.
How about this one? I'll give you a second chance for
everyone. Why is this one no good? Okay,
I'm trying to get animal[4] over there, why? Nobody?
This one's easier. Array index, yes! Array index is
out of bounds because arrays are indexed starting at zero.
Okay, so giraffe, cow, doggie, bird that's zero, one, two and
three. So if I say, give me array number four, bam,
crashed my program array index out of bounds.
Let's talk about the fact that array is a sequence.
Array is actually a collection and collections are sequences.
And a sequence means I can do for in on it. So if I say for
animal in animals, my little for loop will get executed
four times, once with animal being giraffe, next one with
animal being cow, next one with animal being doggie,
next one animal being bird. Okay, so that's a really cool
feature. Remember this is the only four there is in swift.
For in, that's the only for there is, nothing else.
So array in a lot of classes instructs
in iOS have some interesting methods
that have arguments that are closures. So I teach closures
to you right at the beginning of the of the quarter.
And why do I do that? Because closures are an important
thing to understand if you want to really use iOS API
well. So let's take at this (just so we can learn a little
bit about how closures can make our API great,
especially again if you're doing functional programming,
closures can be really good), but why are closures so great?
So let's look at this so, this first function right here
called filter is an array method.
Nothing special about it, it's just funk and array.
It has one argument, which is called includeElement.
And that argument is a function. It's a function that
takes one argument which is of the same type of the things in
the arrays, cuz it's a t. So I have an array angle bracket t.
It's a generic type so this function is declared in that
generic type so the t means the same t, the same type. So
if I have an array of strings this filter expects this
to be a function that takes a string. As its only argument.
And it returns a Bool, so that's the argument.
The argument is a function that takes a string and
returns a Bool or takes a T, whatever that is, and
returns a Bool. And then what's the return
value of this filter thing? It's an array of T, so another
array with the same kind of elements. So what does filter
do? Filter takes every single element In the array that
you're sending it to and it runs that little function. And
if that function returns true, it includes it in the array it
returns. If it returns false it doesn't.
It throws it out. So
it's a filter: it's filtering your array and creating a new
array with all the things you don't want as defined by this
function that you're providing as an argument.
With all the things you don't want thrown out. So for
example here, I have this var that I'm creating,
bigNumbers, and I'm creating an array on the fly 2,
47,118,5,9, (see? I created an array), and look,
I'm sending it a message right away. Yeah, I didn't have to
put it into another var, by the way, I could have but,
I just want all be on one line here. I created this array and
I'm sending it the message filter and
look what I'm providing for the argument there.
That include element it has an underbar, so, you don't have
to actually put the include element column in there. So,
what am I putting in there? That's a closure. So
that means, it's a function. Now, Swift knows that this is
a function that returns a bool. So
I don't have to put the word return in there. I can use
dollar zero to be the one and only argument to the function.
And I'm just going to check and
see if dollar zero is greater than 20, in other words,
is this a big number. So I get back an array which has only
47 and 118 in it, because I've filtered out all the things
where the value's not greater than 20. Imagine writing
this line of code without filter. You're gonna have
to a four loop. You're gonna have to create another array.
You're gonna have to run this little function,
call this function, create a new array and
add, append thing to do it. It's at least four or
five lines of code. And here you get it in one line of
code, okay? So you see how closure there has helped us.
This is also much more readable cuz you can read this
like this: Let the big numbers equal this array, but
filter for things there are greater than 20.
That reads nicely, so I'm just clear.
It's very easy to understand what's going on here.
So here's another one. It's called map. What does map do?
It takes a closure, or a function. And
it executes that function in order to transform each
of the elements in the array you're sending map to,
to a new array.
Now that transformation could be anything that a function
can do. And you can convert it to any new type you want.
Although it's one function, so it
coverts everything to the same type. So you could use it for
type conversions like I have here. I've taken 1,
2, 3 and mapped it to the string versions of 1, 2, 3, so
now I have the new array with strings in it. String of 1,
string of 2, string of 3. That's a trivial mapping.
But you can imagine much more powerful mappings where you
take each element of the array, and
call some complicated function on it, and get the result into
a new array. So by creating a really powerful argument there
instead of String($0), something more powerful, you
can really have one line of code that's really expressive.
It can do a lot of things. One thing about this one: notice
that after the word map, there's no parentheses,
no open parentheses. Did you notice that?
You see the difference between filter and map, okay?
When I called filter, I said open parentheses,
open curly brace, the function, curly brace, close
parentheses. Here I just say, { String$0}, no parentheses.
And this is what's called the trailing closure syntax. You
are allowed to have closures that are the last argument to
a function. The outside of the parentheses of that function,
if they're trailing, see they're trailing the call. And
in fact, if there's only one argument and
it's closure, you don't need the parentheses at all.
And that's what happened there with map.
I'm just saying map, parentheses get rid of them.
Just put the closure there, trailing. And
that also results in some pretty cool looking
code right here, you see that? So you can use that anywhere
that a closure is the last argument of function.
You can take it outside of the curly braces. Cuz you already
got the curly braces, and that's why they do it.
You don't really need the parentheses around it as well.
So put that at the end.
And put all the rest of the arguments, if you had them,
inside the parentheses right before. This last one, I won't
go into the details, but it's a similar kind of thing.
Reduce: what reduce does is it takes an entire array and
reduces it to a single value. So here I'm reducing it by
adding all the numbers in the array up. So my closure is $0
+ $1, because the argument it takes is a function that takes
two elements. One of them is an element from the array and
the other is the answer so far, and
then it returns the new answer so far. So it just executes
that function over and over and over on the arrays.
By the way, notice that I can say this as sum = [1,
2, 3].reduce(0, +) which is my starting so far value.
Which I haven't added a name on, so it starts at 0 and
then I can say plus. The reason I can say plus right
there at the bottom line, is because plus
in Swift is not some kinda weird built-in thing,
it's just a function. It's a function that happens to be
declared in a way that says, this is a function, but
it's two arguments it's going either side of it.
It's called an infix operator on a function. So
since plus is just a function, and
I'm taking a function as an argument,
as long as it's a function that takes two arguments and
returns one, which is exactly what plus does, it works
there. We could have done that in our calculator brain, too.
Remember all that closures we were doing ($0* $1),
that could've just been *), ($0+$1), that could've just
been +. Cuz + is a function that takes two doubles and
returns a double. Plus also know how to plus other types,
ints. And it even knows how to plus and into a float,
for example, etc. You can plus strings together.
So that was just a little aside just to give you
an introduction there. You're gonna start seeing a lot of
methods that take functions as arguments.
So I want you to start to get comfortable with that.
Okay, dictionary. So we learned a lot about dictionary
in our first lecture. Dictionary also has this
special declaration syntax, which is open square bracket
key type colon value type closed square bracket. So
those two things in yellow are equivalent.
And the one on the bottom seems to be the preferred
one these days. So you know you can create a dictionary.
By the way, I show you creating dictionaries from
kind of constant values here.
But you can create a dictionary just by saying,
let d equal dictionary. Open parenthesis,
close parenthesis. And if you say var d
equals dictionary, then now you can start appending items
onto it because you get an empty dictionary to start. But
here I'm starting my dictionary with stuff in it.
That probably should say var pac12teamRankings
because two lines later I say pac12teamRankings sub Cal
equals 12. Note also that
when I try to get the ranking of Ohio State, it returns nil.
And we know that when we use square brackets to get
something out of the dictionary, it returns
an optional of our value type. Our value type here is ints,
so it's gonna return an optional int.
Alright, enumeration. A dictionary is also
a collection and thus can be sequenced. So you can do for
in on it. Of course, a dictionary has keys and
values. So we need a tuple to do our enumeration for
the tuple key common value in the dictionary. And
then it'll go through and key in value will be set.
It's a perfectly straightforward way
to enumerate or iterate over a dictionary. Okay,
String. So string, you would think, is the simplest class
in all of any language. It's just a string, right?
Well, that turns out not to be so true. When you think of
a string that represents every language in the entire world,
all of a sudden it got real complicated real fast.
Because some languages are ideographic. Some languages go
right to left instead of left to right. Some languages have
a lot of diacritic marks and accents in them. Some don't.
Most languages don't use the same alphabet as we use.
So string is super complicated,
this is a very complicated class. Now string tries
its best and does a pretty good job of simplifying it for
your use. But the complexity is still there. Now the most
important complexity about a string is that representing
a string is not always one character equals one
internal representation unit. Now the units, the kind of
thing that's used to represent strings on the inside,
are Unicodes. I had you skip that reading in assignment
one, I'm putting it in for assignment two.
It's really not that critical that you understand it.
I think it's very interesting, though, so
you probably want to read it. But the thing about Unicodes
is it's not one Unicode equals one character.
In fact, if you have like a little emoji of a dog barking,
that might be three Unicode characters. It's only one with
regards to what you perceive to be a character. Or
even more simply, the example I have here, the word cafe.
It's got e accent aigu on the end, right, it's a French
word, e accent aigu. That could be four Unicodes,
because there is a Unicode for e accent aigu.
Or it could be five. It could be the e with another Unicode
character that says put an accent on that previous
character. So, when you have this kind of unknown how many
characters it represents, you need a new abstraction
to represent what we perceive as humans to be a character.
Even that is difficult to truly define, when you think
of all the languages in the world. It's easy in English,
though. So that's really how we're gonna interact with
strings: to try to think of them in terms of characters.
But a string itself is not actually
a collection of characters.
A string is a more powerful internal structure,
it's got all the Unicodes in there and all that stuff,
it's not quite that. And that makes for a little bit of
complication as you're gonna see. Now, you can't string, is
indexable which means you can use the open square brackets
just like you can in array to get one of the characters.
The only rub is what I said on the other slide that index
is not an int. So if you have the word hello,
it's not like you can say, hello[1] equals e.
You can't do it that way because it might,
if it was cafe then cafe sub 3.
It's not clear that you want a character at that point, and
so you've got to make sure you get the right index in there
because that index behind the scenes can be quite
complicated. There's this other struct for it, which is
called a string.index. So let's say I had a string,
which I'm gonna call s and I'm gonna put hello into there.
Now, what if I wanted s[0] now, I can't do s[0],
because we don't index strings by integers but
let's say I want that. How do I get it? Well,
I start by calling this var, again using this var and
string called startIndex, okay? It gives us
a string.index. It's very important, we need to get
a hold of a string.index because the way we're gonna
move around in string is by taking index we know and
moving forward or backward by character, by human
understandable character. Not by Unicodes, because we
don't know how many Unicodes there are per character but
by character, we're gonna be moving back and forth.
So we start with this first one.
And so now I can create a variable firstChar which is of
this type Character,
that human understandable character.
That's gonna be equal s[firstIndex]. So see look,
I've used square brackets to index into a string, but
I had to use a String.Index not an Int. This first index
I got by asking the string "what's your first index"?
Well this is kinda useless because I have to ask
the string, what's the index of your first character and
then turn around say okay give me S of your first character.
So that's not the interesting thing,
what if I want the next character. If I
want the next character, I ask the string what's the index of
the character after your first character. And
it'll give me a new index, of its second character and
now I can get that one. Even that is a little bit like,
my God, you're kidding me,
this is really how I have to do this? What if I wanted
to go jump ahead though, and get the fourth, the fifth
character, let's say? Well I can jump ahead by saying s,
give me the index of, starting at your first index,
that's four ahead so jump four ahead,
four characters ahead. This is all a little
tedious and of course this is not the actual way that we're
interacting with strings. We're usually looking for
substrings, okay, or subranges of characters or
we're trying to find a character in a string.
By the way, you can use ranges, so I could say
give me the substring which is firstIndex...secondIndex and
it would give me he. A new string which is he, so
you can use ranges there but they have to be string.index
not ints. So we want basically something that
is a collection of characters. A string is not a collection
of characters so you can't do for in, for example, on it.
You can't even say index(of:).
Index(of:) is this great thing where you can,
if you have a collection of things, you can say give
me this index of this thing in there. You can't even do that
with a string because it's not a collection. Luckily,
string has a var which will give you a collection of its
characters. And not only is it gonna give
you a collection of its characters that you can for
in over and do index(of:), but the string indexes you get
from that collection will match what's in the string. So
you can use this collection of characters to find things in
there, get the index of a character, things like
that. And then use the indexes you find there to go back to
your string and say, okay now give me a substring. So
characters is the name of this var on string. So for example,
if I want to iterate over all the characters one by one,
do a for loop over them I can say, for c:,
which will be of type character,
by the way, in s.characters. I can't say for c: in s, for
c: in s.characters. And that's just gonna call my for
loop once with each character. Question? Yeah,
great question. So if I do this over cafe,
what am I gonna get? I'm gonna get four characters, okay,
because that E-accent-agu that they use is considered in our
human perception one character.
I'll get four characters. So there is a character.
Characters are struct, right and in the character
there is a character that represents e-accent-agu.
No matter how it's represented in the string,
in the string it might be two characters, it might be one,
it doesn't matter. You're gonna get the one character
for it, so that's a really good question. How would I
know how many characters there are in a string? Again,
from the user perspective. s.characters.count,
not s.length or something, s.characters.count,
the count of the characters in the collection of characters
for this string. Also, what if I want to find the first space
in a string? Got a string with a bunch of words, I wanna find
the first space. I would say s.characters.index(of: " ").
And that's giving me a string.index(of: " ') that
collection of characters. I can then use that string in
that index though to go back to my string and do something.
Maybe insert something there or
delete the word that's right after the space or insert
another space. I'll have the index into the string now.
String is a value type, so when you do let versus var,
if you do let you cannot do things like append on to
the string cuz it's immutable. Most strings in Swift
are immutable, that makes it super high performance.
But if you have a var you can do reading plus equals there
and so I can have hello there. It doesn't have any effect on
hello, the let one because that's immutable, right? Okay,
so I can't do that. And I can use the characters to
manipulate strings in really powerful ways. I can, I don't
have time to talk about all of it, this is where you really
do need to read your reading assignment carefully. But for
example if I want to insert the word you into hello there,
so it says hello you there, okay, I would just get the,
find the first base by doing characters.index(of: " "),
which I showed you on the previous slide. Then there's
a method in string called, insert the constant,
the content of this collection of characters
at this location, this string.index. So, when I want
a collection of characters that represents " you", I say,
" you".characters, because it's the .characters, and
it gives me a collection of characters for that string. So
it seems a little weird but It actually turns out to work out
pretty well. And there's a billion other functions,
in Swift, many, many, many,
I can't talk about them all.
Notice I say if let firstSpace, because that might
return nil if it couldn't find it. So I wouldn't do it there,
I wouldn't insert it there. Yeah a bunch of other things
checking where strings have prefixes or not.
You can replace subrange, of course you have to find
the range which usually you're gonna have to look at that
collection of characters to find the ranges you wanna
replace things etc. Even cool methods like components
separated by string will take a string that has like calm or
separated values and grab all of the values and
put them in an array which is kinda fun. Okay so
a lot: string has dozens and dozens of methods, you really
need to familiarize yourself. If you don't take the time to
familiarize yourself with those thing in strings.
Then I guarantee you're going to be writing a program and
you're going to want to do some string parsing or
something and you're going to write a whole bunch of
code to do it and then find out there was one method that
did exactly what you want in the string.
There are things in there take closures and
do all kinds of fun stuff so
make sure you pay attention to both to string and
to the character view which is a collection of characters.
All right, other classes. This is kind of quick summary here
of other important classes. One is NSObject, NSObject is
a class. It is the root of all Objective-C classes. An iOS
app written before a couple years ago when Swift came out,
all the classes in there would inherit eventually from
NSObject. So NSObject has a base functionality. You do not
need to subclass from NSObject to make a swift class.
However there are some really obscure features still left in
IOS where to expecting a class that inherit from NSObject.
If we even get to them, I'll show them to you. They're
pretty rare. But that's just so you know what NSObject is.
Doesn't really have meaning to you,
cuz you guys are gonna be Swift programmers.
NSNumber is another class, it's a generic number
holder. This is also inherited from Objective-C, you can tell
by the NS there. Until we can hold any kind of number,
floating point, decimal, you can even hold a bool,
since it considers that a number where zero is false and
one is true. Any nonzero is true. And you can also
do type conversions. Again, in swift, we're gonna be using
the actual concrete number classes like double an int and
we'll use their constructors. To do type conversion.
But just so that you know that NSNumber is around. Okay,
date. Super important class. Anytime you are representing
a date or time, you're gonna wanna use this date struct.
And it has a lot of ancillary classes that go with it,
like calendar, date formatter, date components like date
components will tell you the month of a date. And
of course, what the month of is of a date depends on
the calendar it's in, cuz not everyone uses a calendar like
we do. All right, there's a lot of calendars
all over the Earth that are different than ours.
So anytime you're even contemplating putting a date
anywhere in your UI you need to be using this class.
Because if you wanna have your app work in anything but
English, you're gonna have to format your date, using
the DateFormatter, in whatever locale your app is running in.
So date is super important. You would think date is really
easy, but no. Date actually has a lot of complexity to it,
because of all the different ways we represent dates in
the world. Same, similar to strings complexity. And
then there's data, D-A-T-A, that represents a bag o' bits.
Just a bits, bunch of bits in there. Usually this might be
something like an image, the bits of an image, or
something like that. Something you get over the the network.
So data is how we represent those.
Notice that there is a struct, it's a value type.
It gets passed around copy on right. And
you'll start seeing data in UI maybe week five or
six of this class. All right, initialization.
So initialization is very complicated. You're
gonna have to read the reading assignment on it eventually.
I'm gonna give you the really fast highlights of it. init
method, again, we can have them on classes or structs.
They are used to initialize any of the properties that we
didn't initialize with equals or using lazy or they were
optionals. All those are taken care of and if there's one
left, or more than one left, then we need an initializer.
To initialize it. And they're kind of a pain in the neck.
Because, especially for classes. For structs,
they're not a pain in the neck. They are perfectly fine.
Struct, you just have an init with whatever argue you want,
arguments you want. And initialize your variables.
It's all very simple. But for classes, it's a little more
complicated. Now, whether you're a struct or a class,
you can have multiple init. And they can have different
arguments. As long as you give them enough arguments to be
able to initialize all their variables, you're good to go.
So you can have as many inits as you want.
And a lot of classes have multiple inits. Meaning on
kinda how they are being used, how they are being created.
Think of something like string.
String has an init that takes a double.
It has a string that takes another sting if you want to
make a copy of the string it turns out. It takes an init.
It has all these things that you can compare to string.
Or not all the things you compare string but
a lot of things you can compare to a string.
It has an initialized that it will take that as an argument
for example. Callers execute your init by creating
one of the thing. They just put the name of the type and
parentheses and the arguments you want for that init. So
they pick which of your init that they want and
they call that one with those arguments.
We've seen many examples of this in our lecture, so far.
You get some inits for free. If you're a base class, okay,
you don't inherit from anything else.
You get a free init with no arguments. Congratulations.
So that means, at least, people can create you.
But, again, if you have any vars that aren't initialized,
you're gonna have to create your inits that initialize
them. Structs are even better. They get an init that has all
of their vars. Now if a struct implements even one of its own
initializers, it stops getting this one, this free one.
You only get the free one if you don't do any other ones.
So it's like the default one.
And we saw this in the lecture also.
We got the free one in the init function, first operand.
We never wrote that init function, we got it for free.
It just appeared and we were able to call it. , so
structure nice you get the basic free init for
all your vars. What can you do inside of a nit? I'm
gonna start going fast here, okay. So here we go. One,
you can set any properties value even if that property
already had its value set, you can reset something else.
You can set lets. So you're gonna have a let that's
got less x equal 5. You can still reset it in your init.
Your init is allowed to set lets. You can call other init
methods okay in your own class or your own struct. And you do
that by saying self.init with whatever the arguments to that
init method are so init methods can call each other.
In a class, you can call super.init.
An initializer from your super class.
Of course you need to get your super class initialized.
Now, when we start talking about class though,
and we start talking about calling inits in our super,
and we start thinking about inheritance,
it gets really complicated. So
let's start thinking about class and break this down.
Let's start with what are you required to do in an init for
a class? in a struct it's easy you're just required to
initialize all your vars that aren't initialized. But
in a class what are you required to do? By the time
it's done you have to have initialized all your vars.
We know that. There are two types of initializers.
A convenience initializer.
This is only for classes. A convenience initializer and
what's called a designated initializer.
Designated is the default. There's no keyword it's just
if it's not marked convenience then it's a designated.
Now here's where the rules come in.
A designated initializer non convenience must and
can only, call the designated initializer that it is in,
that is in it's immediate superclass,
it's very important. It has to call a superclass init
and it has to get designated initializer. It cannot call
it's convenience initializer in a superclass. Nor
can it call two classes up the inherent chain unless the one
right above inherited the init from the one up above, and
we'll talk about that in a second. You have to initialize
all of your properties before you call that super init.
You must have completely
done all the ones that you introduced in your class.
Get them initialized before you call super init. And,
you have to call the super one before you can reinitialize or
change the value of any of it's Properties, obviously,
you gotta let your super class have a chance to initialize
itself before you start mucking with its properties.
Otherwise, when units call super, it's probably gonna
blast whatever you did to its properties.
A convenience init can only, must and can only,
call an initializer in its own class, no super.
Only in your own class,
convenience initializer only in your own class. You can
call it another convenience or you can call it designator,
but it has to be in your own class,
not in your super class. And
a convenience init has to call whatever init it's gonna call
in itself before it can set any of the property
values. It's kind of different from an init that calls into
super because that one has to do its initialization,
the designated one, before it calls super.
This, in this case, you have to do convenience,
you have to call your other one first.
And the calling of all other inits, whatever you do, any
inits that you have to call has to be done in all cases,
before you start calling methods on yourself or
accessing properties.
Not setting the properties, but accessing them.
In other words, you have to be fully initialized before your
init can start using your class. All right,
whew is right: a lot of rules there, and a lot of them
are conflicting and all that stuff. Let's make it even more
complicated when we talk about inheriting inits. So,
you wanna get some inits from your super class,
whoa, not so fast. If you don't implement
any designated inits, you only implement convenience inits,
then you'll inherit all of your super classes designated,
but only if you don't implement any of them.
And if you implement even one designated init,
now you're not gonna inherit all your super classes. If you
override all of your super classes designated inits,
then you'll inherit all of its con, convenience inits.
Now why is that? That's because your superclass,
when it implements these convenience inits,
it depends on the designated inits being implemented,
so you have to implement them all.
Of course, you could inherit them all by implementing
none of them as well. So, any init that you inherit by these
rules can be used to satisfy any of these other,
other requirements that we're talking about on the previous
stage. You can make an init required. You just say
required init. Required is a key word, and that will
make it so the subclass has to implement that init, okay,
it's required. Failable inits, we talked about these with
double. If you put a question mark after your init,
init question mark, that means that this init can fail.
And that means that this init, or when you create this thing,
will return optional version of this class. So
we thought it was double.
If we said double parentheses string, it would return
an optional double. It was a failable init, and
that's because you could have said double of hello, and
you would be like " I can't turn it into double: fail".
And the way you fail out of a failable init is you just
return nil. Otherwise,
you don't return anything out of init, it just initializes,
but the one time you return anything is out
a failed init, you can return nil. So, an example here is
UIImage. UIImaged named looks up that image in
the xe assets. Remember that xe assets thing that I moved
off to supporting files at the beginning of the first demo?
This looks it up in there, of course, it might not find it.
So we do if let image = UIImage(named: "foo"),
then we do something, otherwise we fail,
that UI image initializer failed. Alright, Any and
AnyObject. There is no more init, we're done with init.
Any and AnyObject: these are types, special types.
These types really are almost exclusively used for backwards
compatibility with Objective C because Objective C had a type
in it called id, which was kind of like Any,
AnyObject actually. So Swift needed some compatibility, but
Swift doesn't really use Any very much, so
we wouldn't, Swift is strongly typed. So you wouldn't wanna
have a type which is like, it can be anything. Okay, because
that's what Any and AnyObject are, it's like anything,
any type. It's this weird kind of typeless type kinda thing.
The only difference between Any and
AnyObject is AnyObject can only be a class,
it has be a reference type. Any can be anything,
reference or a value type, that's the only difference.
So why do we have these AnyObject things?
Where are they gonna show up? Well,
there are some methods in iOS where one of the arguments
truly could be anything. One example here is when you have
multiple MVCs, which we're gonna learn about next week.
The way you go from one to the next, next is called segueing,
you segue from one MVC to the next.
And the thing that causes a segue to happen, okay,
is the argument in this method called prepare for
segue called sender, okay,
just like we had sender in the IB action. So
the sender is the one who's causing this segue to happen,
well, a button could be causing a segue to happen,
a line in a table view could be causing it. Some custom
piece of code of yours could be causing this MVC to segue.
So when you're preparing for it, you've gotta be
able to say which one it was, and that's just Any. So
if this were a Swift API, they wouldn't have done Any.
They'd have had a protocol where you could be a segue
sender. And you'd have to implement probably some
functionality that would make sense here. But in this case,
it's Any. It knows optional Any too because you can have
nil, the center could be nil. Where else will you see it?
You could have an array of AnyObject or an array of Any.
That could be an array that has doubles and strings and
things mixed in there. Now you might be tempted to use this
in your assignment number two when you see the assignment.
But you're not allowed to
because one of the required tasks says you can't use Any
or AnyObject. Also you wouldn't be very Swifty.
In Swift, if we wanted to put doubles and
strings in the same array, we use an enum.
That's what we did with our operations array, and that's
what we would do in Swift, so we wouldn't use AnyObject.
Another thing you could possibly use Any for is for
a cookie, right, some piece of data that you're giving
out that you don't want people to know what class it is.
And they're just gonna give it back to you at some point,
and you'll do something with it and
only you know what it is.
So you know, an opaque type, you could use it for
that. How do we use something of type Any, because we can't
send any messages to it cuz it's not of AnyType, so
we don't know any methods that it does or whatever. Instead,
we have convert it, and we convert type Any or, or
AnyObject into some class that we know about using as.
As is a key word, and we do as question mark because we don't
know for sure that we can convert something,
so it's an optional. And we use it with if let.
So if I had some variable unknown,
which is of type Any, so I don't really know what it is.
But I think it might be MyType, I'm not sure.
But I think that might be MyType in there. So I say,
if let foo = unknown as MyType,
if let. Then inside there, foo is now going to be unknown,
but as MyType. So I will be able to
send foo message whatever my tape, I'll be able to send foo
whatever MyType understands, methods and vars.
So that's how we use it. We use this "as?", it's called
casting. This casting, by the way, not just for AnyObject,
we can cast other things. For example, if I had a variable
which is, vc which is a type UIViewController,
I could assign it to UI, to CalculatorViewController.
Because CalculatorViewController is
a subclass of UIViewController. So
it is a UIViewController, so I could assign it to vc. But
if I assign it like this, with this typing,
I cannot say vc.displayValue cuz vc.displayValue,
displayValue is a var in CalculatorViewController,
and vc is of type UIViewController.
Even though it points to a CalculatorViewController,
from Swift's perspective, it's typed as vc, and
Swift is strongly typed. So if I wanted to send displayValue,
I would have to say, if I can let calcVC = vc
as a CalculatorViewController Now I can send displayValue to
calcVC. Cuz it's the type that you actually
type it as that matters, not what it's pointing to. So you
have to use as to get it to be something that you really
wanna use. Okay, I'm gonna go a little bit over here.
If you have to go, it's, it's fine, just keep it quiet.
The last thing I'm gonna talk about is user defaults.
User defaults is a very lightweight,
limited database. It's a tiny little database that persists
between launchings of your app. It's great for
things like settings and stuff like that.
Don't put anything big in there. The only thing you can
put in this database is what's called a property list.
A property list just means any combination of array,
dictionary, string, date, data, or
a number, like int, float, double, whatever.
That's what a property list is. It's just conceptual.
There's no actual type property list, unfortunately.
If this were a Swift API, it probably would be such a type.
It could be a protocol. But anyway, there's not.
This is from Objective-C. So since there's no type
that represents that codgepodge of classes,
this API that I'm gonna show user defaults uses Any. And
this is what it looks like. It has this core, set, and
get. Set Any? Now that Any can't really be Any,
it has to be one of those types, string, dictionary,
array, whatever, it has to be be a Property List,
forKey string. So you just put it, anything you want
in this database under a certain key.
And then you get it back out by saying, object for key, and
it returns you an Any. And it's an optional Any because
it might not be in the database,
in which it is returned nil. So it says any but
it's not really Any, it's Any as long as the property lists,
which means it's one of these ten classes or
whatever. The way you read and
write it, you don't create a user default by saying let
x equal user defaults with parentheses.
You say let defaults = UserDefaults.standard, so
that's a type var on the user default struct.
And you get this standard UserDefaults and
then you can send it things like set,
that thing I was telling you, set Any. And here,
I've got three different sets, one of them setting of double,
one setting an array of ints. An array of ints is okay
because array is part of property list. And so is int,
so that's okay. That's a property list. An array
of ints is a property list, a property list. So is a double,
cuz it's one of those types. And I just set them in there.
And then when I get them out, I use that object for key.
And actually for some common types,
like doubles, arrays and dictionaries, there's actually
a nice little method in there to give you that type back.
That's so you that don't get an Any back and
then have to do as. Because you know that objects for
key returns an Any until you give this Any and
you have to go if this thing has a double,
then I've got it. That would be annoying. So instead,
it just has a method called double that does that as for
you. And if the as fails then you, I think either,
I don't know if it returns a double or an optional double,
but you might get zero back, you might get a nil.
I don't remember exactly. But for array in dictionary,
a couple of interesting things to note. Obviously,
the Anys that are inside the array in dictionary,
those will have to be property lists as well, the things that
are returned to you. And also know that dictionary,
this convenience method dictionary,
it's the string is always the key in the dictionary.
If you had a dictionary where the keys were say, ints.
I think that's a valid key, yeah, in the dictionary, yeah,
it is. Then you would not use this convenience method.
You'd have to use object for key and then use as to turn it
back into a dictionary with int keys.
Saving the database is autosave, so you actually
don't have to save it. If you wanna force it to save because
you're worried that the autosave might not happen.
You're gonna exit your program or something, you can use this
method synchronize. It's called synchronize defaults.
It will synchronize it. That's almost it, assertions.
Sorry, one last quick thing. Assertions are just a little
Swift function that takes a closure as the first argument
and a message as the second argument.
All it does is it executes the closure. If that returns true,
it does not crash your program. If it returns false,
it crashes your program and prints that message out. So
you could put, you know, some validation call. This line of
code will not even execute in the version of your app that
you ship on the app store. When you build the app for
release, the asserts are completely ignored.
It doesn't even actually give the closure, let alone crash.
It just ignores them, so this is a debugging thing only,
okay? That's it! For Friday,
we do have Friday session, it's at the normal time. And
it's gonna be on source code management.
A really cool way to manage your code even if you're just
one programmer working on a project by yourself. And then
next week I'm going to talk about doing custom drawing
in your own view. Multi-touch, like pinches and swipes, and
things like that. And finally, multiple MVCs so
we can build a more powerful calculator.
Your assignment two has gone out. It's already posted.
It's essentially enhancing your calculator with a lot of
the things that I talked about today like tuples and
defaulting values and even value types.
All kinds of stuff. So we'll have a lot of fun with that.
>> For more,
please visit us at standford.edu