Getting Started with Vapor 4 - A Beginner's Course

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
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.
Info
Channel: CodeWithChris
Views: 9,183
Rating: undefined out of 5
Keywords: swift vapor tutorial, vapor swift, server side swift, server-side swift tutorial, server-side swift 2021, server-side swift vapor, vapor swift tutorial, vapor tutorial, vapor 4 swift, server-side swift with vapor, swift vapor routing, vapor 4, swift rest api server, swift server-side framework, build a rest api, swift vapor
Id: RhYZh3rTMKU
Channel Id: undefined
Length: 179min 37sec (10777 seconds)
Published: Fri Dec 03 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.