TOMMY MACWILLIAM: So now that
we've built out the first version
of our Pokédex app
with that static list,
let's take a look at how we can bring
in dynamic data from the internet to use
inside of our app.
So to do this, we're going to
use something called an API,
or an application programming interface.
And you can think about an
API as basically some code
that someone else has written
that's designed in a way for you
to use it, as well.
So in this case, we're going
to be going to a website
and making requests to that website
and getting back data about Pokemon.
So the format that we're
going to get our data back in
is a format called JSON.
And JSON stands for JavaScript Object
Notation, basically a format that
started off as part of JavaScript, which
is another programming language you
might use to write a website.
But it's really become
this common format
to exchange data between
a client and a server.
And so this happens to be what the
API that we're going to use returns.
So here's what JSON looks like.
You'll notice it looks a little bit
actually like a Swift dictionary,
and that's no accident.
So JSON will start off with this curly
brace and end with this closing curly
brace, and everything inside of
that is basically a dictionary.
So we have keys over here on the left.
In this case, we have
three string keys, one
called "course", one called
"tracks", one called "year",
then a colon to separate
the key and the value.
And then after the colon is the value.
So you'll notice here that we can
have a string value like "cs50".
We could have an array of strings,
like "mobile", "web", and "games",
or we could have numbers, like 2019.
In the case of our
API, we're mostly going
to be dealing with strings
and lists of strings,
but JSON has a few other data
types that you might run into.
So before we get into that,
let's cover a couple more
Swift syntactic constructs,
just to make sure we have them.
The first is a construct
called Try, Catch.
So there's this notion in
Swift called exceptions.
And an exception is something that
happens when, as you've guessed,
something goes wrong under the hood.
So here's the syntax of
what that might look like.
First, you'll have a
block that starts with do.
And when you have this
sort of do catch block,
this basically tells
the compiler I'm going
to call a function or a method
that might trigger an exception.
And when that exception
happens, I want to be
able to catch that and break out
of this code and handle that error.
So my next line, this left half looks
like something we've seen before,
just let result for creating a variable.
Then on the right side
here is where we're
doing something that
might throw an exception.
So you'll see this try line.
And this tells the compiler the
function I'm calling right here
might throw an exception.
And if it does throw an exception, we're
going to break into this second block
here, this catch block.
And here you can see
this catch let error.
This says I'm going to catch this error.
And in this case, I'm
just going to print it.
But if nothing goes wrong, then I'll
just never enter this catch block.
So the next piece of Swift syntax
that we're going to take a look at
is called a closure.
You'll often also hear these
called anonymous functions
or in line functions.
But what a closure is is basically
a way to define a function
right in the middle of
your code and then use it.
So let's look at an example.
So suppose that we're
creating a new variable
and we're calling this
variable reversed.
And our goal is to
take a list of strings.
This time it's called names.
And we want to sort
them in reverse order.
So there is a method on our
list of strings called sorted.
And it's going to take one
parameter, which is a closure.
And so to walk you through this
syntax, we have this open brace
and then we have this open parens.
Inside of the parentheses is
where we're going to define
the parameters to our closure.
So this closure or this function just
takes two arguments, one called s1,
one called s2.
And they're both strings, which
you can see from the types.
After that, it's that same
right arrow that we saw before.
And that just specifies the
return type of our closure.
And so from this declaration,
what we're basically saying is
I'm going to create a function.
That function has two arguments.
They're both strings, and this
function returns a Boolean.
After that Boolean, we
have this keyword in,
and then we specify the
body of the function.
So this function is really simple.
We can just say if s1
is greater than s2,
that's going to return true or false.
And this is the
comparison function that's
going to be used to sort our list.
So as you can see here,
we could have done
something like create some comparison
function somewhere else in the file,
and creating that function would be
the same syntax you've already seen,
func and call that compare, have it
take two arguments, return a Bool.
But there's this nice
shortcut where we know
if we're only going to use this function
once and we're using it right here,
a closure just allows
you to define it right
in line, which makes your code a little
bit easier to read and more organized.
So that's all this function
is doing, and we're
going to see one of these
in our API in a bit.
Another concept that we'll see
shortly is this design pattern
called delegates.
This is really common
throughout the iOS API.
You'll see the word delegate a lot.
And a delegate is basically a
way of attaching event handlers
to some other object.
So suppose you have
some object and it needs
to respond to some data
that comes in and you
want that object to notify somebody
else, that hey, I just got some data
and I need you to
respond to those changes.
So that object is called a
delegate, because what you're doing
is you're delegating
the logic for handling
some change to a different object.
And so you'll see this in a bit.
So let's walk through an example.
And what we want to do is use this API.
It's called pokeapi.co.
And this page is kind of
nice, because it shows you
what the API is going to do.
So we have this little
sandbox here, where
every API URL starts with
https://pokeapi.co/api/v2, so
version 2.
There's probably a version 1 somewhere.
It doesn't matter.
And then if I enter in the rest of my
URL, they suggested Pokémon and Ditto,
who is just a Pokémon, then you can see
what the API is going to give us back.
So you can see that
this is a JSON object.
There's things like abilities and
forms, and basically a whole bunch
of information about this Pokémon.
If I come down here, there's also
this up option to view the raw JSON.
So if I click on that,
then this is literally
what the API is giving me back.
And this looks just like the
JSON object that we saw before.
It starts with this brace.
All of our keys are strings.
They have quotes.
After the quotes is a colon,
and then could be anything.
In this case, it's a list of objects.
Here we have a number, and so on.
And so this is the API that we can use
to start grabbing some Pokémon data.
So let's take a look at how
we would do that in iOS.
So we're back in Xcode.
So let's jump back to our
first view controller,
where we defined that
static list of Pokémon.
So the first thing we're going
to do is remove the right hand
side of this expression, because
we're no longer hard coding this data.
Instead, we just want to
have this be an array.
But now after we fetch
the data from the API,
we're probably going to
change the value of this
so it's not an empty array forever.
So let's make this a var.
And then let's also tell the compiler
what type this is going to be.
And ultimately, we're going to
have that same array of Pokemon
that we had before.
So now to actually
fetch that data, we want
to fetch the data as soon as
somebody opens our app, so as soon
as this view is loaded.
And we saw this last time.
We have this method, viewDidLoad--
and I can use autocomplete
to get it for me.
And remember, this
method is called whenever
the view is loaded for the first time.
You don't call this method. iOS
going to call this method for you.
You just fill in the definition
of what it should do.
So remember last time
that in this case, we
want to make sure we call
the super classes method.
So we want in case that the UI table
view controller defines viewDidLoad
and does some important
stuff, we want to make sure
that we do that stuff because remember,
we're overriding that definition
with our own.
So the first thing we want to do is
decide what URL that we want to use.
So let's come back to our API,
and let's go to the documentation.
This time, let's just go
to the most recent version.
And here we can see a lot of information
about what types of information
this API can give us.
So if I start over here at Resource
Lists you will notice here that it says
that calling any API endpoint without
a resource ID will return a list
of available resources, which is
great because we're looking for a list
of Pokémon, so that's perfect.
And it also lets me know that I can
specify this parameter called Limit
to tell me how many results
I want to limit my search to.
So that's exactly what we want.
We just want to take the first 151
Pokémon from the list of all Pokémon.
So now that we know that, let's
come back to this little sandbox
they provide.
And rather than requesting
to pokemon/ditto,
let's just request a Pokémon, since
they said that's what you need to do.
And then let's add that Limit
equals 151 and hit Submit.
So now if I expand this
results, this looks really good.
This looks like exactly what I want.
I have a list where everything
in this list has a name,
and it looks like it also has a URL
where I can go get more information
about that resource
or about that Pokémon.
And so let's also save that
to make sure we have it later.
So the first thing we
want to do is write out
a model that corresponds to this API.
So let's jump back to Xcode and open
up our model file, this Pokémon struct.
So here we see that every Pokemon has
a name and a number, and that's fine.
And notice that this API
also gives you back URL.
So let's add that to our data, as well.
So let's say that every
Pokémon has a URL.
And this URL looks like
it's a string, so we'll just
use a string to represent that.
So now our model has everything
we need to use the API.
So we're back in our
viewDidLoad function.
So now let's start loading this data.
The first thing we want
to do is create a URL.
So let's say let URL equals--
and there's a URL class
that's given to us in iOS,
and there's a whole bunch of
different ways to construct a URL.
We're going to use a simple one,
which is to just take a string.
And so back over here, if you remember,
we wanted to take this Pokémon
at api/v2, so we'll
just copy/paste that.
And we wanted to call Pokémon, and
then we also wanted to specify a limit
of 151.
So now that we have our URL,
let's do something with it.
The first thing we want to do is make
a request to this URL to get that data.
So to do that, we're going to
use a built in class in Swift
call URL session.
So URL session is a
class that's provided
for us that has a bunch of methods in
it targeted at getting data from URLs.
So the way to do this is
you'll say URLSession.shared,
which is basically just an instance
of URL session that you can use.
You don't have to create your own.
And then we'll say dataTask.
And so a data task is
basically just this method
that you can call that's going to
go fetch the data from that URL
and give it back to you in
a string that you can use.
So it looks like it takes a URL, and
we know that we can just pass this URL.
And then we have a
completion handler here.
And this is going to be our closure.
So this is going to be defining
a function that's going
to be called when the task finishes.
So from the looks of it, there
are a few different parameters
we can use here there's
a parameter with data.
There's a parameter for response,
and then there's an error.
And you can see from the
autocomplete that this function
doesn't return anything,
just something that gets
called once the request is done.
So let's call this data,
response, and error.
And what we've just done here
is we've defined our closure.
Here are three parameters,
and then we have
this keyword in to separate the
parameter list from the body.
But you'll notice here that the
compiler is giving us an error.
So if I hit Build here
and I check this out,
so it looks like when I created this
URL, this is returning an optional.
And when we call data tasks, we need
to specify a URL, not a URL optional.
So let's just make sure that this works.
So we can say something
like guard let u is URL,
and if not, we'll
return from our method.
As before, we could just
use an exclamation point
here as well, as long as we just
somehow get from optional to URL.
So now we've done that.
So now we have a request to
this URL, and we have-- looks
like we're going to get back some data.
So now let's write our closure.
From that autocomplete, it looks
like this data is also an optional,
so let's just make sure that we got
back some data and unwrap that optional.
So we'll say we'll just let guard
let data is data, else will return.
You noticed you can actually use
the same variable name on the left
and right hand side of this?
It's kind of nice, so you don't
have to keep renaming variables.
So now that we've made sure
that our data isn't nil,
let's convert that JSON
data into a Swift object.
So first, let's go back to
that API and remind ourselves
what the data that's coming back
down to us from the API looks like.
So here's this API
response, and it looks
like I'm getting back a JSON object with
a few different keys, one called count,
one called next, previous, but the one
I really care about is called results.
And it looks like the type of this
results key is an array of Pokémon
objects.
So let's first create a struct
to represent this API response.
We'll come back to Xcode
into our Pokémon model.
So we have a struct representing
each element in that list,
but we don't have a struct
representing the list itself.
So let's create a new struct.
We'll call it PokemonList.
And this struct is only going to
have one field, we'll say results.
And that's going to
be a list of Pokémon.
So now let's use this new struct
to decode that JSON into an object.
If we come back to our
view controller, let's
create a variable called pokemonList.
And now we're going to say, use this
new object called a JSON decoder.
We're going to create
a new instance of that,
and then we're going to call
this method called decode.
And it looks like this
method takes two arguments.
One is a type to decode into, and
the second is some data to decode.
So let's use that type we just
created, that pokemonList,
and there's this little weird syntax
here where to reference the type
of that struct, I'm
just going to say .self.
And now the second
parameter, that's just
going to be the data that
came down from the API.
That makes sense, because that's what
we want to decode into an object.
So now you'll see here
that Xcode is letting
me know that this
instance method requires
that pokemonList conform to decodable.
So decodable all is
just another protocol
that's defined somewhere in a UI kit.
And any class or struct that
conforms to this protocol
can be serialized or
de-serialized using JSON.
And all that means is that
we can convert back and forth
between a Swift object and
JSON data, which is just
a string representation of that object.
So all we have to do is take those
two structs that we wrote before
and make them conform.
So I just have to say colon
Codable on both of these things,
and now I can go back
and forth between JSON.
So OK, we've got one more error.
Let's see what it is.
It says call can throw, but
it's not marked with try.
So here is where that try,
catch block is going to come in.
Let's say that some JSON came down
from an API, but it was malformed.
Maybe there is a typo or something
else that made it not valid JSON.
So if we try to decode
some JSON that's not valid,
we're going to get an invalid
object as a result of that.
And to keep going with our
program with an object that
doesn't make any sense, that's not
something that we really want to do.
So let's use a try, catch block
to recover from this error.
So. just like we saw, we're going to
put this inside of a block that says do.
We're going to mark this call that
can throw an exception with try.
And then we're going to have
a block to catch the error.
So let's say catch let error,
and let's just print it out.
If this were a real app, you'd want
to display some sort of error message
to the user, but for simplicity, let's
just use string interpolation here
to print out that error.
So now let's build.
Now it looks like we've succeeded.
So let's run this app
and let's just make sure
that we don't run into any errors.
So let's come up here and click Run.
And OK, it looks like we
did run into an error.
We have this key not found.
OK, so it sounds like there's
some key that's not being
found somewhere with a value of number.
So that makes sense, because if you
remember our original Pokémon struct
had that number field
that we were using before.
But if we look at our API response,
there is no field called number.
And so when we're trying to decode
this JSON and create a struct,
we're looking for there
to be a number field.
But there just isn't one,
and so that error is thrown.
So this is a good example
of an error we need
to recover from, because
we declared in our struct
there has to be a field called number.
And the JSON doesn't have one, so it
doesn't make sense for our program
to continue at that point.
So for now, let's just remove
that field number from our struct.
Let's come back to our
model, remove that field.
And now we also have to make sure
we remove any usages of that field.
So let's jump back to that Pokémon
view controller which was using it,
and let's just comment
out this line for now.
Let's rebuild.
Looks like we succeeded.
Let's try running the app again.
OK, this time there was no error, so it
looks like our API request went through
successfully.
But we haven't displayed anything
yet, because we haven't done anything
to actually use that response yet.
So let's do that now.
Let's jump back to our view controller.
So recall that we've defined a field
in our view controller called Pokémon,
and that's an array of Pokémon.
And that's where our table
view is getting its data from.
So let's use this PokemonList variable
and say that our Pokémon field is equal
to our pokemonList.results.
And you can see here
from the autocomplete
that our types match, as expected.
The type of the Pokémon
field is a list of Pokémon,
and that type of the results
field is also a list of Pokémon.
So great, now we've assigned the
variable we're using for our data model
to be equal to the variable
we got back from our API.
So already, we're getting this helpful
error message from the compiler.
And you'll see here that the message
says that we need an explicit self.
So as you've noticed,
every time I create
a field in a class I'm
not typing self.something.
I'm just referencing the field.
Most of the time, you
can do that in Swift.
And it's a little nicer,
because it's less typing.
But we're inside of a closure, remember.
We're inside of this URL
session shared data task,
and then we're writing
this function that's going
to be called when the task finishes.
And when you're inside
of a closure, Swift just
requires that you prefix
any field with self,
just to be a little clearer
about what you mean.
So all I have to do is say
self.pokemon here, and we're good.
So let's try rerunning the app,
now that we've assigned this value
to the results of the API call.
OK, we're still empty, so
what's happening here.?
Well, we've assigned the variable,
but we haven't notified our table view
that it has new data available.
So to do that, all we have
to do is call a method
on our table view called Reload Data.
And what that's going to do is say
I'm going to look back at that data
source and just reload everything.
So every time you change the
data that's backing a table view,
you just have to remember
to call and reload data.
So let's do that.
I'm going to need self again, because
I'm still inside of this closure, table
view, and we'll call reload data.
All right let's run this
and see what happens.
All right, a new error this time.
And it looks like by the looks of
it our app actually crashed here.
But this nice purple line showed us
exactly where the problem happened,
so let's just expand this.
So this says UITableView. reloadData
must be used from the main thread only.
OK, I'm not really sure what that means,
so let's just click on this question
mark to see a little
bit more about this.
So OK, so this just opened up the
documentation right inside of Xcode.
This is great.
And hey, it looks like this code
example is exactly what we were doing.
We're using a URL session,
a data task, and we're
trying to update something on the UI.
So what's happening here is
when I call this data task
method, what I'm actually doing is
launching a new background task.
But in iOS, tasks that are
launched in the background
are not allowed to update
the UI, because the UI
is going to be running in
the foreground of the app.
So all I need to do is say, OK, I've
got some code running in the background.
I have this line that I really
want to run in the foreground.
And to do that, I'm just going to
follow what this documentation says
and call DispatchQueue.main.async.
And that's just going to jump me
right back to the foreground where
I'm allowed to make UI updates.
So I'm just going to copy this
right from the documentation,
and I'm going to surround just this
reload data call with this block,
because this is the only line
that's actually updating the UI.
I don't need to put this
around everything here.
So now that I have that,
let's restart our app.
All right, so now we have the
result of our API call in our app.
We see here we have a
really long list of Pokémon.
It looks like it has
all of the original 151.
But one thing is a little different.
And that's it.
All the Pokémon names are
no longer capitalized.
And I kind of like the way that looked,
so let's take that data from the API
and capitalize it before we
display it inside of the cell.
To do that, let's create a
new method called capitalize.
So we're going to say func capitalize.
Our method is going to take one
argument, just some text to capitalize.
The type of that argument is a string.
And we're going to return
the capitalized text.
So our return type is
going to be a string.
So let's think about what
we want this method to do.
Given some string, we basically
want to take just the first letter,
capitalize that, and then append
on the rest of the string,
since that will result in just the
first letter being capitalized.
So there's some handy methods in
Swift already defined for us that we
can use for our capitalize method.
So the first one is called prefix.
And prefix is going to take a string
and give us a substring depending
on how many characters we need.
So if I say prefix 1, that's
going to give me back a string
just with the first character.
And that's exactly what we wanted.
And then there's another method
on string called uppercased.
And that's going to do
just what you think.
It will take a string, and
uppercase every letter.
So that's the first
letter of our string.
So now let's add the rest of the string.
So now I just need everything
except the first letter,
and I don't need to do anything to it.
To do that., I can say text. dropfkirst.
and this is exactly what it sounds, this
string just without the first letter.
And that's really handy, because
that's exactly what I wanted.
Lastly, the compiler is reminding us
that we just have to return this value.
And now if we build, we
don't have any errors.
So let's just easily use
this new capitalize method.
Let's come down here to our
cell for row at index path,
and rather than just displaying name,
let's display capitalize of name.
So now let's try
rerunning the app again.
All right, now this looks
what we want it to look like.
All of our Pokémon names
are now capitalized,
just like they were before.
So now that we've used the API in our
first view controller, that table view,
let's also utilize the API in our second
view controller that's displaying some
information about Pokémon.
Right now when we hardcoded
that list, we just
hardcoded the name and the number.
But there's a whole bunch of information
this API gives us that we can display.
So let's jump back to our API to
remind ourselves what that looks like.
Let's just try giving
an example URL here.
Looks like it's this first API response
gave me back this URL, so let's
just see what happens
if I were to load that.
OK, so here is our resource
for just this one Pokémon.
There's a whole bunch of fields here.
We've got strings.
Some are arrays.
Others are objects.
And it has this ID.
That's the number that we're using.
It has the name.
That's just the name.
And just for simplicity, let's say
that we just want to display the types
of each Pokémon in our Pokedex, kind
of a classic Pokédex thing to do.
So we wanted to use this types field.
And it looks like this types field is
an array, and each element of that array
is an object.
And inside of that object, we
have an integer, called Slot,
and then we have another
object, called Type,
with two more keys that are strings.
So in order to use this API call,
let's model this data with structs,
just like we did before, so that
we have a Swift model that reflects
what we're getting back from the API.
So let's jump back into our model
file and create some more structs.
So the first struct
that we can create is,
let's call this a Pokémon data since
it has a bunch of data about Pokémon.
We know that we're going
to be decoding JSON here,
so let's just have it conform
to codeable right off the bat.
And now there's a few
fields that we care about.
We care about this ID field
because that's the number.
And we also care about this Types field.
But it looks like this type is
going to be an object of its own,
so let's sort of work
backwards from the bottom up.
Let's first define a struct
to represent a single type.
So we'll say struct Pokemon
type needs to be codeable.
And that has two things.
It has a name that's a string
and it has a URL, also a string.
OK, so that's our type object.
Now let's look at this
other containing object.
It has a key called Slot, that's an
integer, and it has that Type Key,
so let's just call this a type entry.
So we'll create another struct
called Pokémon Type Entry.
It's gotta be codeable.
And it has a Slot, that's an integer,
and then it has a list of types.
And so there, we can use
our new Pokémon Type.
So lastly, we have this
types field and we're back up
to the root of that API response.
So we can say let types be a
list of Pokémon type entry.
So now we've created these three Swift
structs that map to the API response.
We want to extract this ID
field and this Types field,
and then inside of each types--
we have a typo, actually.
This should be type instead of types.
We have a Slot and a Type.
And then each Type has a name and a URL.
OK, so now that we have
our data model, let's make
another API call, this time
from our second view controller.
So let's jump back to that
first view controller and let's
just copy what we had before
inside of viewDidLoad.
So I'm just going to copy
all of this, and we're
going to paste this into
our second view controller.
We were doing some stuff
before here, so let's
just remove that since we're
going to redo it using the API.
OK, so the first thing we want to
look at is the URL we want to request.
This time, we don't want
to request this list URL,
we want to make requests based on the
Pokémon object that was passed in.
So to do that, we can
just say pokemon.url.
And so now, this is going to be dynamic,
depending on what the user tapped on,
we're going to get a different URL
pass to this second view controller.
OK, so this URL session data
task, that's all the same.
We still want to make
sure our data is not nil.
That's the same.
But how we're parsing it
is going to be different.
So this time, rather than
having this Pokemon list,
I'm now going to have some Pokemon data.
So we're going to change
this to be Pokemon Data,
and we're going to use that
new class that we just wrote.
Now that we've decoded this Pokemon data
object, let's use some of these fields.
First, we can set the
name of the Pokemon
based on the object that's passed in.
We actually don't need to change that.
The API also has the name, but we
already have it from that first view,
so no need.
And remember, we're inside of a
closure, so we need to say self.
Next, let's set the
number for the Pokemon.
That came from our API, remember.
We now have numberLabel.txt,
and we can say pokemonData.id.
And just like before,
this is an integer,
so let's use that string format again.
Let's say format:"#%03d"
and pass in the ID.
And here we have another helpful
warning, we forgot self here.
Let's just fix that.
OK, so now we've set those
two labels we already have.
So to display that type information
we just got from the API,
let's just create a couple more
labels inside of our storyboard.
Let's jump back here,
just like we did before.
Let's come to the top
right, add in a few labels,
and let's give some
place holder information.
We can say Poison.
Let's copy and paste.
So one type is poison,
one type is grass,
and those are some place holder data.
Let's just expand these to make
sure that text is going to fit.
On the right hand side,
we're going to center that.
Same over here, let's center this.
OK, so now we have
our two new UI labels.
Just as we did before, let's
create outlets for those labels.
We'll have one IV outlet called
Type 1 Label, that's a UI label.
And we'll have one more IV
outlet called Type 2 Label.
And you'll notice here that
the circles over to the left,
they're not filled in.
And that's indicating to us that
we haven't actually connected these
to anything on the storyboard yet.
So that's kind of a handy reminder.
So let's do that.
Just as we did before, we're
going to open up our scene,
we're going to hold down Control,
drag over to the labels on the right,
select Type 1 Label, Control drag all
the way here to the label on the right,
select Type 2 Label , and now
our outlets are hooked up.
So now let's use them.
We know that Pokemon data
has a field called Types.
And this is an array, so
let's iterate through them.
Let's say for typeEntry
in pokemonData.types.
Now we can use that
Slot and Type object.
So we can look at this
value of that Slot key
and use that to determine
which label we want to set.
So for example, we can say
if typeEntry.slot is one,
then we can self.type1Label.text
is typeEntry.type.
And it looks like here I
just happen to have a typo.
I made this a list instead of an object.
So let's just jump back to our
model, and this should just
be a single pokemon type.
That makes sense because each
type entry has a key called Type
and that type is an object, not a list.
So now that we've fixed that typo,
we can say, typeEntry.type.name.
Similarly, we can say, else
if typeEntry.slot is 2.
Then we have type2Label.text
is typeEntry.type.name.
OK, so it looks like we're now using
all of the fields that we defined,
and we're setting some things
in the UI based on those fields.
So let's try running our application.
All right.
There's our table view.
Let's try tapping on a Pokémon.
And OK.
It looks like we got that
same issue we had before.
Not only does reload data
update the UI, but we're
trying to manipulate some labels
here, and that's also going
to need to happen on the foreground.
So to do that, after we parse this
JSON, let's just put everything in here
back on the main thread by
saying, DispatchQueue.main,
where I can make UI updates,
call this async method, and let's
just indent everything
here and close our brace.
So now let's rerun
the app one more time.
Here's our list.
Let's tap on my favorite Pokémon.
And it looks like we're now
loading this data from the API.
Every Pokémon I tap is going to have
different data coming in from the API.
So one last thing--
some Pokémon have two types
and others just have one.
And it looks like this
is a little messed up
because I left in some
of that placeholder data.
I happen to know that
Charmander isn't a grass type.
That wouldn't make any sense.
But in my storyboard, I had
this placeholder data just
to kind of remind myself
what the view looked like.
So if you're going to do that, make
sure you clear out any placeholder data
when you're loading in data
from somewhere dynamically.
So to do that, all I'm going to do
is just clear out these placeholders
by saying, type1Label.text is
empty, type2Label.text is empty.
Since depending on what
the API sends back to me,
we might not have values for those.
So now if I run the app again, now the
Pokémon that only have one type show
one type, and the Pokémon
that have two show two types.
So that's it for our Pokédex app.
We've replaced that hard-coded
data with dynamic data
that we're loading in from an API.
And that API has lots more
information that we can
leverage to create a really cool app.
Now, when you're making your own
apps for your final projects,
you can look at all of the
different APIs out there,
and bring in data from different sources
and display it inside of your app.
Now, in the next video,
we'll take a look
at another type of app,
this time using images.
