Hey, guys. Before we proceed onwards and start using Firebase Firestore to save our messages and send 1
messages over for storage, I wanted to quickly take a moment to do a Swift Deep Dive on something that 2
you might have seen in the last lesson that might still yet be a little bit confusing. 3
So notice how in this line here, 4
line 54, 5
we created a cell by calling this method at dequeueReusableCell withIdentifier. 6
We provided the identifier that comes from that cell that we created in the custom xib and we were 7
hoping that our table view would just create this cell and slot it in. But right at the end here, 8
we used this syntax "as!" 9
MessageCell. 10
What exactly does this do and what does it mean anyways? 11
So in this Swift Deep Dive, I want to talk about exactly this. 12
I want to talk about Type Casting because that's pretty much what that "as!" keyword does. 13
Now, recently, I went to this workshop in Wales in the UK where we were casting sword from stone. So adding 14
copper and tin into a crucible that was heated really hot, and then it was poured into this soapstone mold. 15
And after the metal cools down, you end up creating a completely different shape, right? 16
The shape of a bronze sword. 17
Now, there's a lot more to creating a bronze sword including two days of polishing the sword in Wales 18
which is freezing. But by the end, you create this beautifully, shiny mirror-like surface of a sword and 19
it was really really cool. 20
So how does this relate to what we're trying to learn? Now, as your coding and as you're looking at other 21
people's code, you might notice these keywords as, as question mark, as exclamation mark, and is. 22
And in this lesson, I want to explain to you what all of these keywords do, and when we would use them. 23
So here I've set up a playgrounds which has something that you should already be familiar with from 24
learning about classes and object-oriented programming 25
in previous lessons. Here we've got three classes created, one called Animal, one called Human, and one 26
called Fish. 27
The animals all have a name and when you create any new animal, you initialize that new object with a 28
name. 29
Now, we've also created our Human class to be a subclass of Animal. 30
It's inheriting from the Animal class. 31
So, that means all humans also get to have a name. 32
And, similarly, our Fish class is also inheriting from the Animal class. 33
So these are what you would call subclasses and this is the superclass. 34
Now, humans and fish can do all the things that animal can do which is why it inherits. 35
But it can also do some specialized things like humans might be able to code and fish might be able 36
to breathe underwater, 37
so there's really nothing new in this code here. 38
And if any of this looks unfamiliar, then I recommend having a quick revision of the lessons that we 39
did on classes and objects and object-oriented programming. 40
So, now that we've created our classes, let's go ahead and create some objects or instances from these 41
classes. 42
So let's say that I decide to create me, 43
so Angela as a human, and we automatically get this initializer from the superclass Animal class, because 44
human inherits from animal, and the name is going to be my name, of course. 45
And then let's create another human, let's create Jack who's also a human and his name is Jack Bauer. 46
And then, let's create somebody called Nemo. 47
Nemo is a fish. 48
And his name is Nemo. 49
Great. 50
So we've got three objects created from our classes, two humans and one fish. 51
So let's say that we decided to create a array called neighbours, and these neighbours who live really 52
close together are me, Jack, and Nemo. At this point, 53
we've created an array using objects of different types, right? 54
We've got Angela who has a type of human, Jack type of human, but Nemo has a type of fish, and they're 55
allowed to fit into the same array because they have a common superclass Animal. 56
So this neighbour's array is an array of Animal objects. 57
That means if I was to pull out an item, say, the item at index 0 from this neighbour's array, you'll see 58
that it still is an animal class. 59
It doesn't revert back to the human class, even though we know that this is definitely of type Human 60
because the array has mixed types. In order to store them in this mixed array, 61
they have to find some commonality and that is, of course, the Animal superclass. 62
So, now is where some of those type casting keywords come in handy. 63
Previously, we've seen type casting in the sense that we've able to create a double, let's say, 64
myDouble = 0.0, and let's just do a quick old option click check, you can see this is definitely a 65
double data type. But if I wanted to create myDoubleAsAnInt, then I would use the int data type, 66
open up a set of parentheses, and inside here, I get to put in myDouble, 67
and it's able to cast this myDouble which has type double into something that has type int. 68
Now, this is not really quite type casting, even though it does, in fact, change the type of myDouble into 69
an Int. 70
This is actually achieved through initialization. So if we take a look at the documentation 71
for the Swift standard library, 72
you can see that under the document for int it has a whole bunch of initializers. And one of these initializers 73
is used for converting floating-point values such as doubles and floats. 74
And this particular initializer is the one that's responsible for creating a new integer object from 75
the double value, and it rounds towards zero. So without this initializer being created for Int, 76
then we wouldn't be able to do this and change the data type. 77
But in our case, if we wanted this neighbour1 to be treated as the human that it is, we have to do something 78
different. 79
So first thing we can do is we can check what data type it is. 80
So, for example, we can say if neighbour 0, which is this one, is of data type Human, 81
well, then we can print "First Neighbour is a Human." And if I go ahead and run this code, you'll see that 82
what I get printed is, in fact, this because neighbour 0 is indeed a human. 83
So we could perform this check and it tells us what data type it is. 84
And you can do this if you wanted to, say, you could check if Jack is human. 85
And the thing is we already know that Jack is human, and this is why when you try to write this code 86
which is extremely obvious already without any sort of checking the Xcode compiler, it will actually tell 87
you that this particular test is always going to be true, because there is no uncertainty about the data 88
type of Jack. Whereas in this case, when we're tapping into the neighbour's array where everything is of 89
type animal and we said, let's say, try to get the item at index 2, so 0, 1, 2, and we said, well, what if neighbours 90
at index 2 is human. 91
And then, in this case, we actually get nothing printed because neighbours at index 2 is in fact of type 92
fish. 93
So this "is" keyword, basically, allows us to have a object from a certain data type, and then check whether 94
if that object is of that data type. So in this case, if we had a cell created from a UITableVieCell, 95
and we check, if it is indeed that data type between the left-hand side of the "is" 96
and the right-hand side of the "is." 97
So this keyword is used for type checking. So let's say that I decided to go and create a function, 98
let's call it find Nemo. 99
And this function takes a input in the form of an array of Animal objects. 100
So we're going to say findNemo, from, as the external parameter name, 101
and then inside the function, we're going to refer to it as the animals. 102
So, now we've got our function created and we're going to go ahead and loop through our Animal's array. 103
So let's say for animal in animals. 104
So in this case, we're looping through the array animals and for every single animal in that array, 105
we're going to run the code in here. 106
We can either simply just print animal.name. 107
So this is pretty bog-standard sort of "for loop" kind of functionality that you've already learned about. 108
And in this case, when the code runs, when I call findNemo from our neighbours array, go ahead and hit 109
run, 110
then you can see that we've got all the names of our animals in our neighbours array being printed out. 111
So that's just a simple for loop. 112
But what if I actually wanted to find Nemo inside this array of animals? 113
Well, I could say that if the animal that we're currently looping on is of type fish, then that seems pretty 114
suspicious, 115
right? 116
Maybe that's going to be Nemo. 117
So let's go ahead and print that animal's name property. 118
So, now what will happen if I run findNemo from neighbours is that we're going to get Nemo printed out 119
because it's looping through all of the animals and checking each of them to see if they match the data 120
type of fish. And when it does, then it prints out the animal name. 121
Now, notice how, in this case, inside this set of curly braces, we're pretty certain that this particular animal 122
that we've currently got access to is definitely a fish, 123
right? 124
Because we've checked if this animal is a fish before we actually trigger this block. But notice how 125
in here, even though we're certain of that data type, we can't say something like, well, this a 126
nimal.breatheUnderWater because that's what all fish data types can do, 127
right? 128
They have this functionality breatheUnderWater. 129
But in fact it says, "Value of type 'Animal' has no member 'breatheUnderWater.'" 130
So in this case, what's happening is that this particular animal, even though we know it is a fish, we've 131
checked it. It still has that data type animal. So we're not able to tap into the specialized functionality 132
of the fish. 133
So what can we do in this case? 134
Well, we can use something called forced down casting where we cast from the Animal class down to one 135
of its subclasses. 136
Like in this case, fish. 137
So we could write something like let fish = animal as! Fish. 138
So what this now does is it takes that animal which we've already checked and made sure is definitely 139
of type fish, and we use the "as exclamation mark" to cast it down to one of the animals subclasses namely 140
the Fish class. 141
So, now with this fish created, we can now go ahead and tap into that method 142
breathUnderWater. 143
And when we run our code, you can see that Nemo is breathing underwater. So the "as exclamation mark" is 144
used when, let's say, we've created a cell which is still the UI Table of your cell which, remember, 145
is the superclass. 146
And then we wanted to turn it into a subclass, for example, on message cell that you saw in our code earlier 147
on. 148
Well, in this case, we can use the "as exclamation mark." 149
And now, this message cell object has converted or cast its data type into a subclass, 150
so going down, and this is called forced downcast. 151
Now, the problem with a forced downcast is that unless you're sure, 152
say, for example, in this case, we're pretty sure that this particular animal that matches the Fish data 153
type can be cast down into a Fish data type. 154
But let's say that you decided to cast the neighbour at index 1, which is Jack, to a fish. 155
So Jack's not going to like that very much. 156
But let's go ahead and try it. 157
Let's see what actually happens. 158
So, you'll notice that I'm not getting any warnings, 159
any problems, any errors at all 160
because this is perfectly valid code. 161
This is pretty much the same as if we did this, right? 162
The compiler can't tell that it's going to fail. 163
Now, we know because we can see right now what is at index 1, 164
but let me just show you what happens when I try to run this code. 165
You can see that it's at runtime, when your app is actually running in the user's hands, 166
when this line of code is going to crash. And it tells us that it could not cast value of type Human 167
to Fish. 168
So that's the problem. 169
Now, very often what happens is when we're dealing with data that we're getting back from the internet 170
or from APIs, they often have a JSON or some sort of array where we're not really quite sure what's 171
actually in it, 172
right? 173
Otherwise, we wouldn't be fetching it. 174
So in these cases, unless you're certain that this cast is going to work, then a better option is to add 175
a question mark, instead of an exclamation mark. 176
But what this does is it turns this object that's cast from this object to a Fish data type into an 177
optional. 178
So, now if I check this data type, you can see it's a fish question mark. 179
So that means if you wanted to be able to do something like a fish.breatheUnderWater, then you 180
will either have to use the optional chaining, like how it's inserted this question mark here which says, 181
basically, if fish is not nil, then call this method. 182
Alternatively, you would have to use a optional bind, say, if let the neighbour at index 1 can be cast as 183
a fish, well then go ahead and run this code. 184
Now, if I run this code again, you'll notice that it doesn't actually crash anymore because this is actually 185
being skipped. And in fact, I can add an 'else" statement to print that "Casting has failed." 186
So let's run that code and you can see that it's the "else" case that gets triggered in this block of 187
code. So the "as" keyword is a slightly safer way of performing this down casting capability, instead of 188
simply just saying, "Yeah, yeah, you know cell is definitely gonna be convertible to message sell in all cases" 189
when you're not sure it's better to actually use a "as" question mark. 190
So this way, we only actually do the casting if it actually can be cast as the subclass. Whereas in this 191
case, we can be pretty certain to downcast because we know that the animal is definitely a fish. 192
Now, in other cases where you've written the code, say, you've created the array, you know what each of 193
the items are, then you can also make the case that a forced downcast, in this case, is probably okay. 194
So, the last keyword I want to show you is this thing called "as." For example, in this case, we know that this 195
fish object has data type fish, right? 196
But what if we wanted to turn it back into the data type of its superclass? 197
Well, we can say, let animalFish = fish as, with no question mark or no exclamation mark, 198
and then we can cast that to one of its superclasses. 199
So in this case, we could say, turn it into an animal. And this takes the object which has the class Fish 200
and raises up to the animal superclass. 201
So now this animalFish has type animal, whereas it came from this object fish which had the subclass 202
fish data type. 203
So the "as" keyword by itself is simply used to raise a object to its superclasses type. 204
So it's another form of casting but it's, perhaps, use a little bit less frequently. 205
And the reason why it doesn't need to have an exclamation mark or a question mark is because when you 206
take a subclass object and you convert it into a superclass, it's never going to fail. 207
This is always going to work as long as this particular object that you're trying to cast is, indeed, 208
inheriting from the data type that you're trying to cast it to. 209
In other words, the "as" keyword is used to perform an Upcast. 210
That said of casting is performed a lot less frequently than down casting, but it belongs in the set 211
of as, as, as, 212
so it's a good idea to talk about it. 213
Now, the very last thing I want to talk to you about is some of these strange data types that you 214
might have come across. 215
This is a topic that's closely related type casting and working with types in general. 216
Sometimes you don't want to be specific about the data type that you're working with. 217
Sometimes you'll want flexibility and you'll want your code to accept a range of different data types. 218
And in Swift, we have something called "any" which encompasses all objects, so objects derived from classes, 219
from structs. Basically, it can be any data type just like the name. 220
Now, within that group of any data type, there is a subclass of AnyObject data types, and these are object 221
that have to be derived from classes. 222
So sometimes you'll see certain protocols that have a requirement where only an object created from 223
a class can actually conform to that protocol, 224
so they might use this data type "AnyObject" in that way to limit things to work with classes. And then 225
even more specialized are these "NSObjects" which are the objects that are created from the foundation 226
classes. 227
So remember how we often import foundation. Well, inside that foundation module, we get access to NSNumber, 228
and as dates, file manager, and a whole bunch of other objects that Apple has created. 229
So if we were to head back to our code and where we've got this array of objects, right? 230
If I was to create, say, just like an integer, right? Let's call it num and let's make it equal to 12, 231
and I tried to add this number to our array of neighbours, then you'll see that it's not really going 232
to allow me to do that because there's absolutely no commonality between all of these guys. 233
The only way that I can do this is actually by making my array of type Any. 234
So if I had an array of type Any, then this is gonna be fine. "Any" allows us to mix any type, literally. 235
In this example, we're mixing the human type, the fish type, and an integer type because Angela and Nemo 236
are instances created from classes, whereas the integer is created from a structure. 237
So this keyword "any" allows us to mix classes and structures or any other data type. So what happens when 238
I change Any to AnyObject? 239
Now, we get an error. 240
Why. 241
Well, the reason is that AnyObject restricts the types to those that come from classes. 242
So in other words, structures are no longer allowed to play and they're no longer allowed to be part 243
of our collection. And integers, strings, and doubles, 244
these are all structures which is why we get this error. And I can show this to you in another way. 245
Let's say there our fish Class was instead a structure and structures, of course, don't have inheritance, 246
so I have to delete all of this. But there we go, 247
we've got our Nemo which is created from fish. But right now, if I try to run this, this also gives me an 248
error. 249
It says that Nemo which has type 'Fish' can't be converted to AnyObject because AnyObject requires that 250
the object be created from a class, like the Human class, where Angela and Jack are created from. But Nemo 251
is created from the Fish struct which is not an AnyObject. 252
Now, let's say we changed this to NSObject which means that we're even more limited as to what can 253
go into our array. 254
Neither Angela, Jack, or Nemo actually fit these criteria because we need to use one of the classes and 255
foundation that's created by Apple. 256
So, for example, let's say I decide to create a number that's created from the NSNumber class and I set 257
it to equal to 12, 258
or let's say I create a word that's created from the NSString class and I set it to ABC, well, these items 259
can go in here, 260
right? 261
I could put "num" and I could put "word" and if I take out "Nemo," then you'll see that this is perfectly 262
valid. 263
These are both NSObjects that can match this particular requirement. But as soon as I try to add 264
in a struct, for example, on Nemo, or if I try to add in a Human, which is created from our class, none of 265
these will be allowed. 266
So, essentially, depending on how strict your criteria is, you can actually go through these very broad 267
types: 268
Any, AnyObject, and NSObject, and allows you to create collections of items that are more broad than 269
just the classes that we've created ourselves. 270
So, now that you've learned all about typecasting, I have a small reading assignment for you. 271
So if you have some time, I recommend heading over to this link which you'll be able to find in the course 272
resources, 273
and just having a read of this page on typecasting to get a little bit more background information and 274
learn a little bit more about this topic. So consider it an optional assignment. 275
All right, in the next lesson, we're going to be using Firebase to save and retrieve data from the Firebase 276
Cloud Firestorm. 277
And when we retrieve the data back, we'll have to use some form of casting in order to convert it into 278
a data type that has the capabilities that we need.