Hey everyone.
My name is Mikaela Caron. I'm a full time iOS developer and recently
I've been learning Vapor, so I'm excited to show you how you
can use Vapor to build your own API. So if you go to Vapor Xcode,
let's learn like what is Vapor? It is a web framework so you can use Swift
to build the website back end of your iOS app rather than using something
like NodeJS or Express JS. So let's get started installing it. First we need to install Homebrew. Homebrew is a package manager for macOS, so scroll down to Homebrew and then
the website is Brew sh that will be in the links below
and just copy this and paste it into your terminal, but I already have it, so I'm
not going to put that there right now. Next we need to install Vapor. So when you're on the main Vapor
website here click on Docs and then install macOS and then
that will bring you to this page. So first you need to download Xcode if you don't already have that
and then go down to Brew. Install Vapor. So copy this and again paste it into your terminal and that'll take
a second to download. So to double check that you have
everything installed, you type Vapor help and then you'll see this like
list of commands that you can run. We are also going to be using Postman, so
Postman can help you test the API side. So our Vapor API without needing like
the iOS app fully functioning yet. So go to postman dot. Com to their downloads page
and click on download the app. Lastly, we will install Azure Data Studio. So Azure Data Studio is basically a Gui that we will be using to read from our
database, so we can read the data in our database without needing the API
and without needing the iOS app. So this is the Microsoft product, but scroll down to macOS, download
the Zip file and also note right here. It is not compatible with Arm architecture, meaning it's not fully
compatible with the M one MacBook. So you may need to use like Rosetta or something to open it and download
and make everything work properly. Otherwise you can use this application
called Dbever to look at the database. I didn't want to use this one because I
think it's uglier, but you can use this one to download
it or to view the database. And lastly we will be using Docker. So go to Docker. Com and go to their downloads again
and then download Docker desktop and make sure again you choose the right one
for the operating system that you're on. And after you do that open Azure Data Studio
so it'll look like this and you can see it's an open source Microsoft product,
so it looks very similar to Vs code. So click on this button and Chris like the
packages and extensions that you can do. So just search Postgres SQL and this is
the one by Microsoft and make sure to click install which Mine says
uninstall because I already have it. But click on install and then that'll help us because this is the type
of database we will be using. So once we have all of that, let's go and create our new project
so you can see the commands here. Tell us what we need to type. So we need to type paper new to
generate a new application here and we will call this Ytvaper API. So hit enter and it'll start
creating our new project. So do we want to use fluent fluent is what is called an Orm which is
an object relational Mapper. And we will be using that in this project. So type Y and then hit enter
what type of database do we want to use? So we will be using Postgres for this. And then do we want to use a leaf? We are not going to be using that for this project, but it's a templating language
like for making some of the website. So you'll see it ran. If we make this a little bit bigger, it
generated all the files that we needed. It automatically creates a Git repository
for us and creates our own commit, so we don't actually need
to go through those steps. Here's the pretty little water droplet,
and then CD Ytvaporapi. So that is our project. And then we use Vapor Xcode
to open the project index code. So we CD into our project directory,
and then I do have an extension on here. So that's why Mine may look
a little bit different right here. And then we type a vapor code
crew the project. So the first time that we
opened our project here, I'll put that down there it is going and getting all of the different packages
so you can see right here. It's Loading and fetching everything. So we have to wait for this to finish. And while we're waiting for that,
so what we're going to be making is the back end for an iOS app
that will then talk to a database. So the four database operations are
create, read, update and delete. So what that means create is
adding new data to the database. Read is reading the data
from the database. Update is updating any data that's already in the database, and then
delete is deleting it. So when we write to the database,
we're performing these three operations. So we'll be doing create so that's creating some data on the iOS app,
sending the data to our server to the Vapor API and then that sends
it and saves it to the database. And then when we update and delete that's again an operation coming from the iOS app
telling the API to do something, the API taking that and doing
something to the database. And then when we go to read from the database, it starts
with the iOS app asking for data. It goes to the API or the server that tells it, hey,
this is what data we need, and then it translates it and grabs
that data from the database. So the database takes that data. It gives it back to our server
and then sends it back to the iOS app. So we had to download Homebrew, which is a package manager that we had
to use to download everything else. And we had to download postman postman allows us to test the API without actually
needing to create the iOS app first. And with the database we are going to be
using Azure Data Studio or possibly Dbever, if that's the one
that you had to download. And that allows us to look at the data directly in the database
without having to use our API. So first things first. Let's run the project. So we click Command R. Like any other iOS project, it starts to build a project and make sure
the scheme that's selected is my Mac, and now we get to wait for it to build. You can see these are all the different
packages that are inside of our Xcode project, and these are all the different
things that Vapor needs to be able to run. Okay, after running your project, it may take a while to build on the very
first try, but you'll get this warning saying no custom working
directory set for the scheme. This is because by default it tries to run your application from
the Derive Data folder. Close that from Derive data,
but that's not what we want. We want it to run within this scope. So if we stop our application, click on this button
and then click Edit Scheme from here. Go to make sure you're selected
on Run and then go to Options. We want to check use Custom
working directory from here. Click on the little file folder
and then go and find your project. So find the project
in Finder and then click choose. So make sure you select your folder and just click close. And now when we run our project again, it should build faster this time because
it's not the first time we're building the project and we won't
get that warning anymore. Okay, so you may get this prompt says Run
would like to access your documents folder or this is wherever you
ended up saving it. Just click on.
Okay, you may also get this error. So this is warning like address
already in use for some reason. Every so often it doesn't fully detach from the process when you're
running your application. So what we need to do here is type Lsofi80. 80.
Which that's the part that we're trying to use Port 8080 and this says
it's already in use. So if we do this, we'll
see something like this. So Localhost is already using this. What we need is this number PID. So don't mind this. This is part of my terminal. I have like extensions on mine, but type
kill nine and then that PID number. And when you do that, typing the first
command again, we don't get anything. So if we stop our application and run it
again, everything should run like normal. So here we go.
We said server starting on http. 1270, 180 80. So if we open postman right here and go to that address,
if we click, send it's, sending the request and we get back,
it works, which is great. It's exactly what we want to see. And then also by default, another route
which we'll learn about is in there. So if we click send on Hello,
it says Hello world. So that is exactly what we want to see. And we know our project is working. And then you can see
in the debug log here. The first thing we did
was the root directory. So that's where we just went to this
address with no extension or route on it. And then that's just the request ID. And then we went to slash Hello and we can just see both of those
requests that we made. So let's look into the file structure. Let's first look at package Swift. So Package Swift is our package manifest for SPM, which is Swift package manager,
and Vapor heavily uses that. So you can see right here. We have a little package. And then if you actually look at the contents of
this directory for our project, we actually don't have
an Xcode project file. That's because this is like a package. You can see we're using Vapor right here.
We are using fluent. So we said that at the beginning
and we are also using Postgres. So if we open up sources sources is
where all of our code is going to live, and we look at controllers by default. It gives us a to do controller by default.
It gives us a Todo application. So here this is where we can group logic inside of controllers rather than
putting it all on a single file. Migrations are used with the database. So we'll be making what are
called database migrations. And this is things like preparing our database, meaning creating
new tables in our database. We have our model. So this is what models the data
that's in our database. And we can use these
within our application. And we have the configure function. So this just has one function in here and
it configures the database we'll be using. It adds our migration and it runs
the routes function and then routes function is where all of our routes are so
different endpoints that that will be hit. We will be requesting
from with our iOS application. So we saw first the get route right here. We just hit this basic
1270 one at Port 8080. It returned.
It works. And then when we did Hello right here,
it returned Hello world. So these are the different
endpoints that our API has. And then here's the to do controller because the controllers
have their own routes. Instead the run folder. This contains the code that we need to get our application up and running
so you can see right here. It calls the configure function
that we defined right there. Instead of tests. By default, Vapor gives us
a unit test that we can use. So the unit test right here
is starting the application. We are going to the route Hello. And then we want to assert that this says
okay, and it gives us back Hello world. We will not be going into unit testing for this series, so we have
a Docker compose file. This is used to create
a multi Docker application. So this configures our API to run and
for the database to run in Docker file. This is what is used to actually create a Docker image, and I honestly don't know
everything that's in both of these files, but we will get into that
a little bit later too. And last is package resolved. These are the exact versions of the packages that are inside of our
project that are defined in Package Swift. So if somebody were to download and clone your application from like GitHub,
they'll use these exact versions. Unless you go and ask
to update the packages. That is the end of this lesson. We installed all the tools that we'll need for creating our Vapor API
with our iOS app. We had to install Homebrew,
which is a package manager for macOS. We installed Vapor, we installed
Postman and Azure Data Studio. We created a project and ran it
for the first time, and we used Postman to hit the default
route and the Hello route. So this is the first video
that I've ever recorded on YouTube. So let me know how I did in the comments and if you have any suggestions,
I will see you in the next part for this video, I'll show you how to use
Vapor to create tables, use migrations, and how to read and write
data to the database. For this example, we are going to make
a list of songs to read and write. Hey everybody, my name is Mikayla Karen. I am a full time iOS developer,
and recently I've started learning Vapor, so I'm excited to show you how you
can use Vapor to build your own API. Okay, so here we are
in our project directory. When I type LS for list,
you can see all the different files that we have so we can open
this by typing Vapor Xcode, and then you see it says
Opening project in Xcode. Okay, you can see when we open our project, you have all of the
packages starting to load. So all of the packages,
these are all the ones that we need for our project and Xcode is going
and fetching everything for us. The first thing we'll learn
about are what our routes. So we looked at that briefly
through the last project. Or when we built the project for the first time, we open up this routes file
and we see app yet, and it returns. It works and then app yet. Hello returns Hello World. So if we open postman and you may not see these, I just added these because
we will be using them later. But if you just click on the little
plus here, it might be over there. You can create a new request, so we will request from
this after we run it, which we'll have to wait for mine to finish
building for the first time. So you can see it's still
fetching everything. But what we're going to do is we'll see that when we just hit the base route,
it returns, it works. But then if we hit the base route Helloworld or Hello,
it gives us Hello world. And so this is our route. Really. But when we open the project for the first time, we actually have
a bunch of files here. And this is a to do controller create
to do a model called to do because by default, Xcode gives you a to do
project to start everything. Let's type this. Let's go to this. So we'll end up using songs, but right now
we just hit the base route. So Chris is the root route and wait
for my project to finish building. Okay, let's go ahead and build
our project for the first time. So we click command R and make sure the scheme
that's selected says my Mac. Okay, here we go.
Okay. So when you run the project, sometimes you'll get this run would like
to access your documents folder, or this might be wherever you
ended up saving it, just click on. Okay, then we see notice starting
server on 127 one at Port 80 80. So right here I type in that exact URL, and when we hit send
on the root directory, we have our request sending. When we get back, it works
and we can see right here. That's exactly what we wanted back. Then we do Hello. So the slash helloroot and we click send and we get back Hello World because
that's what we Typed right here. So we can see that down here. So we went to the root directory first and
then Hello, not directory, but root next. And then we have this controller. So let's go ahead and delete all of the
controllers and everything that comes default with the project
because we don't need that. So we just click on stop
and then we'll see down here. Eventually, once my computer gets there,
it says it stopped it. And then this is the message
that will sometimes come up. So when we try to start this again, it might give us that weird error
where we have to do like, Lsofi 80. 80 and like, kill that one process.
We'll get to that though. But let's go ahead and delete
this to do controller. So just click on it. Click delete, move to trash and let's
go ahead and delete everything else. So create the to do delete that one
move to trash. Delete the to do model. We don't need that. And we can take out that line and it says can't
find it because it's not there. And then inside of Configure,
we can take out this migration thing here, which we're going to add
it back with our own information, so we can keep these routes here because
it doesn't hurt anything to have them. But we are going to make
our own controller. So we all write our own routes inside of the controller rather than writing them
right here, because if you have a big application, it'll start
to get very crowded. Let's go ahead and click on this
and then do command in or file new. To create a new Swift file,
we'll add a Swift file, so we click next. You see Xcode kind of does this rather
than what it does with an iOS project. You can immediately type
the file name here. So we will call this the Song controller, because this project will be about adding songs to a database you
can see with vapor. It sometimes does this incorrectly. So we have the song Uiviewcontroller,
but it didn't rename this part of the file by default like it
does when we do iOS projects. So we will just change it right there. And before we get into this part, actually, let's go ahead and create our
migration, and I'll explain what that is. So let's name the migration
createsongs So file new Swift file, and we will call this
songs and rename this part again. Okay. So first, what are migrations migrations
are used when creating our database? So when you think we have version control within our Xcode project,
when we type new lines and change the code, we use Git to tell us what have
we changed and we can revert back to certain points in time
for migrations with the database. For a database, we use migrations
to do that, as opposed to using Git. So we need to first import Fluent and Fluent is a framework that's actually an Orm so that's an object relational
Mapper or object relational mapping. And we will use that for the migration. So the migration will then track the differences between our database
for things that we add. So first let's create a struct called Create Songs, and it will inherit from migration. So you can see it says fluent. Migration can handle database migrations, which include adding new tables
or changing existing tables. So this is pretty much like Git. It's tracking all of our changes. But instead of for our code,
it's for our database. It will try to build,
and I think it'll tell us there is an error. So when we build our project, it will give us this error that we do
not conform to the protocol migration. So if we add the protocol stubs,
it gives us these two functions. So we said migrations track changes.
So prepare. These are the changes we want
to make and then revert. This is what do we do if we
wanted to revert those changes, let's get rid of the code completion. And the first thing that we're going to do
is we will use this to create our table. Our table is going to be called songs. So we want to return from this and we want to use the database
that we're using so database schema. And we want to call this song. So this is the name of our table. And what does our table have? So tables have columns,
and they have rows. So rows are all of our data. And then columns are
the different properties of our data. So we want an ID property, and we want a field. So the field we want to actually,
that's not the right initializer. Yeah, we'll use this one,
but we're only going to use part of it. So we want to
give it a column name of title. Then what type it is? It is a string. We want to create a new field. We want to give it the name of title,
and it will be of type string. And then we want to make
this field required. And then here we want to create this. So this means we have
a table called songs. It has a column called ID
and a column called title. And then we want to go ahead
and create this table. And that's what all of this means. Now on the other side of reverting, if we want to revert all of the changes
that we made in prepare, we have to do revertinghere. So we create the table with these kind of fields, and then to revert that change,
we just delete the table completely. So this is our migration that we
will make for our songs table. Now how do we represent
the data that's in this table? We have to create a model for that. So if we go to the Models folder command n for a new file,
want to make a new Swift file, we will name this song. We need to import two things. We will need fluent. It's going to conform to model and content. So think of it like a normal class
that would make within an iOS project. This is how we want to represent our data. But because we are using fluent, this is how you're going to query
the data from the database. We need to first say static. Let schema equals songs. So this is what schema
are we talking about? Meaning what table are we talking about? So this class will
represent the songs table. And then what is in this table? We have an ID property and a title property,
but we can't have only this. We need to add a couple of things on this to help with fluent and recognize
everything that's in the table. When we add Chris on top of it,
it's telling fluent that this property of ID matches the ID property within our
table, and then the field that we were talking about when we look
at the migrations matches. Now our property called title,
and we need to make our initializers. So we'll first have an empty initializer and then one with all of our properties. Okay.
So here we have our two initializers. One will just be empty, meaning it doesn't accept anything
meaning it will have both of these. But then one that will set the ID
property to null and then the title. And then here we are setting
these two properties. So our song class represents our
data inside of our database, which here is our table, our table called songs,
and it has the ID and title properties. And then we can see our
model that we have song. It uses the table songs,
and we have our ID and title properties. So let's go ahead and add
data to our database. So we will do that with the song
controller, as opposed to using routes in this file and making
the route file get very long. We'll separate our logic
into this controller. So this will be a struct
called song controller, and you can see it automatically added vapor, because that's what we
need for this route collection. We'll also need fluent
instead of foundation. So our route collection. This is what is used because a controller
is really just kind of a collection of different routes
and different functionality. So when we try to build this,
we will see that it again fails and tells us we need to conform to route collection,
and we currently don't. Okay, so after it builds, we can see that it fails
and we want to add the protocol stub. Let's go ahead and click fix, and we
just need this one function called boot. So kind of think of it
like an init function. This is the first function that runs. So what we want to put in here. Okay. So first let's type letsongswell
routes groupedsongs. So what this is saying is when we go to the root route,
so 1270 one and then dostongs everything that is then grouped under
this will go to that. So to make that more clear,
make a new function. Okay.
So to make this more clear when we go to the routethsongs, we then use
thissongs get and then use index. Meaning when we go to songs,
we want to use this function. So you can see that's similar
to when we do app get. Hello. It has the functionality right here. But because we are grouping it within the songs controller, when we go to
Slashsongs, we want to group everything. Then instead of app yet we have songs yet,
and we are using the index so that will look like Chris one. So we will go to the root route,
then dotheroutesongs. And that's what we are making right here. So that's what this one means. So let's write the code for what
happens when we do this. So this is all we need. So we return songs query, which Chris
means this is referring to our song. Right here our model. So because we are using fluent fluent is
what is actually doing all of the functionality to go
and talk to our database. So we do songs query, meaning we want to go get data
you want to do on the request DB. So this is our request,
which is what it's called when you say hit the database or hit an API,
it usually means make a request. So this is called a request DB. So we want to use the database that we
have, like within our project. And then we do all because we want to get everything back what this means when we do
all we want to return everything that comes back from this query, and that
will come back as an array of song items. But before we can do this,
we haven't actually created the database. So when we can see in this configure file here, this is creating our database. It's a Postgres database. And then this is trying to get
an environment variable for our host, the Port username password,
and then the database name. But instead of making an environment file right now, if it can't find that,
it'll just use this uses Nilco Lessing, and we'll use whatever is
given here by default. So by default, our database host is localhost the vapor password,
and then the username, and then the password is right here,
which these are generic. So this is not what you actually you don't want to hard code any
of these values in your code. That's why right here it goes to an environment file,
which will end up creating glitter. And then right here you could see that we did delete the
migration for creating that to do, because we don't want to create
to dos in our project. We want to create songs, but we do need this migration
similar to this for creating songs. We want to create our song table. So if we go into configure, we type app migrations.
Okay. So we have Typed app migrations add
createsongs because we want to add our createsongsmigration to all
of the migrations like list of them. And then right here we do
tryapp automigrate wave. So this means all
the migrations that we have. We want to actually run them, and that
will go and create our database for us. And then it will wait for this
to complete before doing everything else. Okay. And before we can run the project,
we created our stock controller, but we haven't actually added
that to the routes file. So our only routes in the project right
now are this route one and Hello, let's go and add our song controller. So we type, try app register collection, and then we want to pass in our
song collection or song controller. Sorry. And we can see it conforms
to route collection. So that's saying right here we are adding the song controller,
which conforms to collection. So this is the type of collection. Now, when we run our project,
it will first go back to configure it'll. First create our database for us, and then right here it'll create the songs table
that we have using the migrations. And then lastly, it will try
to run this function routes. If we go here that function, it'll register these two routes,
which we won't really use. But then here it'll register our song
controller inside of song controller. We have a route called Flashsongs, and then here we have the ability
to read all of the different songs. So this is the slashsongs route. So if we go ahead and run this,
nothing will really happen because we don't have anything in our database,
but we can still run it anyways. I must type that. So we'll stop this and not run it. Okay.
So in mind, we can see that we got the same error that the connection
needs were set and connection refused. Okay, so what this actually means? An X could eventually show me
like it can't do something. What this meant was that our database,
it says connection refused. So what are we connecting to? It cannot connect to the database because
the database is actually not running yet what we need to do. So if you click command tab
on our terminal here, it opens a new terminal window, basically,
but within the same directory. So we're still in the same place. So let's go ahead and stop that. Eventually it'll stop. What we need to do is run our database
because it's actually not running. What Vapor does is with an Xcode. It kind of just only runs the application,
the API sort of side. It doesn't really run the database. When we look at our Docker compose file, we can see here we can start the app
and we can start the database separately. So let's do that. We need to start the database with Docker. And if you get close this and you try to start the database. So if we do dockercomposeupdb,
which this does have a hyphen, but I've Typed it without a hyphen,
and it works just fine too. So if we type Docker,
compose UPDB, hit, enter it's trying to start the database. But if you get back this error of error response from Damon,
that just means that Docker is not running, and that's
what I had just closed. So you need to type in Docker. So it will try to open the Docker app. If you hit enter, you'll see up here the little Docker
whale is trying to get up and running. So it says Docker desktop is starting
and let's wait for it to boot up. So this is the desktop application. It'll say Docker engine is starting. Once Docker started up, you may see this that says nothing is
running or you don't have anything here or have like, one of those tip kind
of prompts, but this one was actually from the testing the API, but we can
close this or you can leave it open. It doesn't really matter. Let's go back here
and type the same thing. Dockercomposeupdp we hit enter. Now you can see it's
starting to do something. So what it's doing is creating our
database for us within a Docker container, and it's doing this by using our
Docker file here in Docker compose. So now if you still have the Docker desktop application open, you can see
YT vapor API that is right here. That is the name of our project, and we are actually running it
from the command line here, as opposed to running it just
through the GUI application. You can expand this and see here is our database, and it's just called
DB One, and it is of type. It's a Postgres database. So now that everything is running now,
let's see if we run our API. We should not get a connection. Refused because our API was trying to connect to our database, but the database
was not actually up and running yet. Okay, on mine, we saw this
address already in use. So that's a different error that goes back
to that weird command that we have to type in the command line, because sometimes
Xcode doesn't fully detach the process. So if we go back to our first
terminal tab here and type Lsoft, it shows us all of these
things that are running. So to know which one to actually close,
it's usually the one that says local host. I'm not sure why that actually appeared in mine,
but typically what we'll do is delete the one that says something about
localhost and we can delete this one. This is postman because I think this is the one that the database is running on,
but apparently it froze mine. So if we click command T
to try to open a new tab, close that type Lsofi 80 80 and don't have any spaces, it comes up with the same things we'll do kill nine
and then the PID number. I don't know. Okay, so if we look at this,
it still says something is running. Let's try to kill that one as well. And if we type LSOs, okay,
now we don't have anything going. So now let's try to run our project again. Okay, here we go. Now we have server starting
on our root computer, but we can't really see anything. Let's open postman. Okay, we have open postman, and if we try to hit the new route that we
just created for getting all the songs. So this was me.
I saved this request within postman. You can do this all without
signing up for an account. If we try to click send, we get back an empty
array and it says 200. Okay, and that's exactly what we want. A 200 response is a good response,
and then we can see we hit the slashsongs route, but it says empty array because we
haven't added anything to our database. If we want to look further into this,
let's open up Azure Data Studio. So this was another one of the
applications that we had to download. And with Azure Data Studio, we can then look at our data in the
database directly without using the API. So to do that first,
we need to add location. Sorry, add a connection and we are not using Microsoft SQL Server. We are using Postgres SQL
and we have that option because cancel when we downloaded the extensions. We downloaded the extension Postgres so we can go to add connection PostgreSQL. So the server name is localhost because in configure right here
the database host is called localhost. So database host and server
name are like the same thing. We want to use password. We don't have Azure active directory, and then the username here we are using
Vapor Username and Vapor password. You can click remember
password if you want. It doesn't really matter. And we want to use Vapor database because that is the one
that our configuration uses. We can give us a name. It doesn't really matter. It database and we click connect.
Okay. So here we can see that the connection is
live because it has a little green dot. If we click on songs and just right
click and then do Select Top 1000. This runs this query of select
ID and select title from songs. And we have nothing here because there's nothing in our database,
but we can see fluent migrations if we right click this one
and click Select Top 1000. This shows us that the migration that we ran is called Createsongs,
and then this is the first one that ran and this is the day
and time that we ran this. So this is like our get commits. It has a record of all the different things that have happened,
all the different migrations. And we can see here. We don't have any data in our
database at the moment. So let's create a new route that allows us to add data because what we have so
far is just allowing us to get data. Okay. So we stopped the project and we
want to make a new route. So we need to make a new function. We are going to make
a function called Create, so our function takes in a request. All of them will take in a request because that is the request coming from Postman
and the request coming from the iOS app. And then I haven't really
talked about this. It's called Eventloop Future. So this has to do with what is
called futures and promises. So it's all about asynchronous code. And note these tutorials are before Vapor has integrated Async await,
which is the brand new Swift I believe, 5.5 functionality that is in iOS,
and we will be using that with the iOS app, but it has not been merged
into Vapor's framework just yet. And then right here we
will return Http status. So the status is like when we
looked at postman just moved. This is the status. So $200 means something good happened. So we will return a status whether or not we were able to successfully
create a song. So let's write the code for this. Okay. What we wrote here is letsongals
tryrequest content decode. So when we have this request, we are going to be decoding
the content into a type of song self. So that's like when we do JSON decoding in iOS, this is pretty much the exact same
thing, and we will decode our song type and we save it into a
variable called song. And then we will save
that to our database. So that is using song save. And we can use this because our model song
is a part of fluent. So it has these functions inherently
that it can use to talk to the database. So we save our song to our database. And then if that goes well,
we transform it to okay. Meaning everything went well in the response from this entire request
is going to be okay if it worked. So let's go ahead and run this. But actually before we run it,
we have to add this to this boot function because we have added the get for the
index, but we need to add the create. So instead of get, we are using Post
and these are different http methods. Get is usually for getting data,
and post is usually for sending data. So we do songs post, and that allows us to make a post
request rather than a get request. Let's go ahead and run this
and bring postman back up. We have a lot of windows so you can
see when we did the Get request. The request type is get when we
hit the songs API or the route. But if we use a post request, we change this from Get to post
and then you can see the request. The URL is the exact same. So that was me testing one. But here what we need to do first is
so you won't see this initially. It might be on like params. Go to the body tab here and make sure you click on Raw and then select JSON
for the type of this body, and we will use a title because that is
one of the properties of our song model. We have an ID and a title, but we
don't need to create the ID ourselves. The database will do that for us. Okay, this is the vapor
like pop up that comes up. Just click.
Okay. And then our Vapor API is now up and running in postman, let's add
a new song, add a song called Money, and if we click send, we get back a status of 200.
Okay. And you can see we made a post request
to the routethsongs, but we can actually see the data,
but we can if we now use our other route that we had for getting all of the songs,
so you can see it says it's a get request, not a post request,
and we don't have anything in the body because we're not sending
anything over to the API. We're just sending our
request to get back data. So if we click Send now we have data coming back so you can see the ID that was created,
which was that UUID. And then the title is Money. So that is the song that we just added. We used our API right here for a get
and a post request to the route songs, and now we have created new data
and we have read that data. We can also look at our Azure Data studio,
and we can query our database directly. So if we click run after we run our query,
we can see the same ID number, and then the title is Money because that's
the one item that we have in our database. So this allows us to look at our database directly rather than using our API
here to query the data itself. Okay.
And then that's pretty much it. We have created two different routes that allow us to read
data and create new data. So here I will actually add this is a get request
to the Songs route and this one and this one is the post request to the Songs route,
and we can see that here we use Get. So let's stop the project from running. And then down here where we had our we can see everything
where we had Docker running. If we click Control C that will stop the container completely,
you shouldn't just close this window. You should just cancels
it gracefully to stop it. And then if we look in Docker desktop,
we can see that it's not running anymore. Let's exit out of that. And then that's all we have
to do on the API side. We have created our table using migrations, and we learned that migrations
are like Git, but for our database we created two routes,
one to read data and one to write data, and we are writing and reading
a list of song titles. Lastly, we used Postman
and Azure Data Studio so we can look at the data in our database and test our
API without needing to write the iOS app. First, in this video, I'll show you how to create
a Swift UI app that uses our custom vapor back end API to read
and write data to a database. Hey, everyone, my name is Mikayla Karen. I'm a full time iOS developer,
and recently I've been learning Vapor. So I'm excited to share with you how you can use Vapor to make
your own API back end. Okay.
Now let's create the iOS application. So just go to Xcode
and do file new project. Then here make sure you select
iOS and then click on app and we will call Chris Ytvaporios app. We will change this. So it is in Swift UI and this is Xcode 13. So this is what it looks like. You don't have to select life cycle. I don't think anymore. So this is that and then click on next. But go ahead and find where you want
to save your project and click on create. Okay.
Our project is created. Let's go ahead and run it. We're just going to use
the simulator rather than my phone. So we have created our project and then
we built it for the first time. So we know everything's working. I'll keep that up and you
can turn on the preview. I don't use it that often,
but we can put it there. But let's go ahead and make
our file structure. So we will be using MVVM,
which is model view view model. So let's go ahead. Right click here and click on new group. So we have models. Come on. We have view models. I don't know why mine's doing that
MVV and then M models, V models. And I like to put these in alphabetical
order just because I think it's easier. So move that one up here and we will
also create one called utilities. And then I'll put that one down below.
Okay. First thing we are going to do is change the ContentView instead
of being called ContentView. We're going to call it song list. So we right click on that
and click on refactor and rename. You want to click the plus here. So it renames the comma itself and you can
see it's going to rename the file too. So now we have a song list. And then let's go ahead
and drop this into the views and just expand all these. And first thing we need to do is create our model because we have
to model our data somehow. So we do file new file. We want to make a Swift file. Okay, let's call this song or just song and create a structure for the song
that we made in our API project. We will make it conformed to
identifiable and quotable. So the codeable so we can decode
and encode everything and identifiable is because it has an ID and we will
be using a list in Swift UI. There we go. So that will go away because it now
conforms because we have an ID property. Okay, let's make our song list. Open up song list here. Maybe one day the preview will work. But let's go ahead and make the list. So we are repo in a navigation view, make our list. And then we need our data to be
represented somehow like an array. So let's go ahead and also
have a view model for this. So we right click View models. New file. Want to Swift file, and I will call it Song List view model. Make sure that is checked down here. Click create. Let's go ahead and create just an array in here. So we have just our songs array and it's
an empty array and it's a published value. So if we go back here to Song list, we have a review model. Is this ever going to build? Oh my goodness. Okay, we are making a list and we want to iterate through all the objects within
our Songs array inside of our view model. So let's type a four each loop and I will make this smaller inside of our list. We are going to have
just instead of a label. We're just going to use a button because we will make it so you
can tap on every row. I just have the text as the song's title. And then we set this to font three, and then the foreground color is label,
which is a systematic color. So when it switches from light mode
to dark mode, it'll automatically switch and we don't have to manually
say what the color should be, and we will add
a navigation title as well. So we don't see any data because
we don't have any in the preview. Okay, so let's fetch
some data from our API. So in our view model, we will
need a way to get that data. Let's create a function called fetchsongs. Okay, so we will be using async await
for this, which is only available. So right here you can see concurrency
is only available in 150 or newer. So let's open the side
panel back up again. So I did that with command
zero and click on our project. And let's set the minimum target to 150. So we don't get that warning 150 right there
when that target is selected, and then make sure it says
150 for that one as well. You can see these two different targets, rolled the target in the project,
and there we go. So reset that and save it.
Close this. And if we build project again,
this little warning should go away. Okay, our project says build succeeded.
So we're good. And then that warning
doesn't come up anymore. To fetch our data. We first need to say what
URL it's coming from. So in our API we had URL was 1270 one,
but that's not really a real address. That is just the name of our computer,
which is also called localhost. But everybody's computer is called local
host, so we can't just hit that URL. So we'll solve that in just a second.
But before we do that, let's first create the code that will
actually go about fetching the data. So what we're going to be doing is
making a single ten called Http client. So go ahead and click on utilities
and create another new file. I'll make it a Swift file, write http client and hit. Okay, so this is what makes it a single ten. We don't have an initializer so you can't
initialize this object, but we can access it through this
shared property that we created. And then that is what accesses all of
the different properties and functions. So we want to fetch data. Let's go ahead and make
a function called fetch. As.
You can see, this is a generic function. So what we are going to be doing is making our http client generic,
so that really it could be adopted. You could copy this code and drop it into any project that you
have that makes http request. So the generic means
this is the value or the property that we're using called tea,
and it just needs to conform to codeable. And then what we return is our object,
which is an array of T objects. And for us it will be the song model. But again, you could drop this code
into any project and it would work. So how do we do this with async await? We first need to make the request
for fetching data. So this is the async version
of the shared data task. And when we use async await because this is an asynchronous function,
we have to have the await in front of it. Meaning wait for this to finish. And don't worry about this. It'll go away in just a minute. And here we are going to check that our
response first is an http URL response, and then this property or type has
a property on it called status code. And we want to make sure that's 200 because 200 means it was a good response,
and then we're going to throw an error. So first let's make the kinds
of errors that we'll be throwing. Put that right up here. These are different types of errors. So if we get to this point, we know that it was a bad response back if
it was anything other than 200, and now we need to Xcode the data
and put it into whatever type T is here. We have an object and we are going
to decode it using a JSON decoder, and it will be of type T,
and it's an array of those objects, and we are decoding the data
that is right there. And then if that doesn't work,
we will throw the error decoding data, and then we will return the object. That's what's returned out of this. So let's go ahead and build the project. Okay. If we build the project,
it succeeds right here. I had accidentally Typed data, not data,
or I Typed data task rather than data. So this is the asynchronous one and you can tell when you command click
on it and it goes to QuickHelp. You can see it has the word async in it, and it tells you it retrieves content
from a URL and delivers the data asynchronously that's
exactly what we want. So how do we go about using this function? We're going to be using it inside of our
song list ViewModel to fetch objects. So the first thing we need
to do is define our URL. So to do that, we are going
to create a constant file. So this URL won't really ever change. It is going to change when we're
doing the development work. But the actual URL that ends up being deployed in the final video,
it won't change. So let's go ahead and make
a constants file for that. So we create a new file
in our utilities folder. Create constants Swift,
and I like to put these in alphabetical order sometimes,
and we will create an enum, so it will look like this. We will fill in the rest
of this in just a minute. But for now, this is all we need. And let's also create an enum. So this is our endpoint. We only have one because we're only reading and writing
to that one songs endpoint. So we just call the songs and in doing it
like this, making enum with a static variable, which is actually a constant,
but there's still a variable. But the static variable allows us
to use this in a type safe way. So in case we ever accidentally wrote song
instead of songs, every time you type this endpoint, you basically won't be able
to do that with this endpoint or with this object because it will
be completely Typesafe. So let's go here and see how we do that. Let's create a URL. So this is just a string. So we have constants baseurl, so that's the URL we will fill
in right here and then plus endpoints endpoints songs, and we
will put a slash at this. So it'll be something like it will look something like this actually. So this is what is called the base URL, and then the endpoints are
whatever as after this. So if we go here. Yeah, that's fine. This is just telling us that we
haven't used this variable yet. So it wants to put an underscore,
but we will use it. This is a string too. So let's create the actual
URL object itself here. We have a guard because we want to make
sure that this URL is for sure a value, and that it doesn't throw, because if you
look at the signature of this function, that's just a structure actual function. Can we go to that? Open up the developer, Doc.
Oh yeah. That's just initializing in the URL,
but it does throw right here. It creates a URL instance, but with the
question Mark means it might be optional. So that means if it can't create a URL,
then the value would be nil here. If there's any illegal characters,
it would be nil. So using guard, we want to make sure
that this definitely has a value in it. Otherwise we want to throw
the error bad URL. So let's use this. We will create a variable
called Song response. So this is how we use our
async await function. So here's our Singleton http client shared, and we are using this fetch
function, and we're passing in the URL that we created right here. And what's returned from this
is an array right here. It said an array of T, but with this we are specifically saying
it'll be an array of song objects. And once we have all of our song objects, we want to set that into our
variable right there. And we have to do this on the main queue because that will update
this variable with Swift UI. It will know to refresh the view. So this is something that must
happen on the main thread and then that's it. So we can go ahead and build the project
and everything should build. But we can't actually reach our data just yet from our API because for one,
we don't have a URL filled in. Okay, we have built succeeded,
and we don't have a real URL here. Let's first CD into our API project. This is what mine is called it's the Ytvaper API, and this
is just the branch name. If you saw this before, I broke up all of the files of the project
into different branches so you can look at a specific branch to see all the code
for that specific lesson rather than having multiple
repositories for each lesson. And we need to run our database. We need to run our database with Docker. Okay, the first thing we need to do is we
need to run our database with Docker, and we need to run our API so
we could write Docker, compose up DB and run it from the command line. Or the other way is we can use
the Docker desktop application. So if you open up the container that we have here and that is the database,
you can just hit play or start. Okay, we have it up and running. And if we just click on this row, we'll see the same kind of output
that came in when we were on terminal. So this is just another way
to run the Docker container. So we have our database running right now. But we need to run our project. We could go ahead and run it via Xcode, or we can just run it
on the terminal as well. So if you type the command vapor run and hit enter that is building
our project and running it. So that will build our project and run it all in the terminal rather than running it
in Xcode, and you can see it's going and fetching all of those
different packages that we have. And this is the first time I'm
running it through terminal. So this may take a while. Here it goes, here it goes. And it says already in use. Okay, mine came up with that error again. That address is already in use. So we need to fix that
by running the command Lsofi 80 80, and we can
see postman is using that. But that one killed postman last time. So let's use this PID. So kill nine. Then the PID number hit enter and then else off and that's gone now. So let's type vapor, run enter and then now the project should try to build again and run all in the terminal
rather than running it from Xcode. We wait again.
Okay. Cool.
So everything worked. But again we cannot hit 127 one at because
everybody's computer will say that. So it's literally not possible. That's just what the computer is called. So the URL we are going to use, we need to open up a new terminal
tab so we can't use this URL. But we do need this actually up and running because this is our API, and
basically it's like it's on right now. But we need something to bridge the gap
from the iOS app to go to our API. So for that, we need a new tool that I forgot to download
initially called in Grok. So it says it right here public URLs for exposing your web URL or whatever
that said for exposing your computer. So go ahead and just click
on this download button. You don't actually need to sign up for an account, even though
it does tell you to do that. Just click on download and it will
download a file called Ingrock for you. So I don't follow this. I tried to and it did not work
on my computer to unzip it. And again, we don't have
to create an account either. We can kind of just use it immediately.
Okay. So once we have that downloaded,
open up our new tab and terminal. Just typing CD and hitting enter
will give us our root directory, and mine went to the downloads folder. So when we type LS, we see our
file called Ingrock sitting here. So to use it, we do ingrock. We type ingrock http.
80. 80. So what we're going to be doing is
like opening a Port on our computer. And this is the Port that we want to use,
which is the one that our API is using. So when we hit enter on this,
we can see it brings up this window here is like this word forwarding
and this URL right here. So make sure to grab the one that's https and you can see this URL will
reroute to localhost 80. 80, which 1270 one is
the same thing as localhost. So for the URL in our constants file, we want to put the same thing and don't
forget to have the slash right there. So when we hit our API and it says it
right there too, when we hit the API, we'll use this URL,
which that will go to Ingrock. And Ingrock understands that then that will forward to our personal
computer that says localhost 8080. So let's go ahead and run the iOS project.
Okay. And we can see nothing happened. And the reason for that is because
we never call our function. So you have to call the function
for the function to actually work. And you see this output right
here about the navigation title. This is some just weird
thing with Swift UI. It has all these constraint
errors for this title. So if you remove that, that will go away. But again, we need to call our function Fetch Songs for it to actually
go out and fetch new songs. If we go back to our song list,
we want to add a modifier to our navigation view. So I'm going to close this
so you can either grab it right here and swipe it away or click on this button,
and then it hides and shows the canvas. So we want to add a modifier
to the navigation view called on Appear. And from here we want to call our function
in the view model called Fetch Songs. So we can't just call it
directly like this because we will get an error. First telling us we need this is an ASIC function, and this function
doesn't support concurrency. So it's the on a peer function
that doesn't inherently use concurrency. Also, there's like four errors on this. And then the other thing is telling us this can throw and it's
not marked with a try. So if we look at the function here for one, it is async and two
the function actually throws. So right here it would throw an error. So when we use throws, we need the keyword try in front of it,
and then we have the other keyword await, which goes hand in hand
with using async await. That's the pattern for concurrency code. And then this is the error that's again
telling us that the function does not inherently use asynchronous code
like we have it written right here. So to fix that we can put this inside of a new task, and this task is
asynchronous. So if we look at the quick help for here
a unit of Asynchronous work. So now if we build a project, this should build and be fine with command
B and there we have build succeeded. But this function can still throw an error, and we kind of don't
handle that immediately right here. So let's wrap this in a due catch block. Okay. We have wrapped our ViewModel fetchsongs
inside of this do catch block. So in case this ends up throwing an error instead of doing nothing with it,
we will print out the error. So this is just printing the word error. And then this is the error that's actually
caught that's thrown from this function. And then I use emojis in my code a lot. Just because it helps you see everything
in the debug log down here, especially when you have all this
constraint kind of stuff, you can just throw in an emoji
and then see it very quickly. Now that we have that we still have this running and you can see it only
says an hour of 54 minutes left. So after that time is over,
this URL doesn't work anymore. So that's why we can
use it for development. But that's not what you
end up using long term. Let's run our project now and we should see
it actually going and fetching the data from our database, which is
just that single row of data. I believe the song says money. Okay, the project built
and we can see on the right. We have our one thing
in the database that says Money. So we have two outputs down here. The first one is in Grok. We can see that we did a get request to our songs endpoint,
and it came back with 200. Okay. And if we look at the vapor output when we
did vapor run, we can see almost the same thing that we did a get request
to the slashsongs endpoint, and that's about it. We don't actually see that it said 200, but we know that it works because the data shows up here and we didn't
get an error thrown. So now that we can see data,
let's add new data to our database, but through the iOS app,
let's stop the project and we can leave this running right now. So we want to add a new project by tapping a button in the top right
in the navigation bar. So let's add that with a toolbar. So we add this to the list property here rather than to the navigation view. Okay, so right here we want
to have a label called Adsong. So if we look at this actually, we could try to look it in the canvas,
see if it works. The canvas doesn't work. Usually it struggles a little bit,
as opposed to running the app all the time in the simulator, because I do UIKit
way more than I do Swift UI still. And so I'm still used to just
always running it in the simulator. My computer is a little slow, so sometimes work sometimes doesn't. But we should have a button in the top.
Right. That is just the system
image of Plus Circle. So system image that is from here. So there's an app called SF Symbols,
and you can go and download that. I will have the link in the description
below, and we would use the one I Typed Plus here and you have all of these
different images that you can use. So if you right, click it and then I don't know why we can't copy,
but the image name is whatever's down here I'm using plus CircleCI, which is
somewhere around here. I found it earlier, but
you can use one of these as system images in your code, and then these are just
a ton of ones that Apple just gives you. So we are using plus circle,
and I used a label rather than just an image with a system
name because this is more accessible. So if somebody was using voiceover, this would say add song instead
of just the words plus circle on it. And then you can see the database was hit
a couple more times when we were running this preview, but when we click the plus,
we want a modal sheet to come up. So let's build that part of it. So we are going to use the dot sheet modifier and put that
on the navigation view. So let's put it right
here before on appear. So an action sheet is what comes up where
it has those options at the bottom. But we just want to use a normal sheet. I'm not sure why my Xcode
doesn't remember what that is. We are going to be using this one that says binding identifiable
rather than just presented, which is just a boolean
because the sheet that we're using we will use the sheet for both
adding and updating values. So we need a variable that we'll use here and we do want to do something
for the on dismiss. So first before you fill any of this out,
what variable are we going to bind to? We are going to make a new enemy type. So let's click plus and then new file and utilities and it will be
a switch file called modal type. Okay, we have created a new enum
that conforms to identifiable. So in order to conform to identifiable,
it needs to have an ID. So here we are just creating a computer
property and switching on our self. Depending on if the case is add or update, we are just returning the ID
of a string just called add or update. Then our two cases are add and update and update has an associated
value of type song. We'll see about using that later. So for now we have our modal type. So inside of our view model
inside of song list, we need to have a variable
right here that we'll be using. So we create a new variable called modal,
and it's of type modal type and we will set it by default is nil because
no sheet will be showing. And then that's because we
haven't touched anything. Here we type modal because that's
the variable that we want to use. We will fill that in a little bit later and for content, I don't like
the way it looks right here. So we will take off just by default. I don't like what the autocomplete does,
so we will take off that part. This is the ContentView
part of that closure. I just didn't like the way the auto completes to look like that and what
we're doing right now. This doesn't work because we
need to fill something in. But what we are doing right now is we have our modal type, and then we are switching
on what that modal is whether Ad or update was selected and we want
to show a different view. So we haven't actually created that yet. So let's go ahead and do that. So let's create a new view
and it will be a Swift UI view. Let's call it Add Update song. Inside of this, we can do two things. We'll either be adding a new song
or we will be updating a song. So what will we be displaying? We are
going to just put a V stack on the screen that will have a text field property so we can type something and it
will have a button. So our Ad update song also needs its own view model that we will use to fill
in some of these things here. So let's create that view model. Now do command n on our view
models using a Swift file. Add updatesong ViewModel and our view model will conform to observable object because
it will be a view model. We'll wrap it in a property wrapper on the view, but the first thing
we need is a published property, and it will be an empty string. So that's what we will use
for a text field over here. Yes.
Okay. So we have our view model and it
has a property called song title. And then this is what's shown in the text field before we type anything
and it will just say song title on. This will add a little bit of styling,
so it doesn't look weird. So here at the text field style will be a rounded border, so that will literally
just put like a Gray border around it and then add some padding,
so it's not right up against the side. Let's fix this down here by just creating a new view model. But what do we want our button to say? So our button is either going to say add
or it's going to say update, depending on what this model is doing,
which will happen over here. Okay.
So let's first make our view model. So we will make the view model. It's going to have a couple
of properties on it, which will be a Uuid. So the song ID. This will have a song ID if it's
the update, meaning we're passing the song over to this view and it
won't have a song ID if we're adding a new song because we
haven't Typed anything in there yet. So we'll use the property VAR is updating to know whether or not this view is in the updating
state or if it's in the ad state. So when we type song ID
is not equal to Nil. Well, if the song ID is not nil,
then that means we are updating, so is updating would be true,
and then vice versa. And then I talked about over here. We don't know what we're going to write for the title of our button if it's
going to say add or update song. So we'll create another
computer property for that. So here we have. We're checking again if the song
ID is not equal to Nil. If it's not nil, then we
will say update song. We are using a ternary operator here. So then if this statement is false,
we'll say Add song. So UIButton title. We can then copy this
and put that in as text right here. Let's create a blank initializer, and we will also have an initializer. And then we have another
initializer with our current song. There you go. We have another initializer that says
that Intake is a current song, and we set the two properties
song ID and song title. But if we don't have a current song,
that means we're adding a new one. So right here it doesn't
take any properties. And this is fine to do, because every property we have in here
will have a default value of some sort. So this one is an empty string. That one would be nil. This one could be true or
false depending on that. And then again, this one is
one of those two values. So we want to add a new song. So let's create a function to do that the code for adding a song is going to look very similar to the code
that we just wrote for fetching a song, and we can actually go and probably
copy majority of this. So we can copy this part because
we'll be doing the same thing. We have to create our URL,
and then we will use the base URL from constants and we're
using the same song endpoint. But what we are doing is we are sending a song to the database to the API,
so we have to create that song property. So here what we're doing
is creating a new song. It does not have an ID yet because we don't assign one within the iOS app,
and then we're using the song title. So that is whatever ended up
being Typed into the text field. In the view. Now we need to actually make the URL. We need to make the URL request,
so we don't do that here. We do that inside of our http client. So we have to make a new function
because we're not fetching data. We are sending data. So we'll make a new
function called send data. We'll close that there so
we can see the whole line. So because we're using generics again, we have our type T,
which conforms to codable. There we go.
And then we are sending it to this URL. I cannot type object. The object that we're using is of type T and then Http method, which we'll look
into, and then that will be a string. So the first thing we do is we
have a couple Http methods. We have seen two so far. We saw get and then post. So let's define those up here. And again, this is all
to make it type safe. So in case we accidentally spell post
wrong, it won't break all of our code. So this is an enum with a raw value. So we've used post and get and we have not yet used put and delete,
but we will get to those. And then with the raw value of string, the
raw value will just be the case itself. We also need to add a few other things,
and this goes with the URL request. So we'll look at Chris a little bit later when we kind of see it more in postman
or I can open that now too. I already have that. So in postman when we did a create song,
when we added a new song, we didn't look at these headers,
but this is what we're doing right here. So that said Http header. We added by default. It adds a header called content type,
and we set it to applicationjson. So that's what these two
things are used for. And then that basically just tells the request what kind
of data are we sending over. Let's go down to send data
and make our URL request. So here we are making our request. And what we saw in postman. I forgot to add the actual name URL here. So what we added was this is
the application JSON part of the header. And then this tells you for header
field in the field was content type. So we see the same thing in postman the key in value, which is sort
of the same thing is content type. And then we have application JSON. Everything else are like defaults. So that's why it says
auto generated headers. So we've added that to what kind of data we're looking tell the request what
kind of data we're looking for. I'm just going to hit enter there
to make this a little shorter. And lastly, we have
to send the data along. So we want to send data to our server. So here we have an HTT body and we want to encode our data from our object,
and then we encode it as JSON. So that's why when we went to send data,
we had the body property, and then this is our JSON object with a
key of title and then the value of money. And right here we're telling it it's JSON. Now we actually need to make the request. So here we are doing the exact
same thing as we did at this part inside of the fetch function. So this is the same thing. Except we are passing a request
rather than passing a URL, and we put an underscore here because we
don't really care about the data that's coming back because we don't
actually have any data coming back. We are responding to the request
with just a response. So that's what this value is. And then for those of you who don't know
this is a tuple or tuple, I don't really know how it's pronounced,
and it allows you to kind of have two values that are returned out of a function
rather than just returning a single value. This is like a comma
separated list of values. So we have data and response.
And then again, we are checking the response that it is
of this type, and the status code is 200. Because if it's not 200, that means that
it didn't go okay or it didn't go well. And it failed. So now that we've created our Http client,
now let's go and use the client in our view model here. So what do we want to send?
We want to send the URL. You can see it says encoded and decoded by default, because right here we
say that T is of type codable. And if you click on this
and click Quick help. So command click on this. Codable is actually a type alias
for decodable and encoded. So we want to pass our object of song
an Http method that we want to use instead of just typing posts directly here
we will use our enum and use the post and raw value. So I'm going to hit enter
on this just to make it easier to read. And then that's all we have
to do to add a new song. So now we need to call
our addsong ibaction. But to do that, we can't just
call that directly in our button. We Xcode for now, which I might do. Actually, it's for the view model. So we need ViewModel add song. We cannot just call ViewModel add song. We could, but we're using
this for both add and update. So we kind of have to have something here
that decides are we adding or updating? And depending on that,
we perform the right ibaction. So inside of our view model, this is part of the view model.
There we go. That one error should go away. But we have had updates song. So we have adding a song. But yeah, we need to decide
are we adding or updating? So here we're calling this
and it's an async function. So we need to put it inside of a task. So how do we decide if
we're adding or updating? We created a property up here called is updating, and we can use that
in our if statement. And by default if you don't type
anything like equal equal true. By default it says if this is equal to true,
which is the best way when using boolean. So if we are updating, if that's true, we want to update the song,
but we haven't done anything here. So let's put a comment updatesong function else we want to call our function add song. And again, this is an async
thing that throws. So we actually need to put try a weight on top of it or before it,
and we want to handle what happens. So we want to put all of this
inside of a do catch block. If there were to be an error,
we want the function to throw and actually handle the error as opposed
to having it just sort of disappear. So here we do that. And then I added a completion handler
on here because after everything has been performed, so we go to right
before the end of our task. I want to call our completion handler, because when we call this function when we
add something new to our database, we want to wait until this action
completes before we do something else, like updating our view for updating
the new song in our list. So we have all of this here. So now we can call this inside of our UIButton. We have our add update action. So after we have done the action of adding or updating, we want to close
the view, which is one thing. So to do that, we first need
to add an environment variable, and this is how we can
dismiss this view after our song has been added. So once we do that, we can go back to song list, and now we can call
the right thing here. So when we're right here we want to show add update song here. Now we're presenting the same view,
except when we do add song, we give a view model with nothing inside
of it because there is not a song yet, whereas when we do update song,
we are using this variable of song to then pass to the view model so it knows what
song we are then going to update and we have this on dismiss code here
because at the point of after we presented the model,
type something in clicked on the button. The view is then dismissed and we want to then update our view here to show
the new song inside of that list. Let's do that with our function of fetch. Okay, so we have ran the function inside of our task block
because this is an Asynch await function or it's an asynchronous function and we
are calling ViewModel fetchsongs to fetch new songs after the sheet has dismissed
to get updates from our database. Then we put this inside of a new catch again to handle the error
if it comes back. So we built the app and ran it. So now we should be able to click on run. We should be able to type in new songs to add to our database and also read
all the songs from the database. Bad access. What is that about? So I'm going to clean the project and then build it again because I'm not sure
why that came up. So you clean your project
with commandshiftk. Okay, our project built and we can see that we have a new request here through
the songs endpoint because that's what runs when we loaded for the first
time in this dot on appear. Let's click on the plus and I did not connect it because you need to call the function
and not just print add song. Where did I do that?
Right here. So we want to type modal equals add because our sheet is bound to this modal
property, and when we click on add song, we want it to present the ad
sheet, not the update one. So let's stop it and run it again. We see a new request here for the songs
endpoint and we see a new one right here because that's what we did
when we got all the songs. Now we click on the plus here and we see our modal pop up with the text
field on the button. So what song do we want to add? Let's add the year by the Jonas Brothers. So if we type that and click on add, we saw the modal go away and we
saw our data update right here. You can see in our logs. We did get songs first. That wasn't the first thing.
Yeah, it was. We did get songs when the view
loaded for the very first time. We did a post to create our new song, and then we did get songs again so
that we can update our initial list and there we go. We have successfully created new
items and put that in the database. And also we have read data from the database
to view in our iOS app. So to stop everything, we can stop the project and to get out
of this, you type Control C to stop our Vapor API from running,
and then to stop in Grok from running. You type control C as well, and it'll
just go back to your downloads folder. That was a lot of code. What we did was create our folder
structure using MVVM model view model. We created network request using the new Async await syntax with Swift 5.5. We made
it generic so that we could drop this code into any other project that we
wanted to that uses network request. Lastly, we used in Grok to connect
our iOS app to our local vapor API. In this video I'll show you how to create two new routes in our Vapourapi to update
and delete data in our database. We will then call those two
routes from the Swift UI iOS app. Let's get started. We are going to make
the update and delete routes. So let's first make
a new function function. Oh my gosh. Okay, update. We will have a request that throws
like the rest of them. We'll make a function called update and it looks similar to the rest of them
and it will return an event loop future and it will return an Http
status similar to the request. So if it works will return.
Okay. Otherwise it will return
some kind of error. So the first thing we do when we get
an update, we are going to be sending data over from the iOS app from postman about
the song that we are updating. So we have to first decode the song similar to how we do it
with the post request. So I'll just copy this
because it's the same. And then I'll also add a comment. So this is what the route
is going to look like. It'll be a put request because that is
what it looks like when we do an update, that's the request type and what we do
when we do an update is we first look to see can we find the song
that we are going to update? And then we will update the data itself. What we do here is we first
query to find the song. So can we find the song that we give
it from postman or from our iOS app? And so we look for the
song based on its ID. And if we cannot find it,
we will then grow abort not found, which is a 404 error and unwrap
it's unwrap or meaning. Can we unwrap this value because
this returns an optional? If we look at find. If we click command,
click on that and then click quick help. We see it returns an optional. So if we cannot unwrap this so unwrap or we can't we will return
four or four not found. But if we can, we continue
and we will flat map this. So the first thing we do is take the value and basically set because
there's like a pretend value here. We set the value of the title
property equal to our new song title. So that is the song here. And then what we do is we return
the instance of updating it. So we take this new song
and update it on the database. And this update function really
is kind of just like saving it. So if we come and click on that, that
doesn't give us any useful information. But really what we're doing is updating
the value within the database for this one song, and if that all works out,
okay, we transform it to okay. And then let's write
the delete function as well. So this is our delete. And actually I messed
up the comment up here. It won't besongsomething it'll just be
the slashsons route. And then this is what this route will look like. So for delete, it will be
a delete kind of request. And we'll see that in postman in a minute
and the route will look likesongid. And that's that UU ID that we
have for our song model. So what we do with the delete function is
we first use our song model, and we're using fluent to do this finding
and basically querying our database. So we do find and then we get the request
parameters and we called it song ID. So that's what this part will look like. So it'll besongs. And then that long ID
number from our database. We find it in this database. And then again, we unwrap
the value if we have it. Otherwise we're going
to throw a four or four. And what we do is a flat map. And we dot delete for this database. And we're deleting that song
from the database. And then this is again a fluent function
because we're using our song fluent model. And if everything goes well with that,
we go ahead and transform it to OK. So let's go ahead and run this. But first we have to make
sure the database is running. So I'll use the Docker desktop application rather than the terminal. So that was my testing one. Here is our actual API that's running. And then that is the database. So let's go ahead and just
click run or start. And then this is now starting up this
container and we can click on this row to see the terminal information as if we
ran it from the terminal down here and it says it's starting and we can check if
this is working using Azure Data Studio. Let's go ahead and open that. So we have Azure Data Studio. We're on the welcome page because we've
already connected to this database. We can see it in the
connections over here. But if we go and just double click on it. It brings up that list as if we were
connecting to a new server, but we have all the information
here because it saved it. I didn't check remember password,
so it doesn't have it Typed. It doesn't have it auto filled in here. But if I go ahead and type it and click on connect. So now we have a green dot here,
meaning we're actually connected to it. We should see something. There we go.
Took forever to load. Okay, so we have a green dot, so we're
connected and I double clicked on this. Usually it shows a list down here. I think mine still might be Loading, but we can go ahead and still click
on the Songs table and click Select Top 1000, but we know because we
can connect to the database. We know it's already up
and running with Docker. Otherwise we wouldn't have
been able to connect at all. So we see our two songs
that we have in here. Let's go ahead and now run our API, so we just go ahead and click the run
button within Xcode, and we will need to grab a postman
as well to make our request. This build failed because we did not call the function because you have to call the
function to be able to use the function. So we added our two routes
update and delete down here. But what we didn't do up at the top is actually put them in our boot
function so that we can use them. So there's another operation called Songstot put, and this is using our
routesongs, so it groups that route right here and then we have get and posts,
but we also need to add the put, and then lastly, with the update,
it looks a little different. There we go.
Okay. I think those probably should be syntax highlighted, but my computer just doesn't
know how to do that, but we have our Songstock group,
and we are grouping anything that has this ID on it, and we're going
to call the delete function. So what that is it will look like Song ID. And then this is what that song ID is so that this string is the same between these
two and what it looks like when we actually use it within postman,
it will look something like this is we have our URL, the route songs, and then
this is the ID that's in our database. So that is finally calling
our two functions. So we are calling to use Song delete, and we want to use our delete
function that we have down there. And then when we do a put request,
we use the update function right here. So we should be able to run this now
and that should go away. We still failed. Oh, I spelled parameters wrong. Okay, now let's run it again. This dialog comes up, click on. Okay, and our server is starting. So that is good. So let's test our update function first. So I have all these tabs because I've been
adding them and trying everything out. But we go ahead and you can click on a new tab or just use the one
that you've been using. So we need to change our request type from Get or post and change it to put
because we're doing an update. So we have our URL right here. We have our songs route,
and then we click on the body properties, and we want to change this to Raw
and then change the type to JSON. So we type out our JSON model,
which is just the title and the ID. So let's update one of these values so we will need the ID and we'll need
the title that we want to change it to. So if we click on this row and just click command C for copy, and we fill
that in right here for the ID property. And then right now it says year 3000,
but we can change it to anything else. So let's change this from year 3000 to two
year thousand and 21, which is almost over. So let's change it to year 2022. So that's in the future or if you're watching this in the future,
it's now in the past. So if we change this,
let's go ahead and send our request. Okay. We sent our request and it
gives us back status 200. Okay, so that's good. That means we did the right thing
and everything worked because right here we tell it to transform everything
to okay, if everything else was good and we can confirm that this worked
by going down to Azure Data Studio. So we click on Run and we see
our data down here for this ID. We change the title to year 2022
instead of being year 3000. So we know our update function worked. So now let's test our delete function. So we go back to Postman
and I have a new tab called Delete. But all this is doing is really
just changing the type to Delete. But for this one,
we do not need a body property. So if you're using the same one,
go ahead and select none here or just make a new tab and it resets
sort of everything. So this is an old ID I was using. But let's go ahead
and use one of these ID. So let's say we don't
want your 2022 anymore. So copy the ID, go back into Postman and just paste
it into this URL and let's hit send. We got back 200. Okay, so that's good. And we see in Vapor, there was our Put request, and here is
our delete, and we know how we coded it. If we got back 200, that means everything
was good and it was able to delete that. So let's go back to our Vapor database. Click on Run again. The only song that's left in our database is the money song, and the year
2022 is now completely gone. So that part works. We have our API done. So we have our two new requests for update
and delete, and then we have verified that using Postman and Azure Data Studio
to check everything is working. Now let's go and write this
functionality in the iOS app. Let's make the iOS app. So first we want to update a song. So to do that, let's first run the project to see what it
looks like, and we don't have anything because we need to start in Grok,
and that's not from our iOS app. That is from the API side. So we can run in graph in another tab or I'll just open it into a
different window over here. We are running the API on the left and we need to run in Grock, which I
run from the downloads folder. So CD just goes back to our root directory
and then I want to go to downloads inside of here.
We have ingrock, so I do ingrock 8080, which starts up in Grok
and we need to take this URL. Copy that and put it
into our constants file. Okay, let's paste this here because we're still committing this. It looks like we're changing it,
which is fine, but we need that URL there and we need to stop this
from running and run it again. We should see our two values
that we saw that we saved earlier. I ran the project, but I forgot to add a slash onto it because
you can see the URL right here. There's no slash before we
have the slashsongs route. So I need to stop this and run it again. We ran our project. We can see we do one get request and it turns back a 200 and we see
it in our terminal over here. So we know we are reading something, but when we want to update a song,
we had add or update song view and this is presented if we
look at our song list here. This is presented when we tap on it. So actually right now
it'll just say selected. So if we go and just click tap on money, it just says selected,
which is not what we want. Now we want to show this and update it,
but we want to show not the add modal. We want to show the update modal. We did a lot of that set
up in the last video. So let's change this. We want to change the modal
property from add. Modal equals update, and this will change Chris
property that our sheet is viewing here. This was the on dismiss it's right here.
We do a switch. If it is update, we present the add updatesong view
and we give it the current song. So let's go and run this
and see if our view pops up. I think that's what
autocomplete is telling me. Modal needs a song. So we see the song right here because we have to tell the next modal that's going
to come up what song we want to update, and then that is this song
for whichever one is actually selected. We run this and we see in the simulator. If we tap on money,
we see almost everything is working. It fills in the word money, and we have update song here and we see it
says update rather than saying add because it's presenting the right type of modal
for which state that we want to use. But we need to write our update function. So let's go into the view model of Add
updatesong, and we already wrote it here commented out because we knew we were
going to put the update song function here, but we haven't actually
made the function yet. So let's go and make it. Let's just put it right above here. Technically, personally,
what I would do is move this one up, move this one up here. So you would have the add update action would be called and below it,
the two functions that are called inside of it would be below,
but it doesn't really matter what order. Let's type our function. Okay, this is our update method. Okay. Our update song method looks like this. We are creating our URL
similar to how we did in the one up here. And then we're doing the exact same thing
right here as we are making our URL into an URL object,
and then we have our song to update. So this would be the song right here. So we give it the song ID,
which is passed over from the last view. When we are passing where did it go? Song list right here. When we pass Chris song over,
it has an ID and it has a song title. So here's the song ID that gets passed over and then song title whatever is
Typed into that song title text box. And we can see that if we go
to the actual add song view, it's the song title right there. So we take that we make
it into a song object. And we already have created
this send data function. So we know if we look at our client send data, it is using whichever
method that we specify. So back there we saw we did the put method, and here we're just passing
the JSON over, which is what we want. So we already wrote
the ibaction that we have. All we had to do is implement
this function in a different way. So we implemented this in almost the exact
same way as we do an ad right here. But instead of using a post and creating
a new song, we use a put method. So let's try that. Now what is this complaining about? Again, we need to Mark this with try await because this function uses
the async await pattern, and that should go away. So it uses async await. So we need to tell this
function to use it in that way. Let's go ahead and rerun this. So we have the song called Money. What is a different song title? Let's change this completely to let's change it to how you like that because you can see I like the
was that the band? Yeah, black Pink, and they've been
stuck in my head for a while. So we'll change it to this. And if we click update song, we see it updates and how you like that is now here rather than the song Money
and we can even further conform. Confirm that if we go to Azure Data Studio, which I closed,
so we have to open it again and check the database to see if it says how you like that instead of saying Money
as the one record that is in there. So I'll go and connect to it and I didn't click remember password,
so I have to type it again. Click on Connect. So once we load our database, we see the one record and the only
thing that's here is how you like that. So if we go to add a new song,
just click on the add button and type in a new song, we'll call this
one Money and click on Add song. Now we see both of them here. So if we go back to Azure Data Studio
and click on Run again or I might just close this and open it
again because mine is having issues. Now we have both of our songs
and they have two different IDs. So we know both of those are working and we know the read function works
because we see them in the list. So that's all we had
to do to update the song. We just had to add this one function
because all of the work that we did in that last lesson
set everything up for this one. So let's go now and create
the delete function. So let's stop the app and I'll
just move this out of the way. We need to go to the song list and we are going to add a delete
modifier on this for each loop. So first let's actually write the function and we will write that
within our view model. So let's open the song list ViewModel. And instead of fetch songs,
let's make a new function for deleting. So what we are going to do is use
the delete function where it looks like right here on delete and then perform
something that takes in an index set. So ours will be ViewModel delete. But let's actually write that. Okay.
We have almost written everything. So we have offsets and that's part of an
index set or the type of it is index set. And if we click on the quick help here, it shows us that this is a range,
because really, you can kind of delete multiple things at once
when you use index sets. So what we're doing here. Is iterating through all of the index sets? And actually this
needs to go inside of here. Now we are going to iterate through every index set and delete each one in case there are
multiple how we are going to code it is there won't be multiple at once, but this
gives you the ability to do it that way. And then you can see we actually need to make a new function for deleting
because we can't use let me delete those. We can't use the ones that we've already
written because we have written fetch and we have written send data,
and neither of those do a delete. So we do need to make that new function within our http client
so we can use it here. And then this function delete
gets used inside of song list. Actually, it's ViewModel, and we don't actually need the app or
the parentheses just because we're passing this function itself and it has the same
signature that this was expecting. So we don't need to type
the little extra characters there. But we do still need to make our delete
function within our http client. So here is our delete function. We make a new URL request object,
we make a new URL request object, and we set the method to delete,
and we define those methods up above. We use the new URL session shared
data instead of data task. This is the new async one and we give it a request and we are getting
back data in response. But how we have it coded,
we're not actually getting back data because we kind of don't
care what we get back. We just want back a response. So we're putting an underscore here
and then this is a tuple or a tuple. I don't really know how it's pronounced. So that's the object
that we're getting back. So we care about the response. So we have an underscore for the data, and then we take the response and try
to cast it as an http URL response. And if that all works,
we see if the status quo is equal to 200, and then if that is all good,
then this function is like over. Otherwise it would throw
an error that says bad response. So if we go back to our song list view model and try to use this,
we should now get autocomplete. Here we go delete at ID. So we have the ID that we defined above
for the song ID, and then the URL is the URL that we defined here. So here this is putting
everything all together. Otherwise we could have made first a URL string variable and then
added this together. But just in this part I decided to put it all on one line,
so we have that and that should be it. And we can see in our song list on delete. When that action happens, we are performing our delete
function inside of this View model. So it's calling here. So let's go ahead and run the project
and bring over Azure Data Studio. Okay. We have our two songs here
and let's just add a third. Just so we have kind of a few. They have a song called Today. So I'll click on Add song. And so now we have our three songs,
but say we don't like how you like that. So if we do the swipe action of Swiping left, that automatically comes because we
define this on delete action here. So that just works automatically. So we have slide here tells us delete,
you click on delete and it's still there. Oh, I know why it's there,
but I believe if we check our database. Okay.
So it has gone from our database, but it's still in the app that happened
because we did go about deleting it from the database, and we saw
that that happens right here. So we know our Http client is working. What we did not do is inside
of this whole delete function. We use offsets and deleted that. So we did this functionality all worked. But what we did not do was delete
it from our array of songs. So our View model has all of our songs
here, and we didn't delete it out of this. So we need to just call songs that remove an offset and pass in
the variable offsets. So let's stop this. So basically the array
was still in memory. So that's why it came back,
even though in reality it wasn't there. Let's try to run this
again and see if it works. So if we go to add a new song, there's a song called Kill This Love. So if we add this song,
so now we have our three songs here. But let's say we want to delete stay there we go now. That is deleted. And what we should see here is
just Money and Kill Chris Love. So I'm going to close this tab and open it again because I think
mine is having an issue. And there we go.
We just see these two songs. So there are the UIDs and the two songs
that we have and that we see in our app, and that is it. We are able to successfully update
these songs and delete them. And in total, we can do all four options of crud of create, read,
update and delete. And there we go. We have made two new routes to update and delete data with our Vapor API,
and we use those routes in our iOS app. Hey, everyone, my name is Mikaela Karen. I am a full time iOS developer,
and recently I've been learning Vapor, so I'm excited to share with you how we
have finished building our Vapor API and how we can take that and actually
deploy it to the real world so anybody can use it and how we can
use it from our iOS app. Let's get started first thing we
need to do is install Heroku. So we are going to use Homebrew
like we have for vapor. So just type brew install Heroku and then just hit enter. Mine is already installed. But every time you try to install
something new, it will try to update Homebrew if it hasn't
been updated in a while. So mine is fine. And then it's telling me a warning that it's already installed and up to date
and then just tells you that if you want to reinstall it, that's the command
you write, but we have that installed. So that's good. And second thing we need
to do is make a new account. So let's do that right now. So go to sign up Heroku. Com and we're just taken to the sign
up page and let's type in my name, type in your email. You don't really need a company name role. I'm a professional developer, so we'll
put that I don't think it really matters. And then primary country. I'm in the United States and primary development language is I use another
language because Swift sadly isn't here. So just check.
I'm not a robot unless you are. Yes, I spelled everything right.
Okay. Click create free account,
and now I need to go check that the address is correct
and you'll get an email about that. Okay.
So the email that you're going to get will just direct you to this link
to actually input a password. So type in whatever password you want. Make sure it meets those minimums
and click set password and login. Then we can close the other window here.
Okay. It tells me my new account is all set up,
so we'll just click to proceed page not found.
Okay. I think it did this to me when
I tried it the first time too. So if we just go to Heroku. Com. Yeah, it looks like we're good
because it let me sign in. It has my name, has my email
and we're fine. Okay.
Wants us to accept terms of service. I click accept, and then we're
brought to this main page. So what we are going to do, we are going to deploy our API to Heroku
and we do part of that using the GUI. So the graphical user interface. So that's just the website and then part of it we are going to do from the command
line for our project here. So the first thing we are going to do,
let's set up our command line. So what we want to do after we've
installed it correctly, I click command K all the time
and that's what clears the terminal. We want to type Heroku login, and what this will do, we'll press any key to open up
a browser or login or Q to exit. So I'll just press the space bar and then we see it automatically opens my browser and tells me,
hey, do you want to log in? So I'll just click on login. We will do that later. We don't need that right now, and it says we're good. So it says we can close this page
and return back to your CLI. So CLI is command line interface. If you didn't know that just
means your terminal. And then we see right here. This is it tried to open a web
browser in case it didn't work. It says logging in and done
because we did it. And then we are logged in as. And then that's my email and you can verify this with Heroku author. This is like just part of a plugin
that I have on my computer. So you pretty much won't see that unless
you have the exact same plugin. Click Heroku off.
Who am I? We just see my email come up because that's just the email
that we're signed in as. Let's create our app within
Heroku on the web now. So let's just click on this button here that says Create new app, or you could
click from here for Create new app. But this is already here
because it's the welcome page. So what do we want to call this? Let's call it CWC. Must start with a lower case. That's interesting. So that popped up because I have CWC automatically as like a keyboard shortcut
to show like code with Chris because I type it so often, but we'll
just keep it like this. Cwc vaporapi. I'm in the United States and then we'll just click on Create app
and then this is the main page. So part of this, we won't use a pipeline for this right now and then
the deployment method. We're just going to use
the Heroku Git CLI. And then here it gives us
the instructions deploy using Heroku get. We have done this stuff already. And right now here we are. We are in our project directory. So that's why it says ytvaporapi on mine. And that's also part of a plugin
that I have downloaded and it wants us to CD into our
project and type get in it. But we don't need to do
that because our project is already uses Git. So the only thing we need to do is
actually copy this, which says Heroku, which is part of the
Heroku command from what we downloaded. And then we're using Git. We're adding a remote and it's going to be called CWC Vapor API, which is what
we call our entire application. So we just copy that and paste it.
Here. We hit enter and it shows us set
get Remote Heroku to that link. And what that really means
is we added a new remote. So if we type get remote V,
I have two remotes here. So here is the origin
remote that I called it. And then this is the one on GitHub
where you can find all of the code. But then we added a new
remote called it Heroku. And then this is the link for it. So before we deploy this application, we want to set what is called a build
pack before we go and deploy it. So a build pack is really just a script
that runs before the app is deployed. So what we want to do is
type Heroku build pack. Set Vapor vapor because this
is a vapor application. We want everything that comes with the dependencies of Vapor to install
before everything is deployed. So we just type this command hit enter. Okay, after we hit enter,
we can see it says Build pack is set. Next release for our app,
we'll use Vapor Vapor and then run this to push the new release
using this build pack. So that is the command we're going to end
up using and you can see it's just Git push Heroku main, which is the same as if
you are pushing to GitHub where you would write like Origin main or
Origin feature branch. It's the exact same thing. But we do have a couple of things
we need to set up 1st 1st. So we need to tell it what
Swift version we want to use. So we can do that with this command of Echo five, two, one,
and then this little carrot. It's a great advanced sign Swift version. So what this will do? It will make a new file
called Swift version. And inside of that file, it will just
have these words five, two, one. So if we hit enter on that, you see this old lightning Bolt come up
on mine because of the extension that I have, and it just means
that we've changed something. So when you type get status and hit enter, you can see that we've added
a new file called Swift version. I don't actually know what Cat stands for. Honestly, I'd have to look it up. But what we do here is this is just
printing out the contents of this file. So when I type Cat Swift version, we just see that it's a file
and the Swift version is five two, one. That's all it is.
That's what's inside of that file itself. So we have that.
But we have to add one more thing. Heroku uses a thing called a Proc file, and it just tells the app
what it needs to look for. So we write that with this command. So Echo again, and then
all of this comes with it. And it's just telling it where
to put the file and what Port. And then it's creating. All of this will be inside of a new
file just called Proc file. So if we hit enter, it does that. And then if we type get status,
we can see our two new files that we have. So we want to commit these files because they have to be there when
we deploy everything. So let's type get add
and that will add our two files. So if we hit enter
and type get status again, we can see it changed from untracked
files to changes to be committed. And let's go ahead and commit these. So we'll just type get commitment and we'll make a message,
add Heroku build files, and now we can go ahead and deploy it. So we will type get push. Heroku and I have not tested this. So we will see typically you'll type get push, Heroku, Master or Main because
that's the main branch that you have, and you'll typically only
have one branch within your not one branch within your project,
but you may have one branch that you're using for deployment,
so you would use that one. But because I've split everything up
in lessons, I'm going to type lesson five because that's the one that's
ready for us to actually deploy. And let's see how this goes
because I do not know. Okay, there we go. It worked. It created a new branch
called lesson five. Let's see if we look at Heroku
and refresh this page. Did anything happen? It has not. Okay. Looking at the docs
to actually deploy code. It does say that the branch
name has to be Master or main. Otherwise, if we push to a different
branch, it really will not matter at all. So because I have this less than five
branch, it kind of doesn't matter. So let me make a new branch and I will call it Main,
and I'm branching from my Lesson five branch, which has that new
commit with our new build files. So let me type get check out
to check out the new branch. So now I have a new branch called Main
and I will type get push, not origin. We're going to go to Heroku
and then maintain hit enter. There we go. Okay.
Now it failed. When did it fail? So after some troubleshooting and then this is my testing repo where
I've done everything before. It was because the Swift version file that we
created needed to be 5.5. I don't actually remember changing it, but it has to be 5.5 instead of what we
Typed originally, which was 5.2 .1. So then once you
successfully deploy it and you have to deploy it on the main branch,
so it has to be either Main or Master. It can't be what I was doing, which was I called it lesson five because
that's how I broke up the whole course. So once I switched to the main branch
and then changed that to version 5.5 rather than 5.2 .1 we're Apple to deploy
and you can see what's going on in terminal right now is
everything trying to deploy. So you can see over in the website that was me troubleshooting. You can see build progress and whatever is happening here is actually the same thing that's
happening in our terminal. So we can wait for this to finish. Here we go.
More compiling. I'll sell for Rubik's Cube
while we're waiting. I'm still not doing it right. How to do this for a minute. Okay.
We have successfully deployed everything, so you can see all the build
files right there. Or you can see the View build blog right
here, which show you the same thing that happened in the terminal,
and then in the end it shows you it created a new branch called Main,
and then it's been deployed. And what we have to do after doing that, we need to add a server basically so
that our application can go somewhere. So we do that using Heroku Psscaleweb
equals one and you'll see this appear. So once we hit enter on this and it runs,
you will see this and it'll say done. So if we go back to the web interface
and we refresh it, we will see something. Now we see this that we had from our Proc
file, so I don't know exactly what this is, but it's needed to get
everything running. So we have that and we
can see we deployed. This is our commit hash. So if we were to type get log one,
which only shows us one in our log, the beginning of this commit hash
matches what is right there. And that was my last commit
was updating that version. So we have that. And now it's completely deployed. So we could use Postman, which let me open that up and we
could then hit our API already. Everything's running,
but we haven't yet set up the database. So our app is fully deployed right now. So if we go to settings,
it tells us this is the name of our app. We are using Swift and
that's the get URL that we have. We scroll down a little bit more. We find this too much. We find this add domains. So you could configure this to use your
own custom domain like codewithchris. Com.
If you wanted to make the API from a domain like that,
you would have to configure it. But here is our API
for the built in domain. So when we use CWC vaporapi, that is the name of our application,
and then the subdomain is herokuapp. Com. So if we just put that
directly into postman and hit send without adding any kind of route, we get back 503 service
unavailable, which is not good. If we go to this in the web browser and
hit this, it gives us application error. So you can see this is application error,
and it's telling us an error occurred. And that's how we view the logs,
which we'll look at. But we also see when
we do this in postman. This is the HTML that came back for that web page because just
basic like going to a website. You are doing a get request. So if we see down here
we'll see the same thing. It's showing us whatever this web page is. And I guarantee if we command click,
we'll see the exact same thing. It takes us to the same error page,
because here the URL is our project URL and it's showing you it's making an iframe
which is just like embedding a website. And what it's embedding is this
like application error website. So when we go directly to that,
we see the exact same thing. So let's see what the error is. Let's type Heroku logs, tail and hit enter. What does this tell us? So you can see it gives us an ongoing log,
as in it doesn't stop. So we would have to hit
control C to get it to stop. But let me see if I can make this bigger. So it's kind of not all one. So if we try to go to our
application and hit enter, we see a new status appear
and it says app crashed. So part of what is happening when
we go to configure our application. So if we look at our configure code right here, it's trying to create
migrations and set up our database, but we haven't done anything in Heroku
to actually set that up yet. So that's part of why it's failing. I believe so we will do that the rest of that. And then we'll go back and try to see if
we can hit our main routes, which we didn't take out the Hello
and the root route these two. So that's what I was trying to hit
first was to return it works. And then let's try to hit Hello
and see if anything happens, so they still give us 503 unavailable. And then we can see
in the error down here. It tells us App crashed and you can
see we tried to hit the Hello route. Okay, let's set up the database
and then go back and try this again. So to get out of this, we type control C and then now we're no longer live
monitoring the logs and I don't like this being gigantic, so we're going
to make it smaller again. Let's go and add our database. What we want to do is go to the overview
tab and installed add ons where you're going to add a new one
and we will search for Postgres because that's the type
of database we want to use. So we want to do Heroku Postgres. Click on that one and we want to use the Hobby Dev one,
which is free as opposed to paying a ton of money for it, because we don't
need any of that right now. So we have the Hobby version
and click Submit order form here.
We can see that it's been added, and I believe this is the command we could
have Typed into the command line to do this as well rather than
doing it from the website. But the website is pretty easy. So that's why I did it that way. So we have our database and we can click this Heroku Postgres right here,
which opens up a new window and it takes us to a new part of the website that tells
us about the data and the database. So this is fetching everything and we can see we don't have any rows in our
database because we just created it. So basically it exists as a database, but we haven't added any of our
tables and any of our migrations. So when we go to migrations to create songs, here's our songs table
and we haven't ran any of this yet. So what we need to do is go back here. We need to change our code because how it works right here,
it doesn't actually work. And if we go and look at the Vapor documentation, which is
mostly what I'm following here. If you go to Docs vapor codes and you click on the side,
there is this whole menu and you go down to deploy and go to Heroku,
you get to this page and we are way down at the bottom for
almost everything running. It's telling us to change the code to look
something like this for getting our environment URL,
which is the database URL. So actually I'll keep this
right here for a minute. When we were looking at this here we are getting our environment database host
and database username password because these are the values it's using Vapor
password and Vapor username because it could not find an environment
variable because we never added any. But we still need like
a URL and a database host. So like where does the database live? We have to tell our app that in some way. So what we are going to do is it's
going to look for a database URL. But again, this is not a value. We want to hard Xcode and it even tells you if we type Heroku config,
it tells us how to access the database and they called their app. Today I learned ours is
called CWC vaporapi. It gives us this URL to use
to connect to the database. But here in the docs it tells you don't
ever hard code this because Heroku rotates through these URLs because it might change
because for whatever reason and because of that, we can't use
a hard coded value for anything. We are going to be using
a database configuration. So if we look at the Heroku app,
go down to Settings reveal config VARs. So we go to database VARs
and then config VARs. We can see the database URL
that Heroku is storing is right here. So this is the one that it will use. We need to tell our app now
to look for this value. Let's keep it on that and let's go
and change the code to do this. The next step it's telling us within
the Docs is mostly what I'm following along with is to add the database URL
and make a Postgres configuration with both of these. And I tried to type this code and it
does not work or did not work for me. So you can try to type this and deploy
again and see if that worked. But here is the code change that really
actually worked on my machine. So what we wanted to do was first
check if we have that database URL. So what we are doing here is first we are going to make a URL string from this database URL in the environment
variable, which will look for the environment variable
we saw over here in settings this config variable. And then we are going to make a Postgres configuration because that's
the type of database that we used. And then all of this will
be in this if statement. Otherwise we'll set it up like this, which is how we've done it on our
local computer and everything worked. So we have our Postgres configuration, and we also need to make something
called a TLS configuration. So we have made our postcard configuration
for our database, and we are going to configure
the database using this database URL. And then we make a TLS configuration
and let me double check that. That is right. Make client configuration.
Yeah. So we make the TLS configuration and it says make client config, and we set
the TLS certificate verification to none. I honestly don't really know what this is. Tls is part of like the Internet. It's a transport layer. And then we set our Postgres config
to use our TLS configuration. And then lastly, we do app databases use,
which is almost exactly the same as this rather than hard coding
everything, though. We are using Postgres, and we are using our Postgres
configuration to set everything up. And then this is just the name of our
database because you can have multiple databases within an application,
but we are only using one. So that's what setting
up right here is doing. And lastly,
we have this try auto migrate line right here, because when we ran our application
for the first time, we wanted the migrations to run and for
the app to basically not work unless basically make the app not
work until the migrations ran. But that's not what we
want to do within Heroku. We want it to still run,
and we only want to run these, like in development, and we can write
a command to run it within Heroku. So what we are going to do
is change this to be we are going to change this
to make an if statement. So if our app is in development mode or
development environment meaning on our computer, then we want
to run the migrations. Otherwise they won't run. And we are going to just run
it from the command line. So we've made all these changes. Let's commit these changes. So if we type get status if I can spell which I can't there we go get status. We see we changed our configure
method, which is correct. So I'm making a new message called
update config for database. And when I do am that allows me to add whatever here was ever ready to be
committed and write the message at the same time rather than just
writing get add and then get commit. So we have committed that. Let's go and push these changes to Heroku. So we type get push Heroku main and this will
deploy to Heroku again. So when I hit enter on that,
you can see it's starting to build again. We go to overview. We can see what we've
done here is we deployed. We created the new database,
we set up the database URL, and then that's just what it sort of does
automatically when we made the database. And then right here because we tried to push, you can see now it's going
and building the project again. So let's wait for that to build. After we built everything,
it has been fully deployed so we can see right here and then it's
deployed our new commit. And so if I click on this,
it'll take me to this Heroku data tab because I've already clicked on it
and we should be able to refresh it. And it doesn't look any different right now because we haven't ran our
migrations just yet either. So if we look at data clips, this allows us to create new queries
that we can run on our database from this interface, as opposed
to connecting to it from Data grip. If we try to do Select Star from Songs, it won't work because the songs
table still doesn't exist yet. We need to give up a title. And then if we click
Save and Run, I think. Yeah, it tells us Songs doesn't exist. That's because we have now,
like configured our application to run and use our new database URL, but we still
haven't ran the migrations themselves. So to do that, we need to use this one Heroku run, run migrate
environment production. So Run is by default the name right
here of what it calls our application. Oh no, it's in the package. That's where it's at. So here it's. Our app name is called App, and then
the target is called Run by default. So that's why it says Run run. So if we type this and hit enter,
Chris is going and running our migration. So I'm not sure if anything shows
up in this latest activity. I don't think it does. Yeah, nothing shows up there,
but we can see right here. Something is happening. It says we wanted to run our migrations on our new app, and then it asks us,
do we want to run this migration, which is app createsongs because right
here that's what we call the migration. So we say yes with a Y and enter and it tells us migration successful,
which is awesome. So now when we go to Data, which is again, we get to it by clicking
on Heroku Postgres or you could go to Resources and then
click on it right there. I just like the overview page. We get to this. And if we go here and try to run it again,
which I'm not sure why. It always makes you click like save
and run as opposed to just run. I don't know. I think you can just run it. I've always had to click save and run. Yeah, because it just doesn't do it
again, which is weird. So click save and run. We get back nothing because
not the database. The table itself exists, but there's no data in it yet
because we haven't added anything. Does that show us anything? No. So now if we go back to postman,
let's try to hit our base URL so we can use again. This is the error that showed
us originally, which is Heroku. We can see the logs for what's happened. So we'll keep that up as well. While that is running, if we go to configure two routes,
we have our two routes of it works. And Hello. So let's try to hit the base route
that has nothing and we should get back. It works. Okay, we got back.
It works. So we got 200.
Okay. It says it works, which is awesome. And then we see here we got back the root path, which is also a get request,
and it's just a flash. And that's the request ID. That's the web one and that's it. So it didn't show us an error,
which is amazing. And if we try to go to Hello, we hit this, we get back Hello world, because
that's what we have Typed right here. Now the moment of truth. Let's go to our slashsongs endpoint. So we get back an empty array, which is good because we don't
have anything in our database yet. So that's exactly what we want back. And we got back a 200,
which is also a good thing. So I have this other tab for new songs,
and what we do is let me copy this JSON and go back to the tab
that has the actual URL in it. Let's add a new body. I already have it here and we have a song just called Money, and we change the body
from none to raw and change it to JSON. And if we click send, it got back 200.
Okay. Which is good.
Oh, wait. I have to change this to post that's. Whysend post. There we go. So we still got back to 200,
which is a good thing. If we go and change this to a get request now and hit send, we should get
back our one row that says Money. So there we go.
We got that back. And that's the new song
that we just posted. So when we go to this
data clips tab again, I don't know why we have
to do save and run. I don't know where the button
is to actually just run. It like normal. I think there was a refresh somewhere
but we click save and run. And here we go. This is our table with the ID and the title, and we have our
one song called Money in here. So that is great. That's exactly what we want. So we know at least it is reading
data and we are sending data. So let's try to do an update. So if we do an update,
we need this ID number. No, we don't.
We just do a normal put request. That's what it is, right?
I don't remember. Let me look, I don't remember. We do a put request and we
decode the whole thing. Okay.
So we do need this whole object so it would look like this. But instead of money, we want to do how you like that? And we change it to put. And if we hit send, we got back 200.
Okay. Which is good. And is there a refresh button here? I could have sworn there was, but I can't find it. So I just had a semicolon
hit Save and Run, and there we go. Our one row in here says how you like that for the same ID number
as this one, which is good. So we were able to update
and let's add one more song to it. So let's just add a new song. We'll just add another song
called Lovesit Girls Hit Send. No, that's put.
Okay. So because I tried to do a post request or I meant to do a post request,
but I didn't change this. So it said put, it just came back four or four
because it couldn't find one right here. It tried to look for that song right here
from the ID, but we didn't give it any. So it came back for a four.
So we know that's working. So that's good. So let's change this to a post
to add this new song. Messed up the bracket.
That's fine. Click send. We got back 200.
So that's good. And if we go to our data right here,
save and run, we should have two songs. And then let's check delete. So we want to delete
by thesongid so we know the ID. Let's delete how you like that. So copy this whole ID. And technically, we don't need the body
here, but we could erase it or keep it. It kind of doesn't matter. We'll just keep it there. And then we do slash the ID number, the UID, and we change this
to a delete request and hit send. It came back 200.
So that's good. So now let's look at it
and click Save and run. And there we go.
We only have the one property. So we know all of our
operations are working. So that is a good thing. Lastly, we need to use this for our own
application and to do that, all we need to do is open our iOS app,
click Control C to close this. And I'm going to open the iOS app. So that was in the folder above and then
I put it right here. I still have it on less than four. So I need to make a new branch. So let's open our project and Xcdspace
that would open an Xcode project if you have like, an Xcode project file within
whatever directory that you're in. Okay, so we have opened our iOS project. Let me close postman because we don't need
it anymore because we've confirmed everything works and all we need to do for
our iOS app is go to the constant file. And instead of using our ingrocke URL, we
want to use the one for our application. So let's go to the Heroku app,
click on settings and scroll down to domains. We want to copy this URL and paste that right here and make sure
you still have one slash at the end. So if we do that and let's change this
from my cell phone to the simulator and click on Run. The first thing we should see is the one
song that we have called Love Sick Girls. So let's refresh this or run it. So Commandr and wait for my simulator to come up. And once our project runs, we see our songs Love Sick Girls
that's here, which is amazing. That means we successfully read from our
database that's hosted with Heroku. So let's edit this and change it to
Pretty Savage and click on Update. We see our song appears properly here, but we can double check by looking at
the database within Heroku Save and run, and now it's Pretty Savage. So let's go and add a new song. So we've tested read, we've tested update. Let's create a new one. Click on Add song. They have a song called Whistle. So let's just type that click on Add song. We see both of them here and double check again with our database
in Heroku Save and run. And there we go. We have those two songs
and let's then delete one. So delete Pretty Savage delete. We only have our one song here. And if we refresh our database
on this side, there we go. We only have one song left,
and that is it. We have fully deployed our Vapor API
with Heroku and our Postgres database. Then we use that instance
within our Swift UI iOS app. So during this lesson we
created a new Heroku account. We installed Heroku via Homebrew on our computer and we configured it within
the terminal to use our new account. We created a new Heroku app within
the website and then configured that app. And lastly we set up our database to use
Heroku Postgres database, and we ran the migrations so that our app
had our brand new table that we made. And finally we configured our Swift UI app to use our brand new Heroku
instance running up in the cloud. So if you liked this video series
on paper, be sure to like and subscribe and Let Me Know How I Did on my Very
first YouTube tutorial series.